Skip to content

[finding] gen:sdui-lockstep records the objectui checkout's HEAD while stamping recordedAgainstPin from .objectui-sha — a checkout at tip silently ships a record that names the pin and describes a different tree #16797

Description

@claude

Filed by the domain:devx @ objectstack execution seat (session_012GKcPZbMoGq7WPzKLfRBTU) at 2026-09-08T05:5xZ, out of the contract-review of PR #16788 (closes #16626). ⛔ Left unlabelled for triage to route and rank.

The defect

pnpm gen:sdui-lockstep takes its reading from whatever the objectui checkout has at HEAD, while stamping the output's recordedAgainstPin field from .objectui-sha. Nothing compares the two. So if the objectui checkout is not sitting exactly on the pin, the script writes a record that names the pin while describing a different tree — and it does so silently, at exit 0.

The comparison site is scripts/check-sdui-lockstep.mjs:459-464 (rev-parse HEAD against the pin file).

Measured, deterministically, both ways

During #16626's objectui pin bump (a472b07167a3 → 53ded82bf7a4):

objectui checkout at recordedAgainstPin written diagnostic codes recorded
tip (d65b2baa) 53ded82bf… — the pin 25 (adds member-type-mismatch)
a worktree pinned at 53ded82bf 53ded82bf… 24

⇒ The difference is real content, not noise. Re-generated at the pin, the output is byte-identical to the record shipped in PR #16788; at tip it gains a diagnostic code describing a tree this repo does not build against.

Reproduced twice: once by the dev during the bump, and independently by the contract-review-tier reviewer, which regenerated with OBJECTUI_ROOT pointed at each of the two trees.

Class (a), and ⚠️ that is a correction to the original self-rating

The dev that hit this recorded it as "borderline (c) — it is a tooling trap, not authored metadata" and left it as a noted, not filed item. The reviewer overruled that: it is a reproducible defect with a deterministic reproduction and a firing control, which is class (a) outright. Recorded here because the reclassification is the reason this card exists at all.

Why it matters beyond one bump

objectui-lockstep.json is the record a later reader consults to learn what the pinned console actually is. A record that names pin X while describing tree Y is worse than a missing one: it answers confidently and wrongly, and nothing downstream can tell. This is the same shape as the transcribed-count family this lane closed today (#15231, #15864, #16007) and as #16769 — a fact recorded with nothing comparing it to the thing it records — except here the recorded fact is stamped with the identity of a different thing, which makes it self-certifying.

Shape of a repair, ⛔ not a prescription

The script offers no --pin / --rev flag; pointing OBJECTUI_ROOT at a pinned worktree is the only lever today, and it is a convention nothing enforces. Either:

  • resolve the objectui checkout to the pin before reading (git -C "$OBJECTUI_ROOT" rev-parse the pin, read from there), or
  • refuse loudly when HEAD != .objectui-sha rather than recording — the fail-loud direction this repo prefers, and it needs no new machinery.

⛔ Do not "fix" it by making recordedAgainstPin record HEAD instead: that makes the field honest and the record useless, since the field's whole purpose is to say which pin the description belongs to.

Re-check commands

sed -n '455,470p' scripts/check-sdui-lockstep.mjs
grep -n 'recordedAgainstPin' packages/sdui-parser/objectui-lockstep.json
git show origin/main:.objectui-sha

At filing, the pin on origin/main is a472b07167a39e55491109e864bb5a54027dcfbd; PR #16788 moves it to 53ded82bf7a4. ⛔ Re-derive before acting — that PR is about to land and the line numbers will move.

Not a duplicate

#16715 (open) is a different defect in an adjacent gate — check:react-declaration-parity telling the reader this repository contains no sdui.manifest.json while the file is in the repo root. Same family of pin/manifest tooling, different failure.

Refs #16626 · PR #16788.


Generated by Claude Code

Activity

  1. added theissue type on Sep 8, 2026
  2. os-zhuang commented on Sep 8, 2026

    @os-zhuang
    Contributor

    分诊:domain:devx / Bug / priority:p2 / pm:queue

    域 —— scripts/check-sdui-lockstep.mjs(非 scripts/pm/)⇒ 车道表 scripts/ ⇒ domain:devx。

    当刻复核

    .objectui-sha                                             a472b07167a39e55491109e864bb5a54027dcfbd
    packages/sdui-parser/objectui-lockstep.json:23            "recordedAgainstPin": "a472b07167a39e55491109e864bb5a54027dcfbd"
    scripts/check-sdui-lockstep.mjs:359                       if (record.recordedAgainstPin !== livePin) {
    scripts/check-sdui-lockstep.mjs:308-309                   (recordedAgainstPin 必须是 40 位 commit id)
    

    ⇒ 当刻两者一致(PR #16788 尚未落地)。

    ⭐ 而 :359 恰恰把这个缺陷的形状照亮了:检查器会把 recordedAgainstPin 和活的 pin 比对 —— 所以字段本身是受监督的;不受监督的是生成器:它按 HEAD 读内容,却按 .objectui-sha 盖章。⇒ 检查器看到的两个值永远一致(都来自 pin 文件),而描述的树可以是别的。

    ⇒ ⭐ 这就是卡面说的 self-certifying:那条记录用它所描述之物以外的东西的身份给自己盖章,而唯一在比对的那道检查,比的正是盖章那一侧。

    等级 p2

    • ⭐ 确定性复现,两向都测过,且差异是真内容:
      objectui checkout 在 tip (d65b2baa)      → recordedAgainstPin = 【pin】,  25 个诊断码(多出 member-type-mismatch)
      objectui worktree 钉在 53ded82bf        → recordedAgainstPin = 【pin】,  24 个
      
      在 pin 上重生成的输出与 PR chore(console): bump the objectui pin to 53ded82bf7a4 — the shipped console lowers array analytics filters #16788 里发的记录逐字节相同;在 tip 上则多出一个描述本仓并不据以构建的那棵树的诊断码。
    • 复现两次、由两个不同角色:交付 dev 一次,契约评审席一次(把 OBJECTUI_ROOT 分别指向两棵树重生成)。
    • 失败静默且 exit 0。
    • 不到 p1:不影响运行时、不影响已发布物;坏的是一份给后来读者查"被钉住的 console 到底是什么"的记录。

    ⭐ 卡面里一处分类更正,本席认为它比缺陷本身更值得记住

    The dev that hit this recorded it as "borderline (c) — it is a tooling trap, not authored metadata" and left it as a noted, not filed item. The reviewer overruled that: it is a reproducible defect with a deterministic reproduction and a firing control, which is class (a) outright.

    ⇒ ⭐ 一条被自评为"边界情形、不立卡"的观察,被评审席按准入三类重新归类为 (a)(可复现缺陷),于是它才成为一张卡。本席同意这次改判:有确定性复现 + 开火对照 ⇒ (a),与"被测的是不是授权元数据"无关。

    ⛔ 一条不得走的"修法"

    ⛔ Do not "fix" it by making recordedAgainstPin record HEAD instead: that makes the field honest and useless, since the field's whole purpose is to say which pin the description belongs to.

    ⇒ 本席加固:那样改之后 :359 的比对会永远通过(两边都成了 HEAD),把一道有效的检查也一并废掉。⛔ 绝不。

    两条可行方向(卡面给出,本席补判据)

    1. 读之前先把 objectui checkout 解析到 pin(git -C "$OBJECTUI_ROOT" rev-parse 那个 pin,从那里读)。
    2. ⭐ HEAD ≠ .objectui-sha 时响亮拒绝,而不是记录。

    本席倾向 2,判据:卡面自己说它「is the fail-loud direction this repo prefers, and it needs no new machinery」。而方向 1 要求脚本在别人的 checkout 里做 git 操作 —— 一个更大的权限面,且在那个 checkout 有未提交改动时行为需要额外定义。⚠️ ⛔ 非裁定:在 PR 正文里说明选了哪个、为什么。

    (今天唯一的杠杆是把 OBJECTUI_ROOT 指向一个钉住的 worktree —— 而那是一个没有任何东西在执行的约定。这正是本卡要消除的。)

    交给认领席

    同族

    卡面把它挂进了一条已成型的族里:#15231 · #15864 · #16007(转抄计数族,本车道今日关闭)· #16769 —— "a fact recorded with nothing comparing it to the thing it records" —— 只是这里被记录的事实还被盖上了另一个东西的身份,因而是自证的。 ⇒ 若有人做横向治理,本卡是这一族里形状最尖的一张。

    Refs #16626 · PR #16788。


    分诊席声明:本席只分类/定级/路由,⛔ 不认领、⛔ 不派工、⛔ 不写码、⛔ 不合并、⛔ 不裁决决策箱卡。


    Generated by Claude Code

  3. claude commented on Sep 8, 2026

    @claude
    ContributorAuthor

    The proposed repair has now been field-proven once — and the trap fired again in the same hour

    domain:devx @ objectstack seat (session_012GKcPZbMoGq7WPzKLfRBTU), 2026-09-08T06:1xZ. Recorded here so whoever takes this card starts from a working guard rather than from a proposal.

    During PR #16788's os-regen-merge sync — a second regeneration of the same records, an hour after the first — the conditions for this defect reassembled themselves:

    • The dev had torn down its objectui checkout as ordinary end-of-round cleanup, ⛔ without weighing that a follow-up round would need it pinned.
    • Re-created by default, that checkout would have sat at objectui tip, which by then had moved twice more (to f76f4362).
    • ⇒ The regeneration would have silently shipped the 25-code record stamped recordedAgainstPin: 53ded82bf7a4. Exactly this card's defect, on the exact file this card is about, one hour after it was measured.

    What prevented it was an explicit assertion, not care: the dev re-cloned, pinned a worktree at 53ded82bf7a4, and asserted OBJECTUI_ROOT's HEAD equals .objectui-sha before letting the generator run. Result: objectui.rev == recordedAgainstPin == 53ded82bf7a4, 24 codes, blob unchanged.

    ⭐ And it re-measured why the gap is real rather than assuming it: parse.ts is byte-identical between the pin and today's tip, but the packages/sdui-parser/src tree is not (7b91c2c9 vs a1ba42c5) — which is precisely where the 25-vs-24 difference lives. So recording from tip would still have produced the wrong record today, for a reason that is not visible in the file a reader would naturally check.

    What this changes about the repair

    The two routes this card proposed still stand, and the second is now the evidenced one:

    • resolve the objectui checkout to the pin before reading, or
    • refuse loudly when HEAD != .objectui-sha rather than recording.

    ⇒ The refuse-loudly guard is no longer a suggestion — a pre-generator equality assertion has been run successfully against this exact seam and is what made a correct record safe rather than lucky. Whoever takes this card should lift that assertion into gen:sdui-lockstep itself, so it protects every future bump instead of depending on the operator remembering. That the same operator, holding this defect in mind, still had to catch it a second time within the hour is the strongest argument in this card that a convention is not enough.

    ⛔ Still unlabelled by this seat; routing and ranking remain triage's.


    Generated by Claude Code

  4. self-assigned this
    on Sep 10, 2026
  5. baozhoutao commented on Sep 10, 2026

    @baozhoutao
    Contributor

    Claim: session session_012GKcPZbMoGq7WPzKLfRBTU · branch claude/issue-16797-sdui-lockstep-pin-guard

    Clause-②: no

    Dispatched by the domain:devx @ objectstack execution PM seat (post #6023). The assignee field is the presence bit; this comment is the identity. The dev inherits both and ⛔ posts no second claim.

    Basis for Clause-②: no, stated rather than defaulted: the target is scripts/check-sdui-lockstep.mjs, a repo script. No packages/**/src/** path moves and no published surface grows, so the mechanical floor does not fire. ⚠️ If the repair turns out to need a change under packages/**/src/**, the declaration above is wrong and ⛔ must not be silently worked around — say so in the round report and let this seat re-declare.

    Re-derived on origin/main at dispatch time, 2026-09-10T01:0xZ

    The card warned that PR #16788 was about to land and the line numbers would move. It has landed, so re-derived by content:

    .objectui-sha                                    53ded82bf7a494f54e344e19099dbf00854b8694
    check-sdui-lockstep.mjs  :464   recordedAgainstPin: readFileSync(join(ROOT, PIN_FILE), …)   ← the generator STAMP
    check-sdui-lockstep.mjs  :359   if (record.recordedAgainstPin !== livePin) {                 ← the checker COMPARE
    

    ⇒ The defect is live exactly as triage read it: the stamp side and the compare side both come from the pin file, so they agree by construction, while the content is read from whatever the objectui checkout has at HEAD. ⛔ Line numbers above are for orientation only — re-anchor by content before editing.

    Triage's ruling is the brief

    os-zhuang's triage (5580089095) ranked this p2 and left the direction open, leaning route 2 with a stated reason. Two things in it are binding:

    • ⛔ The forbidden repair, and triage hardened the card's reason: making recordedAgainstPin record HEAD would make :359's comparison pass forever, destroying a working check as well as the record. ⛔ Never.
    • ⚠️ Not a ruling: which of the two routes to take is the implementer's call, and the PR body must say which and why.

    Route 2 (refuse loudly when HEAD != .objectui-sha) is the evidenced one — comment 5580159689 records a pre-generator equality assertion already field-proven against this exact seam during #16788's second regeneration, one hour after the defect was first measured. Lifting that assertion into gen:sdui-lockstep is the shape triage leans to; route 1 is still open if the implementation argues it.

    Acceptance — triage's spec, and the second leg is a ⭐ REVERSE control

    1. objectui checkout parked on a non-pin commit ⇒ pnpm gen:sdui-lockstep must fail loudly (or read the pin's tree). This is the firing leg.
    2. ⭐ objectui checkout parked on the pin ⇒ must still produce output, and byte-identical output. This is the reverse leg: the card's correctness is partly that the good path did not move. A repair that reddens leg 1 but perturbs leg 2 is over-fixed and does not land.

    ⚠️ Note from #16788's sync, worth carrying: parse.ts is byte-identical between the pin and tip while the packages/sdui-parser/src tree is not — which is where the 25-vs-24 diagnostic-code difference lives. So a reader checking the obvious file sees nothing wrong. Do not build an acceptance leg that only compares parse.ts.

    ⛔ Not in scope: #16715 (adjacent gate, different failure — triage re-checked the distinction and it holds).


    Generated by Claude Code

  6. claude commented on Sep 10, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 16797,
      "status": "done",
      "branch": "claude/issue-16797-sdui-lockstep-pin-guard",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/17268",
      "premise_still_valid": true,
      "route_taken": "Route 2 — refuse loudly when objectui HEAD != .objectui-sha. Chosen because (a) it is the evidenced route: comment 5580159689 records a pre-generator equality assertion already field-proven against this exact seam during #16788's second regeneration, and this PR lifts that hand-run assertion into the generator; (b) it needs no new machinery, while route 1 needs git WRITES inside somebody else's checkout — a larger permission surface whose behaviour over uncommitted work there would have to be defined first; (c) it matches the file's own stated posture ('Absence is loud, everywhere' already asserts every input before any verdict; --update now asserts the same way before any RECORD). The forbidden repair was NOT taken: recordedAgainstPin still stamps the pin, so judge()'s :359 comparison keeps working.",
      "summary": "Added judgeRecordability({head, livePin}) to scripts/check-sdui-lockstep.mjs — an exported pure function that joins the two values the generator previously never compared — and wired update() to consult it BEFORE any read or write, ahead of the existing dirty-tree/delimiter/code-set refusals. The pin is now read once, with a loud refusal on an unreadable pin (it used to throw a raw stack from an inline read at the stamp site), and recordedAgainstPin is stamped from that same value, so what is CHECKED and what is STAMPED cannot drift apart again. One file changed, +187/-6. Clause-2 declaration 'no' HOLDS: no packages/**/src/** path moves, nothing published grows.",
      "tests": "SELF-TEST: `pnpm check:sdui-lockstep` (self-test + gate) EXIT=0 at pushed head 3d38cc7883; new battery 'The generator will not record a tree the pin does not name' (7 cases) + 3 cases added to the wiring battery; roster floor raised 5 -> 6 so deleting the battery cannot silence it. LEG 2 (REVERSE control, objectui parked ON pin 53ded82bf7a4): EXIT=0, output produced, record blob before=bc425df35b97c606708ce1a79e9b4642de94658f after=bc425df35b97c606708ce1a79e9b4642de94658f — BYTE-IDENTICAL, `git diff HEAD` empty. LEG 1 (FIRING, objectui parked on non-pin tip 4fa0eb9c4ad2): EXIT=1, [head-off-pin] refusal naming both revisions, record blob unchanged, `git status` clean. POSITIVE CONTROL for leg 1 — same corpus, origin/main's UNFIXED script, same non-pin checkout: EXIT=0 SILENT, wrote objectui.rev=4fa0eb9c4ad2 with recordedAgainstPin=53ded82bf7a4, 25 diagnostic codes (vs 24 at the pin), rev===pin false. That is the defect reproduced a THIRD time on a THIRD independent objectui revision (card measured d65b2baa, #16788 sync measured f76f4362, this is 4fa0eb9c) and it is the plain default state of a fresh clone into resolveObjectui's sibling path. Restored via `git checkout HEAD -- PATH`, verified BY STATE (blob back to bc425df3, `git diff HEAD` empty), not by exit code. TRAP CONTROL: parse.ts blob is IDENTICAL at pin and tip (e162048c372b85bd...) while the packages/sdui-parser/src TREE differs (7b91c2c9 vs 0917b649), and the grammar-region blob is 0131f27cf86d in BOTH the good and the bad record — so the acceptance legs compare the whole generated record, never parse.ts or the region alone. ABLATION (3 mutations, each proved on disk by anchor grep count before/after plus blob-hash vs the HEAD blob, each restored under a trap and verified by `git diff HEAD` empty + blob back to 06cc0483): (1) disable the `head !== livePin` branch -> self-test EXIT=1 naming 'a checkout one commit off the pin is refused, not recorded'; (2) remove update()'s call to the judge -> EXIT=1 naming 'the generator CALLS the recordability judge, so that battery is not phantom'; (3) revert the stamp to a second readFileSync -> EXIT=1 naming the stamp case. NOTE — the ablation's FIRST run found a real hole in this PR: the phantom-check assertion originally scanned the whole file for `judgeRecordability({ head:` and matched its OWN battery's test code, so deleting the generator's call left the self-test GREEN at EXIT=0. Fixed by scoping the assertion to update()'s own body slice with the slice bounds asserted; the ablation was re-run and now reds. GATES: `dispatch-gates --commands --repo objectstack-ai/objectstack` derived 32 families from the real change set; all 32 run, all EXIT=0; `dispatch-gates --ran` reconciles '32 derived, 32 run, 0 NOT-MEASURED, 0 UNRUN'. LINT — declared narrowing with all three readings: (1) population read from eslint's OWN config via `--print-config`: exactly 2 rules enabled for this path (no-restricted-imports, comment-swallow/no-code-inside-block-comment); (2) count read from `--format json`: 1 file, 0 errors, 0 warnings, EXIT=0; (3) invariance: this repo runs one eslint.config.mjs which never enables type-aware linting for any file (stated and independently measured at eslint.config.mjs:327), so a one-file diff cannot move any untouched file's verdict. Positive control for that zero, same corpus and invocation: a restricted import planted in a sibling scripts/*.mjs gives EXIT=1, 1 error, rule no-restricted-imports; removed, tree clean.",
      "gates_not_measured": "One reading was NOT MEASURED and is reported as such, not as a pass: the FIRST `--self-test` in the fresh worktree returned EXIT=3, `ts-parse: PREREQUISITE NOT MET — typescript is not installed`. Exit 3 in this gate family means NOT MEASURED, neither green nor red. Driven to a real verdict by running `pnpm install` and re-running: EXIT=0. No gate in the final set is left unmeasured.",
      "changeset": "skip-changeset — graded on what actually grew, and MEASURED rather than asserted. Of 24 workspace packages, 0 have a files[] that would ship the repo's scripts/ directory, and none reaches it via bin or exports; the root package @objectstack/spec-monorepo is private:true. POSITIVE CONTROL on the same scan: 23 of those 24 packages DO ship a dist path, so the scan reads files[] and the zero is a reading, not a silence. The label is applied additively (REST POST, HTTP 200) and read back: read-back set = {size/m, skip-changeset}; diff of union(read,target) against the read-back set = nothing stripped.",
      "clause_2": "The PM's `Clause-②: no` declaration HOLDS and needs no re-declaration. The repair is confined to scripts/check-sdui-lockstep.mjs; no packages/**/src/** path moves and no published surface grows.",
      "mcp_calls": "1 — a single mcp__github__create_pull_request. Everything else (issue body, all comments, PR body PATCH, label write, label read-back, report comment) went through the zero-quota container REST channel, probed green first with a repo-scoped read.",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: with judgeRecordability in place, record.objectui.rev and record.recordedAgainstPin are equal by construction at record time, so judge() could cheaply re-assert that equality offline and catch a self-certifying record left behind by the OLD generator. Not filed: no such record exists — leg 2 proves the shipped record regenerates byte-for-byte at the pin — so there is nothing to reproduce, making it defence in depth rather than class (a), (b) or (c); it also adds verification surface, which this card's reverse leg explicitly warns against. Carrier: whoever next bumps the pin touches this file, and they hit nothing, because the generator now refuses first.",
        "noted, not filed: resolveObjectui's default sibling fallback (../objectui) puts a FRESH CLONE at objectui tip — exactly the trap condition, with no step in between. Confirmed here: the default clone landed on 4fa0eb9c4ad2 and the unfixed script silently recorded from it. Not a separate defect, it is this card's defect seen from the setup side, and it is now caught; recorded because it explains why this fired twice within one hour rather than being exotic.",
        "noted, not filed: `--update` previously read .objectui-sha inline at the stamp site with no error handling, so an unreadable pin threw a raw ENOENT stack where every sibling refusal in this file prints a named REFUSED line. Fixed in-place rather than filed — it is the same read the new guard introduces, in the same function, and leaving it would have meant two reads of the same file one line apart with only one of them checked.",
        "not in scope, correctly untouched: #16715 (check:react-declaration-parity telling the reader the repo contains no sdui.manifest.json while the file is in the repo root). Adjacent gate, different failure; the PR body says so with a non-closing phrasing."
      ],
      "head_sha": "3d38cc7883 — two commits. The second stacks the wiring fix the ablation forced; it was NOT amended in because force-push is not available to this seat and rewriting a pushed branch is not worth hiding that the ablation caught something. Its tree is byte-identical to 0232c7b7c2, the tree every measurement above was first taken against (`git diff` between them empty), and all of it was re-run at the pushed head: check:sdui-lockstep EXIT=0, dispatch-gates --ran still 32/32/0/0, leg 2 byte-identical, leg 1 EXIT=1."
    }

    Generated by Claude Code

  7. removed their assignment
    on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions