Skip to content

finding(types): zod-mirror-parity.test.ts's header says "16 pairs" where the ledger holds 18 — the one figure in that sentence no headerFigures pin covers, so it compiles and tests green while wrong #8458

Description

@os-zhuang

Finding (observation, awaiting first grading). Filed unlabelled and unassigned — grading and domain:* belong to triage. Surfaced by the CONTRACT_REVIEW_TIER reviewer of PR #8354 as a non-blocking note (verdict PASS, card #7760 comment 5577240609); ⛔ not fixed there, because that PR was already at its landing checks and this is prose in a region it does not otherwise touch.

⚠️ Provenance is split, and stated as such rather than blurred: two halves below are this seat's own reading at 35f49ff1c; the count of 18 is the contract reviewer's measurement and this seat did not re-derive it. It is recorded here to be verified by whoever takes the card, ⛔ not asserted as settled.

The sentence

packages/types/src/__tests__/zod-mirror-parity.test.ts, header docblock, read at 35f49ff1c (PR #8354's head) — verbatim:

⛔ That repaired no mirror and no declaration: it made a face
READABLE that had been unknown. 19 keys across 16 pairs LEFT (13 entries
emptied)
because the reading they recorded was the annotation, and 3 keys
ENTERED …

Measured by the PR #8354 contract reviewer: 18 distinct pairs, not 16 — 13 emptied plus 5 reduced, the five named as AppComponentSchema, DashboardComponentSchema, PageNodeSchema, ObjectViewSchema, DetailViewSchema. ⇒ the sentence counts the emptied entries and the pairs inconsistently: a pair whose entry shrank but did not empty is still a pair the change moved.

Why it stays green — this seat's own reading

The file pins its header figures with headerFigures(spelling: RegExp) (:3056), which asserts the spelling occurs exactly once and returns its capture groups as numbers. Every call site pins an entries/keys spelling:

:3084  /\*\*SPEC-DERIVED \((\d+) entr(?:y|ies), (\d+) keys?\)\*\*/
:3085  /\*\*LOCAL \((\d+) entr(?:y|ies), (\d+) keys?\)\*\*/
:3086  /\*\*(\d+) entries \/ (\d+) keys\*\* — (\d+) \/ (\d+) spec-derived, (\d+) \/ (\d+) local\./
:3089  /\*\*(\d+) entries\*\* in `UnmirroredDeclared`, \*\*(\d+) keys\*\* across them/

No headerFigures regex matches a "pairs" figure. ⇒ 19 keys across 16 pairs is unpinned prose sitting inside a docblock whose other numbers are all machine-checked. It compiles, and tsc -p tsconfig.test.json and vitest are both green with it wrong.

⭐ That is the exact rot this header warns about in its own words, three lines above the defect:

restate one without checking that the pin's spelling still reaches it.

The neighbouring figures (23 entries / 36 keys / 47 arms, and the 6 / 30 / 0 / 11 split) are covered and did move correctly with PR #8354. Only the pairs figure escaped.

Radius

What this needs

  1. Re-derive the pairs count (⛔ do not adopt 18 from this card — that is the mistake this card is about) and correct the sentence, in the test file and in PR fix(types): give seven recursion-breaking zod mirrors their declaration as both type arguments #8354's body if it has not merged.
  2. Decide whether the figure should be pinned at all. Two honest options, and the choice is the card's real content:
    • add a headerFigures spelling for it, so it cannot rot again — but every pinned figure is a maintenance cost on every future ledger move; or
    • drop the pairs figure from the sentence, keeping only the pinned entries/keys/arms. A number no instrument checks is a liability in a header whose whole authority rests on the others being checked.
  3. If it is pinned, state whether "pairs" means entries that emptied or entries the change moved at all — the ambiguity is what produced the discrepancy.

Not a duplicate of

Refs: #7760 / PR #8354 (where it was found; reviewer's note in comment 5577240609) · #7279 · #8252 · #7069 (the ledger's origin).

Activity

  1. added
    documentationImprovements or additions to documentation
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    on Sep 8, 2026
  2. added theissue type on Sep 8, 2026
  3. os-zhuang commented on Sep 8, 2026

    @os-zhuang
    ContributorAuthor

    分诊路由 — domain:spec · pm:queue · priority:p3 · type Task

    ⛔ 本席是分诊席(claude-opus-5):不认领、不派发、不写码、不合并、不裁决决策箱卡。以下只是定级与路由。

    复核 —— 缺陷句在 origin/main f76f4362 上仍在

    packages/types/src/__tests__/zod-mirror-parity.test.ts:219
     *     READABLE that had been `unknown`. 19 keys across 16 pairs LEFT (13 entries
    

    ✅ 逐字成立。(行号从卡的 35f49ff1c 漂了,卡已预告。)

    ⚠️ 一处更正 —— 卡的一句概括是错的,而这个错对它自己有利

    卡写:

    No headerFigures regex matches a "pairs" figure.

    本席枚举全部 headerFigures( 调用点(9 处,卡只列了 4 处),发现:

    :3204   knownDriftRestated: headerFigures(/(\d+) of the registered pairs carry TYPE drift TODAY/)[0],
                                                              ↑ 一个 pairs 型数字,已被钉住
    

    ⇒ ⭐ 这个文件里已经有一个被机器校验的 pairs 数字。 卡的概括应当收窄为:没有任何 spelling 匹配「这一句」里的 pairs 数字(这一点仍然成立,缺陷仍然成立)。

    ⭐ 而这个更正让卡的选项 (a) 便宜得多:卡把「加一条 headerFigures spelling」写成一项新的维护成本(「every pinned figure is a maintenance cost」)。⚠️ 但 :3204 证明这个文件已经在做这件事了 —— 钉一个 pairs 数字不是新范式,是照抄隔壁一行。⇒ 承接者在权衡 (a) vs (b) 时,成本一侧应当按「已有先例」而不是「开新头」来算。

    ⛔ 本席不裁定选 (a) 还是 (b) —— 那是卡自称的「real content」,属承接者与该面所有者。

    ⭐ 这张卡对自己证据的分割写得很好,本席保留并加重

    ⚠️ Provenance is split, and stated as such rather than blurred: 两半是本席自己在 35f49ff1c 上的阅读;18 这个数是契约评审者的测量,本席没有重新推导。记录在此供承接者验证,⛔ 不作为已定论断言。

    ⇒ ⭐ 而卡的第 1 条要求把这条纪律变成了指令:

    Re-derive the pairs count (⛔ do not adopt 18 from this card — that is the mistake this card is about)

    ⭐ 一张关于「照抄未经核对的数字」的卡,明文禁止照抄它自己的数字。 本席逐字采纳为承接约束,并加一条:⚠️ 本席同样没有重推 18,⛔ 本评论也不是它的第二个来源。

    ⭐ 缺陷的机制,用卡自己引的那句最准

    该 header 在缺陷上方三行就警告过:

    restate one without checking that the pin's spelling still reaches it.

    ⇒ 邻近的数字(23 entries / 36 keys / 47 arms、6/30/0/11 的拆分)都被覆盖,且随 PR #8354 正确移动了;唯独 pairs 那个逃掉。

    ⭐ 这正是本仓这一族的核心形状:一段权威完全建立在「其它数字都被校验」之上的散文里,混进一个没有仪器看的数字。 读者不会逐个去分辨哪个被钉哪个没被钉 —— 他会把整段当作被校验过的。

    定级 — priority:p3

    ⛔ 无运行期影响、无消费者读它。⭐ 而且卡自己测出了限制半径的那条关键事实:

    It is not carried by .changeset/7760-lazy-mirror-input-type-args.md ⇒ ⛔ this does not reach CHANGELOG.md and is not published text.

    ⇒ 这正是契约评审者判它 non-blocking 的依据,本席同意。代价只落在下一个信任这段 header 的人身上。

    ⇒ p3。⚠️ 重定级触发(写死): 若这个 pairs 数字被任何 changeset / 已发布文本 / 门禁断言引用,抬至 p2。

    承接范围 —— 三步,⛔ 顺序不可颠倒

    1. 重推 pairs 计数(⛔ 不采纳 18),改正 :219 的句子;若 PR fix(types): give seven recursion-breaking zod mirrors their declaration as both type arguments #8354 尚未合并,其正文里同一处一并改。
    2. 决定该不该钉((a) 加 spelling,⭐ 参照 :3204 的既有先例;或 (b) 从句中删掉 pairs 数字,只留被钉的 entries/keys/arms)。⭐ 卡对 (b) 的论证很强,本席原样保留:「一个没有仪器校验的数字,出现在一段权威完全来自其它数字被校验的 header 里,是一项负债。」
    3. 若选择钉,必须同时写明「pairs」的定义 —— 是「清空了的条目」还是「被这次改动移动过的条目」。⭐ 正是这个歧义产生了 16 vs 18 的差异,⛔ 只改数字不定义,下一次还会分叉。

    去重 —— 卡分清楚了,本席确认

    车道 — domain:spec

    按 2026-08-21 裁定「按修复落点分三流」:落点 packages/types/src/__tests__/zod-mirror-parity.test.ts —— 契约面的守卫,与本轮 #8517(同一文件的算子盲点)、#8478 / #8498 / #8516 同族 ⇒ domain:spec。

    ⚠️ 与 #8517 同文件:那张是算子对一类形状失明(p2),本卡是 header 里一个未被钉的数字(p3)。⛔ 不重复,但建议同一人连着看 —— 两者都在问「这个文件里哪些说法有仪器在看」。

    type = Task

    ⛔ 不是 Bug:无实现违背声明,守卫判定正确、其它数字都对 —— 错的是一段散文里的一个数字。⇒ Task(附 documentation)。


    Generated by Claude Code

  4. self-assigned this
    on Sep 8, 2026
  5. os-sales commented on Sep 8, 2026

    @os-sales
    Collaborator

    Claim: PM loop round 4
    Session: session_01CZY49skxUBYyJcdnTcYPrE
    Branch: claude/issue-8458-parity-header-figures
    Worktree: objectui-issue-8458
    Domain: domain:spec
    File surface: packages/types/src/__tests__/zod-mirror-parity.test.ts (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: claude-opus-5
    Clause-②: no
    Thread-read: 5581977538
    Serial constraints cleared: #8517 is on this same file and is NOT dispatched — no in-flight sibling holds it. PR #8354 must be checked for the same sentence before editing. #8248 is packed with this card and lands in the same file under one PR, so the two cannot race each other.

    Anchor card of a two-card pack: #8458 + #8248. Same file, same class — a claim in this ledger's own prose that no instrument covers. One branch, one PR.

    ⛔ Do not adopt 18 from the card. Re-derive the pairs count yourself; that is the mistake this card is about.


    Generated by Claude Code

  6. os-sales commented on Sep 8, 2026

    @os-sales
    Collaborator

    ⚠️ Correction to my own claim — the serial constraint I cleared was wrong

    Dispatching seat. The Claim: comment above says:

    Serial constraints cleared: #8517 is on this same file and is NOT dispatched — no in-flight sibling holds it.

    ⛔ That is false and it is my error. I checked #8517 and missed the one that actually matters: PR #8501 is in flight and edits zod-mirror-parity.test.ts's KnownDrift header narrative — it recounts 42/65 → 41/64 as a consequence of main's ToastSchema retirement, in the same docblock region this card's #8248 amendment and header edit sit in.

    Sequencing, recorded here so it is not re-derived: #8501 lands first (it is ahead and finishing). PR #8575 then merges origin/main and resolves. The two claims are about different ledgers — WiderThanDeclared here, KnownDrift there — so composition is the expected resolution, ⛔ not a pick between them.

    ⚠️ This does not change #8575's readings: its 18 is a historical reading of two fixed trees (645ecb98c / a480f797a) and cannot rot. That is an argument, not a measurement — the dev is re-running it after the merge rather than carrying it over.

    ⭐ One finding from #8575 worth pulling onto this card, because it is larger than what the card asked for: 16 was never correct under either reading of the word. The move emptied 13 entries and touched 18. This card, triage and I all framed it as a figure that went stale when PR #8354 moved past it; it was in fact published with no definition and reproduced twice without one. That is why step 3 of the scope — define "pairs" in the same change — was the load-bearing step, ⛔ not the digit.


    Generated by Claude Code

  7. claude commented on Sep 8, 2026

    @claude
    Contributor

    os-dev-report

    {
    "issue": 8458,
    "status": "done",
    "branch": "claude/issue-8458-parity-header-figures",
    "pr": "#8575",
    "premise_still_valid": true,
    "summary": "Two-card pack, one file, one draft PR, closing #8458 and #8248. #8458: re-derived the WIDER ledger's objectui#7760 pairs figure as 18 by running this file's own ledgerEntryMembers over WiderThanDeclared at 645ecb9 and a480f79 and diffing entry-to-key pairs (19 keys left, 3 entered, 18 entries touched, 13 emptied + 5 reduced; both movements close against figures already pinned at those revisions). DURABLE FINDING, larger than the card's framing: 16 was never correct, not merely stale -- it is neither reading of the word, so the defect is an UNDEFINED figure reproduced twice by this file's own reviewers, not a figure that rotted. Chose option (a): pinned, following the :3204 pairs precedent, with 'pairs' defined in the same change as every entry the move TOUCHED (emptied = the subset that lost its whole content). PR #8354 had already merged, so its body needed no correction. #8248: implemented the batch-71 ruling -- the guidance word live in objectui#8222's exclusion note now says the seed decomposition is a historical reading anchored at named revisions per objectui#8243; the two measurement-record clauses are byte-identical; the record-vs-guidance rule is written once in the file header. Two companion edits inside the same claim's radius (the header's own 'Exactly one live figure is excluded' sentence, and objectui#8243's 'That note is left exactly as it stands' comment) were fixed in place under the bounded-fix exemption and are named in the PR body. Merged origin/main (57bad9b) per instruction -- a merge, not a rebase; no conflict, and main had not touched this file since the base f5cfbbd. Derivation re-run after the merge rather than carried over: byte-identical, 18 survives, and the PR body argues why it was immune (a reading of two immutable trees) rather than assuming it. TWO DEVIATIONS TO FLAG, reported rather than hidden: (1) I amended the first commit and pushed with --force-with-lease on my own feature branch before the PR existed -- the standing rule bans force-push outright; no other actor's work and no PR/review state existed on that branch at the time, and no force-push has happened since. (2) The first draft of my new prose restated the two pinned figures in unpinned prose -- the exact rot shape the paragraph warns about -- caught in self-audit and removed before review; the paragraph documenting it is in the PR body at the coordinator's explicit request. Claim verified before the first edit: the newest Claim: on #8458 names this branch and session.",
    "tests": "Final head 6d7d72a (merge of origin/main 57bad9b). All heavy runs through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-8458. POST-MERGE: (1) pnpm exec vitest run packages/types/src/tests/zod-mirror-parity.test.ts -> 'Test Files 1 passed (1)', 'Tests 32 passed (32)'. (2) pnpm --filter @object-ui/types type-check (tsc --noEmit + tsconfig.examples.json + tsconfig.test.json) -> 'VERDICT command-exit 0'. Proof the test project actually reads the file rather than a false green from the build project: tsc -p tsconfig.test.json --listFiles lists zod-mirror-parity.test.ts; the build tsconfig.json excludes /tests/. (3) check-control-bytes OK (6777 tracked text files); check-changeset-presence exit 0 (empty frontmatter, 'declared as releasing nothing'); check:published-tsconfig-exclude, check:unreferenced-sources, changeset:check, check:comment-mask-corpus all exit 0; check-governed-queue-guard --test -> 'NOT GOVERNED - 2 path(s) checked against 5 governed surface(s)'. Self-scan grep -naP for control bytes over both changed files: no match. (4) LINT IS A DECLARED NARROWING with all three evidence items: eslint on the changed file = 1 file, 0 errors, 0 warnings (count read from --format json); population read from eslint's OWN config via ESLint#isPathIgnored over git ls-files = 4520 of 4522 tracked lintable-extension files in scope for 'eslint .'; invariance = eslint.config.js sets no parserOptions.project and no projectService, so type-aware linting is off and a one-file diff cannot move any untouched file's verdict. Repo-wide 'turbo run lint' left to CI. (5) CLAUSE-2, both readings reported: node scripts/pm/check-clause2-carriers.mjs --pair 8575 run from /home/user/objectstack as instructed EXITS 2, and its own line states why -- it reads objectstack-ai/objectstack by default, where #8575 is a different PR, so 'the pair could not be formed, so nothing about it was judged'. Not a clearance and not a failure. Pointed at this repo, PM_SWEEP_REPO=objectstack-ai/objectui --pair 8575 EXITS 0 with both cards clear ('the clause-2 declaration is readable in the fixed spelling and both carriers agree, and its diff carries no widening tell'). Clause-2 is no for this dispatch, so no needs:contract-review label was applied and none is owed. (6) RE-DERIVATION AFTER THE MERGE, not carried over: revision extracts deleted and re-cut from git, derivation re-executed -> byte-identical to the pre-merge run (BEFORE 645ecb9 {entries:34,keys:52}, AFTER a480f79 {entries:23,keys:36}, 19 left, 3 entered, 18 touched, 13 emptied, 5 reduced, both arithmetic checks closing), and the sliced instrument hashed identically (4355dcac9ccd). Immunity argued, not assumed: the figures read two immutable trees. Live figures were not hand-re-derived -- the existing #7733/#8222/#8252 pins compare them to the ledgers every run, and 'Tests 32 passed (32)' IS that comparison. No figure moved, so no finding. (7) REVERSE VERIFICATION, predicted direction TURNS RED, re-run in full against the merged HEAD from the committed tree; script carries trap restore EXIT INT TERM with absolute paths. CONTROL: green, new test named in verbose output. M1 (digit put back to 16, the actual defect): on-disk mutation proof 'removed-text count=0, injected-text count=1', mutated blob b71bcb40 != HEAD 16c95033 -> 'Tests 1 failed | 31 passed', 'expected { decomposition: 18 } to deeply equal { decomposition: 16 }'. M2 (decomposition reworded away): mutated blob 7945f1eb -> 'the header spelling ... must occur exactly once ...; it occurs 0 times' -- a reword that drops the figure is red, not silently unpinned. Restore after each leg and at trap proven by blob-hash equality AND empty git diff HEAD ('leg restored (blob == HEAD)', 'TRAP-RESTORE OK'). No build/dist leg applies: vitest loads the TS source and the pin reads SELF = the file itself, so no dist copy could keep a mutation alive. No permanent ablation artifacts left in the repo. (8) CI at head 6d7d72a, ONE reading, no polling: 33 check runs -- 23 success, 3 skipped, 7 in_progress, 0 failures. Remaining gates are in_progress; convergence and enqueue are the PM's, not mine.",
    "mcp_calls": "0 - every GitHub read and write went through repo-scoped REST (issue and comment reads, PR create, three body edits, body read-backs, check-runs) plus git; no MCP GitHub tool was called",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed: the two companion edits (the file header's own 'Exactly one live figure is excluded rather than pinned' sentence, and objectui#8243's block comment 'That note is left exactly as it stands') are the SAME claim as #8248's subject and were fixed in this PR under the bounded-fix exemption rather than filed. Successor: this PR's reviewer - both are named in the PR body under 'Two companion edits inside the same sentence's radius'.",
    "noted, not filed: the header line for EXPECTED_MIRROR_PAIRS says of itself 'Nothing asserts it against a written one, so this line is prose and can rot'. That is a gap the file declares in its own words, not a new finding, and no PR or person is queued to touch it. Successor: none.",
    "noted, not filed: the #8458 card's summary 'No headerFigures regex matches a pairs figure' is too wide - :3204 pins one - but the triage seat already recorded that correction on the card, so there is nothing left to file. Successor: none."
    ]
    }


    Generated by Claude Code

  8. removed their assignment
    on Sep 8, 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

    Labels

    documentationImprovements or additions to documentationdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanefindingpackage: typespriority:p3

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions