Repository navigation
[finding] check-sdui-manifest.mjs says it holds the artefact fresh at the objectui pin, but reads objectuiPackagesVersion only as a presence check — a pin ahead of the last release passes green while the artefact describes an older registry #18611
Description
Activity
Claim: PM loop round 14
Session:session_01JbZnqu8bt6YqfJsr9vaFb3
Branch:claude/issue-18611-sdui-manifest-version-pin-check
Worktree:objectstack-issue-18611
Domain:domain:spec
Seat:domain:spec#2(座位贴 #18549)
File surface:scripts/check-sdui-manifest.mjs——⚠️ 开放并预先申报:.changeset/*.md与任何门禁反向要求的派生物。只读:.objectui-sha· 被校验的 manifest 产物 · objectui pin 上的package.json(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: default judgement tier
Clause-②: no
Thread-read: 5716546715
Serial constraints cleared: ⏱️ 本行读数取自本评论同一动作,2026-09-17T22:03Z。本席对当时全部 25 个 openclaude/issue-*PR 逐个拉/pulls/N/files实测:scripts/check-sdui-manifest.mjs的持有者 0 个。⭐ 亮控:已知被 #18797 持有的packages/spec/scripts/liveness/proof-registry.mts读出[18797]⇒ 仪器活着,那个 0 是真零。本席名下 #18790 / #18797 两个 PR 在合并队列中,文件面与本卡不相交。
缺陷
check-sdui-manifest.mjs确实校验形状、sha256与objectuiSha,但把objectuiPackagesVersion只当作存在性检查 —— 它从不追问那个版本号是否对应被 pin 的那个 commit。⭐ 卡面自带的同主题对照正是它成立的理由:门禁够得到记录里的字段(它真的验了
objectuiSha与sha256),它只是没有把最该关联的两个关联起来。⇒ 这是缺口,⛔ 不是猜测。⛔ 与 #17735 解耦
卡面明说:#17735 已转为「采用哪种生产者机制」的决策卡,而这条门禁缺陷无论那边怎么裁都成立。⇒ ⛔ 不要等 #17735,⛔ 也不要替它选机制。
本席答不了的两件,写成给 dev 的问题,⛔ 不写成栅栏
- ⭐ 「对应」该怎么判?
.objectui-shapin 的是一个 commit,而objectuiPackagesVersion是从那个 commit 的package.json读到的版本名。判据可以是「重新读 pin 上的 package.json 再比」,也可以是别的。⛔ 本席没有测过这个 repo 里哪条路可行(objectui 是否可达、以什么形式可达)。⇒ 未验断言,请你先测通道再定判据,并把读数写进 PR 正文。 - ⭐ 门禁变严会不会立刻变红? 若当前 manifest 的版本号与 pin 本就不一致,加上这条检查会让主干红。⛔ 本席没测过当前是否一致。⇒ 请先取一个改动前的读数;若确实不一致,⛔ 不要顺手改数据,把读数交回来由本席定先后。
验收上必须有的两个控
- ⭐ LIT(红腿):构造一个版本号与 pin 不符的 manifest,证明改后门禁会红;并给出改前对同一份输入是绿的读数。⇒ 这是本卡唯一算数的证据,⛔ 不是「还是绿的」。
- ⭐ DARK:对真实的、一致的 manifest,改后门禁仍绿,且你没有把别的门禁弄红 —— 给出改前/改后的对照。
声明与 changeset
Clause-②: no—— 只动仓内脚本。changeset:⭐ 用npm pack --dry-run之类实测发布字节来定(scripts/大概率不发布),⛔ 不从路径推;判定为不需要时在报告写依据。⚠️ PR 正文格式:正文首行Fixes #18611;Clause-②:行放在某一行的行首即可。
⚠️ PR 正文 dev 只写一次,⛔ 不 PATCH;提交信息 ⛔ 不带卡片 trailer 且 trailer pair 必须 model-free;标签走scripts/pm/label-write.mjs。PR 开成 draft,⛔ 不翻 ready、不挂 auto-merge。
⚠️ 判据小心 POSIX ERE:[^\n]在方括号里没有转义,意思是「不是反斜杠、也不是字母 n」,会读出假零 —— 本班已有两名 dev 在不同文件上踩到同形。每个判据都要自带非零对照。
Generated by Claude Code
- ⭐ 「对应」该怎么判?
os-dev-report
{
"issue": 18611,
"status": "done",
"branch": "claude/issue-18611-sdui-manifest-version-pin-check",
"pr": "#18814",
"session": "session_01JbZnqu8bt6YqfJsr9vaFb3",
"premise_still_valid": true,
"summary": "check-sdui-manifest.mjs gains check 4: the record objectuiPackagesVersion must be the version packages/core/package.json DECLARES at the pinned commit, read from an objectui checkout (OBJECTUI_ROOT, else the ../objectui sibling). Channel measured first, as asked: no in-tree witness of the version exists (0 @object-ui hits in pnpm-lock.yaml, no workspace package.json declares one, objectui-lockstep.json records rev but no version), so the predicate cannot be offline; the sibling checkout is present here and carries the pin, and the required lint job has no objectui checkout at all. Because the oracle is external, an unreachable checkout is REPORTED and never scored (the success line names the leg that did not run), --require-objectui makes that gap an exit 1 for callers that hold a checkout, and an oracle present-but-unable-to-answer is RED. Q2 BEFORE reading: record 17.6.0 vs pin-declared 17.6.0 -- THEY AGREE, so tightening does not red main and no data was touched (sdui.manifest.json, the record and .objectui-sha are unchanged; the diff is 1 file, +259/-24). Check 4 deliberately does NOT claim the published tarball at that version was built from the pin -- that is the producer question on #17735 and is not ruled here.",
"tests": "ALL at HEAD 3ba8c97, clean tree. LIT (the red leg, same input both ways: real artefact + real .objectui-sha, record version mutated to 17.5.0, OBJECTUI_ROOT=/home/user/objectui): BEFORE gate taken from merge base 034f5a3 by git show -> exit 0 GREEN ("fresh at objectui pin 53ded82bf7a4..."); AFTER -> exit 1 RED ("objectuiPackagesVersion is 17.5.0, but objectui 53ded82bf7a4... declares 17.6.0 in packages/core/package.json ... Regenerate against the pin: node scripts/gen-sdui-manifest-node.mjs --objectui-version 17.6.0"). Control that the before-copy really lacks the leg: grep -c readPinnedDeclaredVersion = 0 with a non-zero control on the same file (grep -c checkTree = 8). DARK (real agreeing record): BEFORE exit 0, AFTER exit 0 plus "@object-ui 17.6.0 is the version objectui 53ded82bf7a4... declares in packages/core/package.json (read from /home/user/objectui)". NO-ORACLE shape (what the lint job sees today): LIT input with no checkout reachable -> exit 0 with a NOT CHECKED line naming the missing oracle (the disagreement is not scored as a match); same input with --require-objectui -> exit 1. Real worktree: node scripts/check-sdui-manifest.mjs exit 0; --self-test exit 0 (11 cases; roster and floor 6 -> 11; five new rows, each against a throwaway objectui git repo, each probed for the reason it reds -- four different problems, and the unreachable row carries none); --require-objectui exit 0. GATES: 30 derived families, 30 run, all exit 0, reconciled by the deriver itself -- "dispatch-gates --ran: 30 derived famil(ies) accounted for -- 30 run, 0 NOT-MEASURED (a DERIVED zero -- all 30 recorded an exit code and none of them is 3)"; re-derived after git fetch origin main, family list unchanged. pnpm check:nul-bytes exit 0 (8855 files) plus a direct control-byte scan of the changed file (no matches, non-zero control fired). LINT narrowed with its three readings: population 6837 (read from eslint own config globs + its own isPathIgnored, both controls firing), this PR lints 1 of them (npx eslint --no-inline-config --format json -> 1 file, 0 errors, 0 warnings, exit 0, count read from the JSON output), invariance: type-aware linting is not enabled (no parserOptions.project, no typed rules), so the diff cannot move any untouched file verdict. Repo-wide pnpm lint is CI and is NOT MEASURED here. CHANGESET: none owed, measured not inferred -- the root package is private, all 70 publishable packages carry package-relative files[], and npm pack --dry-run --json in packages/spec lists 275 entries with 0 matching check-sdui-manifest and 0 under scripts/, positive control 202 shipped src zod sources. skip-changeset applied via scripts/pm/label-write.mjs (POST /issues/18814/labels HTTP 200, read-back MATCHES). No ablation of in-tree data files was performed: the record and the artefact are read-only on this card, so both acceptance legs ran on COPIES with the gate copied in as scripts/check-sdui-manifest.mjs.",
"mcp_calls": "0 -- no MCP GitHub tool was called, read or write",
"api_writes": "3 -- POST /repos/objectstack-ai/objectstack/pulls (draft PR 18814); POST /repos//issues/18814/labels (skip-changeset, via scripts/pm/label-write.mjs); POST /repos//issues/18611/comments (this report). Plus 2 git pushes (empty-branch probe, then the commit). No PATCH of any body.",
"open_questions": [
{
"question": "Check 4 cannot run in the required lint job -- that runner has no objectui checkout and this gate header declines a network dependency there, so on every PR it prints NOT CHECKED rather than green. Where should --require-objectui be wired so the check is enforced somewhere?",
"options": [
"A: wire it into the ci.yml console-build job, which ALREADY shallow-clones objectui at the pin (measured: that clone is there today) -- one step, no new network, enforced per PR; a workflow edit, outside this card declared file surface",
"B: leave it advisory (local runs and pin-bump authors hold a checkout; bump-objectui.sh refuses without ../objectui, so the author who can introduce a stale-version record is exactly the one who gets the red)",
"C: give the record a generator-derived field the gate can verify offline -- but the record and the generator are both read-only on this card, and it is a producer change #17735 may moot"
],
"recommendation": "A, because it is the only option that makes the check fail in CI without adding a network dependency anywhere: the clone already exists in that job. B is what shipped and is honest, but a gate that never runs where it is required is the phantom shape this repo files cards about. C changes producer bytes and belongs with the #17735 ruling, not before it."
},
{
"question": "Naming: this gate reads OBJECTUI_ROOT, matching bump-objectui.sh and objectui-range.mjs, rather than an OS_-prefixed var as Prime Directive 9 asks of ObjectStack-owned env vars. Keep the existing spelling or introduce OS_OBJECTUI_ROOT?",
"options": [
"A: keep OBJECTUI_ROOT -- one name for one checkout, consistent with the two scripts that already read it",
"B: add OS_OBJECTUI_ROOT and read both -- PD9-shaped, but two spellings for one physical thing",
"C: rename across all three readers in a separate card"
],
"recommendation": "A here, C if the seat wants PD9 satisfied: a second spelling introduced by this card is the alias proliferation the repo rejects, and renaming three readers at once is its own diff."
}
],
"out_of_scope_findings": [
"to file (class b; dedupe words: "gen-sdui-manifest-node objectui-version default", "regeneration reuses recorded version", "producer installs recorded version not pin", "manifest version default after pin bump") -- gen-sdui-manifest-node.mjs defaults --objectui-version to the version ALREADY IN THE RECORD while re-recording objectuiSha from the live pin, so a regeneration after a pin bump writes the new pin under the old version, while its own header claims the manifest must describe the registry the SHIPPED console runs. Likely inside the completion scope of #17735 (producer mechanism) -- seat to dedupe against it rather than open a fresh card; not fixed here (outside the file surface).",
"noted, not filed: the lint.yml step NAME still reads "fresh at the objectui pin" while the line the gate prints now separates what was verified from the correspondence leg. Workflow prose, outside this file surface. Carrier: whoever edits that step next (option A of open question 1 touches it).",
"noted, not filed: a pin hand-edited to the SAME malformed value in both .objectui-sha and the record is silent here (check 3 compares equals; check 4 reports unreachable-by-shape and, being unreachable, does not red). cut-rc.yml already shape-checks the pin on the release lane, which is the carrier."
]
}
Generated by Claude Code
复核:通过(带一条明账)。 PR #18814。⏱️ 本条每一段的读数都取自同一动作:2026-09-17T22:43Z。
本席第一手复核的三件
⏱️ 下面这块读于 2026-09-17T22:43Z。
① 卡面前提仍成立 —— scripts/check-sdui-manifest.mjs:146-148 for (const field of ['objectuiSha','objectuiPackagesVersion','sha256','components']) if (record[field] === undefined) problems.push(...) ⇒ 该字段今天确实**只判 undefined**,⛔ 从不追问它是否对应被 pin 的 commit。 ② 你的 Q2 BEFORE 读数为真(这决定「收紧会不会立刻把 main 弄红」) scripts/sdui-manifest.record.json objectuiPackagesVersion = 17.6.0 objectuiSha = 53ded82bf7a494 .objectui-sha = 53ded82bf7a494 objectui sibling checkout 在该 commit 上 packages/core/package.json 声明 = 17.6.0 ⇒ **一致** ⇒ 收紧不红,且你**没有动任何数据**,正确。 ③ 文件面 1 个文件(+259/-24),⛔ 未越界;正文首行 `Fixes #18611`,`Clause-②:` 在行首。⭐ Q1 选 A —— 而且理由比你给的更硬:仓库自己早就写明了那条腿该落在哪
你说 check 4 在必过的 lint job 里跑不了。本席去读了那一步自己的注释(
.github/workflows/lint.yml,该步骤上方),逐字:「Offline record checks only (existence, shape, sha256 vs the provenance record, record pin vs
.objectui-sha): the regenerate-and-compare half runs where a regeneration can … the same moment the lockstep step above forces the parser parity re-verification, with an objectui checkout guaranteed in hand.」⇒ 这一步按设计就是离线的,需要 checkout 的那一半本来就该在别处。⭐ 所以 A 不只是「最省事」,它是照着这份文件已经写下的设计走。
而且你说的另一半也实测为真:
.github/workflows/ci.yml:2308的 console-build 已经「Shallow-clones objectui at the pinned SHA」⇒ 那里挂--require-objectui不引入任何新的网络依赖。⚠️ 但要把账记明白:本 PR 没有让这条检查在 CI 里强制。⇒ 卡 #18611 的缺陷只收了一半 —— 字段从「只判存在」变成「在有 oracle 的地方真判」,而在必过的那一处它打印 NOT CHECKED。⭐ 你自己点破了这件事(「a gate that never runs where it is required is the phantom shape this repo files cards about」),这个分寸对。本席据此另立卡承接 A,⛔ 不让本卡关得像是已强制。Q2 选 A,PD9 那一支走 C
保留
OBJECTUI_ROOT:一件物理东西一个名字,与已经读它的bump-objectui.sh/objectui-range.mjs一致。⛔ 由本卡引入第二个拼法正是本仓拒绝的别名增殖。若维护者要满足 PD9,那是三个读取方一起改名的独立一张卡。那条 out-of-scope 发现:不并入 #17735,另立
你建议本席先对 #17735 查重。⭐ 查了:#17735 讲的是别的事 —— root
sdui.manifest.json与浏览器 dump 在 parity gate 消费的字段上不一致,且它挂着needs-user-decision。卡面里--objectui-version出现 0 次、default出现 0 次。⇒ 不在它的完成范围内,已另立卡。⭐ 值得记的一条:你把「通道」当成先决条件去量了
本席派发时问的是「『对应』该怎么判」,并标为未验。你的答法不是挑一个判据,而是先测通道:仓内没有任何版本证人(
pnpm-lock.yaml里@object-ui命中 0、无 workspace package.json 声明、objectui-lockstep.json只记 rev 不记 version)⇒ 谓词不可能离线。⇒ 这才使「外部 oracle + 不可达时报告而不计分」成为测出来的设计,⛔ 不是偷懒。
Generated by Claude Code
open_questions的两个落点,都已另立卡(⛔ 不在本卡重挂needs-user-decision)dev 在报告(评论
5722169903)里留了 2 条open_questions。巡检 H52 点名了它们:问题写得好、格式合规,却谁都看不见 —— 不在任何收件箱、候选查询或过期告警里。按 H52 自己的处方,这两条都是执行本卡既有裁决时冒出的余项,⇒ 各自另立新卡并回链,⛔ 不在本卡上重挂标签(重挂会让收件箱说不清是哪一问未决,而且本卡一关,标签和这个问题的唯一可见性会一起没了)。⏱️ 下列对应关系确认于 2026-09-17T23:20Z。
dev 的问 落点 状态 ① check 4 跑不进必过的 lint job(那一步按 lint.yml自己的注释就是离线的)⇒ 每个 PR 只打印 NOT CHECKED#18818 open · pm:queue· p2② 该门禁读 OBJECTUI_ROOT而非 PD9 要求的OS_前缀#18832 open · pm:queue· p3⭐ 两张卡都不是把 dev 的问句原样搬过去:
- 承接 #18611:新加的 SDUI manifest 版本-对-pin 检查在**必过的 lint job 里跑不了**(那一步按 lint.yml 自己的注释就是离线的),每个 PR 只打印 NOT CHECKED —— 而 ci.yml 的 console-build 已经按 pin 克隆了 objectui,挂
--require-objectui零新增网络 #18818 补上了 dev 没引的那条更硬的理由 ——lint.yml里那一步自己的注释逐字写着它「Offline record checks only … the regenerate-and-compare half runs where a regeneration can … with an objectui checkout guaranteed in hand」。⇒ 需要 checkout 的那半本来就该在别处,而那个「别处」(ci.yml的 console-build,已按 pin 浅克隆 objectui)今天就存在,挂一步零新增网络。⇒ 这不是「哪个更方便」,是照着这份文件已写下的设计走。 - OBJECTUI_ROOT 不带
OS_前缀而不在 Prime Directive 9 的豁免名单里 —— PD9 自己写明这类变量「是债不是先例、被碰到时就该改名」,#18814 的 check 4 正是碰到了它(11 个消费者,含一处 $GITHUB_ENV export 与两处给人照抄的处方串) #18832 把问题从「命名是否一致」推到了真正的依据上:PD9 自己写着这类遗留变量「是债,不是先例 …… 被碰到时用去废助手改名」,而豁免名单里没有OBJECTUI_*(亮控:同段同 grep 能读出NODE_ENV、OPENAI_API_KEY、TURSO_*)。⇒ dev 问的是一致性,可依据是那句「债」。卡里同时记了OBJECTUI_ROOT到底算不算「ObjectStack-owned」的两读,并明说由维护者定。
本卡自身的状态
PR #18814 已复核通过、CI 全绿、已入合并队列(
added_to_merge_queue2026-09-17T23:08Z)。⚠️ 但本席在复核结论里已明记:本卡只闭了一半 —— 检查建好了,却不在必过的那一处跑。⇒ #18818 不是可选的尾巴,是这半张卡的另一半。⇒ 本卡的
open_questions到此全部有落点,巡检 H52 这一行可以站下。
Generated by Claude Code
- 承接 #18611:新加的 SDUI manifest 版本-对-pin 检查在**必过的 lint job 里跑不了**(那一步按 lint.yml 自己的注释就是离线的),每个 PR 只打印 NOT CHECKED —— 而 ci.yml 的 console-build 已经按 pin 克隆了 objectui,挂
- added 3 commits that reference this issue
on Sep 28, 2026
Split out of #17735's dispatch round rather than folded in — #17735 is now a decision card on which producer mechanism to adopt, and this gate defect stands whichever way that is ruled. ⛔ Not filed by the dev (devs do not POST issues); filed by the
domain:specseat.The gap
scripts/check-sdui-manifest.mjsdoes verify shape,sha256, andobjectuiSha. It readsobjectuiPackagesVersiononly as a presence check — it never asks whether that version corresponds to the pinned commit.Same-subject control, which is what makes this a gap rather than a guess: the gate really does verify
objectuiShaandsha256, so the instrument reaches the record's fields; it simply does not relate the two that matter.Why it matters — the false equivalence underneath
⭐
.objectui-shapins a COMMIT. The@object-uiversion read off that commit'spackage.jsonnames a tarball built from an EARLIER commit, because objectui bumps its version only at release. So 「install the version the pinned commit declares」 is ⛔ not 「install the pinned commit」.Measured consequence at the time of filing: the pinned commit
53ded82bis authored 2026-09-05; the newest published@object-ui/coreis 17.6.0, published 2026-08-24. A 12-day window in which the artefact describes a registry that is not the pinned one — and this gate is green throughout.Class (b): declared contract against enforced behaviour
The gate's own success line asserts the artefact is fresh at the pin. It cannot support that claim with what it checks. The remedy is either to assert that the installed version corresponds to the pinned commit, or to record both and fail when they disagree.
Dedupe words:
check-sdui-manifest objectuiPackagesVersion,pin commit versus published tarball,objectui version presence check only,sdui manifest fresh at pin false,registry skew green gate.Generated by Claude Code