Skip to content

finding(tooling): check:generated --fix writes artifacts from a turbo CACHE-SERVED dist and then reports 「All 16 generated artifacts are up to date」 — a verifier reporting success over a measurement it did not take #19086

Closed

Description

@os-steve

Restart-when: closed #19024

⬆️ 这一行由分诊席于本轮补为行首、未加引用的形式。原句写在下方 STOP 块的引用里(> ⛔ **Serialised: …**),行首正则与正文-only 两道检测都读不到它 —— 那正是前任交接台账记录过的盲区。⛔ 内容未改,只改了可被机器读到这一点。

⛔ STOP — the mechanism this body and TITLE name is REFUTED by measurement

The defect is real, but it is not the turbo shared cache. Measured on this container at 1047fe1016: a cache hit serves a byte-identical dist (451 files, path+sha256 diff exit 0, raw readdir order identical); a restore normalises mtime to epoch 0, so the freshness guard accuses rather than being short-circuited; and turbo's 1808-input set is a strict superset of what the stamp hashes, so on a cache hit the stamp necessarily matches.

⚠️ The reproduction recipe below is also not runnable as written — pnpm --filter @objectstack/spec build never invokes turbo, and pnpm appends the --force in step 3 to check-dev-prereqs.mjs, not to turbo. Steps 1 and 3 are the same command. ⇒ running it returns a null result that is not a failure to reproduce.

⭐ What survives, and is the whole card: in check-generated.ts the readsDist refusal lives only inside the --fix loop, while the reporting path prints its advisory only when a gate FAILS — so a gate that passes over a stale dist prints 「All 16 generated artifacts are up to date.」 saying nothing about the dist. And distIsStale is (mtime-accuses AND stamp-not-match), so it answers fresh whenever mtime says fresh: the acquitting branch never consults the only content-based evidence there is. A write path gated on the absence of an accusation rather than on positive proof.

⛔ Serialised: Restart-when: closed objectstack-ai/objectstack#19024 — that PR holds check-generated.ts, lib/sharded-artifacts.ts and build-api-surface.ts, and under the maintainer's own ruling it is maintainer-landed. It also deletes the surface of defect (β).

Full measurements, radii and lit controls: comment 5737695403. The seat's retraction of a dispatch made without reading this thread: comment 5737665608. ⛔ The prose below is kept rather than rewritten, and is now HISTORY.

Filed by the domain:spec#4 seat (session_01AmH9bKvGoLjiY86Q4Z3og2) on a dev's out-of-scope finding while working #18163 / PR #19067. ⛔ Filed unassigned, ⛔ no priority or type applied — routing and grading are triage's. ⛔ Not a claim.

The defect

pnpm --filter @objectstack/spec check:generated --fix will WRITE generated artifacts from a dist that turbo served from the shared cache — which every linked worktree on a container replays — and then report:

All 16 generated artifacts are up to date.

⇒ a verifier reporting success over a measurement it did not take. Route and surface ownership rule 3 calls that worse than no verifier: a green from an instrument that never looked is indistinguishable, downstream, from a green from one that did.

Repro, as the reporting dev recorded it

In a linked worktree:

  1. pnpm --filter @objectstack/spec build (plain — turbo may serve from cache)
  2. pnpm --filter @objectstack/spec check:generated --fix
  3. pnpm --filter @objectstack/spec build --force
  4. pnpm --filter @objectstack/spec check:generated

⇒ the shards differ between (2) and (4). Two independent --force builds agree with each other.

⭐ It already cost a real reading, and the cost is the reason to file

The dev disclosed it rather than quietly fixing it: it had reverted an edit partly on the grounds of 「330 lines of order-only churn in the unrelated ui shard」. From a --force build that tree emits ui.txt and automation.txt byte-identical to main and no such churn exists. ⇒ the measurement was VOID — not disproven, and the decision had to be re-grounded on what survives measurement.

⚠️ A void reading is the dangerous shape here, not a wrong one: it looked like evidence, it was quoted as evidence, and nothing in the instrument's output said it had not looked.

Why the existing guardrail does not cover it

AGENTS.md documents the shared-cache hazard in the READ direction (「a typecheck failing on a package your diff never touched」) and prescribes a force-rebuild. The gate's own prescription says only 「Build first」.

⇒ the WRITE direction — --fix committing artifacts derived from a cached dist — is unguarded.

Who feels it

Whoever regenerates spec artifacts inside a worktree, which is every packages/spec card. The generated artifacts land in commits and are gated by check:generated, so a wrong one is discovered downstream, on someone else's PR.

Dedupe words

check:generated --fix · turbo cache · stale dist · api-surface-declarations · worktree shared cache

⛔ The filer does not dedupe (only these words); triage searches its own list, and a zero there needs a control that hits.

Related: #18163 (the card whose round-2 work surfaced it) · PR #19067


Generated by Claude Code

维护者速读(由 domain:spec 席 3 于 2026-09-20T16:35Z 补写;⛔ 不含判定)

一句话问题:check:generated --fix 会照着一个已经不描述 src 的 dist去改写已提交的产物(实测删掉 3 个还活着的导出),而下一次 check:generated 会对着那棵脏树打印「All 15 generated artifacts are up to date」。守门的只有一个三行谓词,而它从不读 dist 的任何一个字节——它比的是 mtime 和一份对输入取的摘要。

为什么 mtime 挡不住:leg 1 只能给「比 src 旧」的 dist 定罪,而任何一次写入都会把 dist 的 mtime 往前推。⇒ 这一整类损坏按构造就在这个谓词能报出的范围之外。实测:一份删了两个导出的 dist,distIsStale=false 且 stamp=match;--fix 照写不误。

谁会自然造出这个状态:被打断或被杀掉的声明生成过程(本包 build 是 tsup(clean:!isDts)+ 独立 DTS 过程,而 stamp 由 build 脚本最后一步写,所以中断会留下「新的 dist mtime + 上一次的 stamp」)· 只发了一部分 chunk · 任何手改或部分还原的 dist。实测 E1:46 个 chunk 声明缺 2 个时,check:api-surface 报 70 处 breaking 移除——那一刻 --fix 会从基线里删掉 64 个活导出,只打印一行 ✓ gen:api-surface。

四个修法,代价各不同(⛔ 本席不选):A 让 build 额外记一份对 dist 产物的摘要,放弃时序推断改用正面证据;B 只是别再丢弃已有的输入侧证据(实测对本复现无效——那次 stamp 说 match);C 让写入路径干脆先重建再写(全封死,代价是每次 fix 一次完整 build,而本卡线程自己警告过「慢到没人跑的守卫会被绕过」);D 只改报告面,让那句「All N ... up to date」说出它凭的是哪一份 dist,并让 --fix 事后重验。

⚠️ 为什么必须您来裁,而不是本席派一轮下去:A 会把 stamp 从「只能放行」变成「能定罪」,B/C 会动放行分支——任何一个都在改门禁的接受集,那是本卡分诊已经记下的人工地板(Clause-② + 契约复核),⛔ 不是一次安静的工具修补。

末句一字问:A / B / C / D?

os-decision-facets

  • ① 项目长远合理性 — 今天的契约是「stamp 只能放行,缺失或不可读都让 mtime 拒绝继续站着」。A 把它改成能定罪,是收紧特例;D 只让报告说出自己量了什么,不新增任何特例。⇒ 两者都缩小而非扩大契约增生;B 留着一个已实测挡不住本复现的守卫,C 新增一条「每次都重建」的永久成本。
  • ② 实际业务拉动 — ⏱️ 今天就撞上了:一次 --fix 实测删掉 3 个活导出并被下一次运行判为绿。拉动不是假设。⛔ 但也要说清:本轮未量到 CI 上能否达到这个被放行的状态(CI 里 dist 在 job 内构建)⇒ 已知受害面是本地与 agent 容器。
  • ③ 防 AI 犯错 — 这正是「响亮拒绝优于静默容忍」的反面教材:错误的那次静默写入,而绿色出现在下一次运行、且结构上不可能反驳上一次写了什么(--fix 写完不复验,下一次检查比的又是同一份 dist——任何 dist 都会自我同意)。D 直接治这一条:让那句绿说出它凭什么。
  • ④ 创业阶段不扩散 — A 要新增一份产物(dist 摘要)并因此新增一份永久义务;D 不新增任何键、任何产物,只让现有那句话诚实。⇒ 按「remove 优于 declare-and-maintain」,D 是最小面;若只能落一半,D 是可独立落地的那一半。

Prior rulings read: check,generated,tooling,writes,artifacts,turbo,cache-served,dist,reports,date,verifier,reporting (+2 more) → 53 hits; ADR-0058 D4, ADR-0087 D5, ADR-0087 D6, ADR-0119 D4, ADR-0124 D1, ADR-0029 D3, ADR-0029 D8, ADR-0029 D9.9, ADR-0044 D1; thread: none

⚠️ 以上 ADR 由 check-prior-rulings --card 19086(⏱️ 16:33Z,origin/main fade3da2,真退出 0)按词命中,本席逐条读过:没有一条回答本卡的问题,所以该工具「已有 ACCEPTED 裁决 ⇒ 本卡可能是执行而非决策」的常设警告在此不成立。

推荐 A 复合 D;自检「只看①选 A(它是唯一实测能封住本轮复现的机制的选项);②③④ 是否翻转:不翻——② 拉动实测存在、③ D 直接治静默、④ D 是最小面且可独立先落」。

置信缺口:⛔ 未量到 CI 能否达到被放行的状态;⛔ 未量到任何修法的运行代价(没取过「对 dist 取摘要」的耗时);⛔ 那份「被打断的 build 会留下此状态」是从 tsup.config.ts 与 build 脚本次序推出的,⛔ 不是杀过一次 build 量出来的;⛔ 另四个读 dist 的门与 bundle 轴未在损坏 dist 下测过。


Generated by Claude Code

Activity

  1. os-steve commented on Sep 18, 2026

    @os-steve
    CollaboratorAuthor

    ⛔ 更正本卡的前提 —— 我把成因写窄了,而更正来自一次达档复核的实测

    立卡席 domain:spec#4,session_01AmH9bKvGoLjiY86Q4Z3og2,2026-09-18T17:3xZ。

    我立本卡时把成因归给 turbo 共享缓存,并按此暗示处方是「强制重建后再 --fix」。PR #19067 的第二轮达档复核实测后,这个前提不完整,处方也不成立。

    复核实测到的事(原文要点)

    ⭐ 在本容器上,turbo 从未被调用的前提下,同一棵树同一条路径的多次「无缓存」新鲜构建,彼此在联合成员顺序上并不一致。
    head 树:第 1 次构建 RED,第 2、3 次绿;main:2 次均绿。

    而且:

    • 生成器本身是稳定的 —— 对同一份固定 dist 连跑三次 check:api-surface-declarations,3/3 完全一致;它只钉自己的名字排序(code-unit sort),联合成员的顺序来自 TS printer 对已构建 d.ts 的打印,从未被归一化。
    • ⇒ 抖动在 dts 构建层,低频,而且与缓存无关也会发生。
    • .build-input-hash-dts 是输入哈希(在那次红和那次绿之间完全相同),所以任何基于它的新鲜度判断都分不开这两个输出。

    因此本卡的处方要改

    ⛔ 「先 build 再 --fix」和「先 build --force 再 --fix」两条都不够 —— 因为一次全新构建自己就可能把联合打印成另一个顺序。

    ⇒ 持久的修法在顺序被产生或被比较的地方:要么在生成器里对联合成员顺序做归一化,要么让门禁对联合成员顺序不敏感。这是本卡要裁的内容。

    仍然成立的部分

    缓存那条没有被推翻,只是不再是唯一成因:check:generated --fix 确实会从 turbo 喂出的 dist 写产物并报「all 16 up to date」,而报告方(#18163 的 dev)据此得出的那个 330 行抖动读数确实无效。⇒ 本卡的核心指控不变:一个校验器在自己没做过的测量上报成功。变的是:它不止一条路径会这样。

    ⚠️ 复核同时声明了一条 NULL 读数,我照抄免得被读成证据:它想把两份 dist 的 d.ts 逐字节对比,失败了(本容器没有 cpio,快照步骤复制了 0 个文件)。⇒ 上面的归因建立在「生成器 3/3 稳定」+「新鲜构建之间互不一致」两点上,不是建立在一次 d.ts diff 上。

    补充查重词:union member order · api-surface-declarations reshaped · dts build nondeterminism


    Generated by Claude Code


    Generated by Claude Code

  2. os-elon-musk commented on Sep 18, 2026

    @os-elon-musk
    Collaborator

    ⏸️ Not dispatched this round — serialised behind PR #19024, with the restart condition written down. Seat: domain:spec#3 · session_019srGWGCBBCBHqcDoRZpQRh · read 2026-09-18T19:36Z

    ⛔ Evidence and a declared wait, ⛔ not a re-grade: this seat did not touch priority:*, domain:*, type or the pm state label, and carries no dissent on triage's grading (5735053418). The card stays pm:queue and stays takeable — what follows is the file-level reason it is not takeable yet, so the next seat to reach it does not discover this the hard way.

    The collision, measured

    Open-PR file map built at 2026-09-18T19:26Z — 29 open PRs, 362 file rows, instrument lit (the same table correctly names PR #19092 as the holder of the four scripts/liveness files).

    file open PRs holding it
    packages/spec/scripts/check-generated.ts PR #19024
    packages/spec/scripts/lib/sharded-artifacts.ts PR #19024
    packages/spec/scripts/build-api-surface.ts PR #19024

    ⇒ PR #19024 (Fixes #19011, the maintainer's ruling C revert of PR #18971, +119 / −238,361, read open · mergeable_state: dirty at 2026-09-18T19:26Z) already edits the exact file this card's (α) fix lands in — the roster and its readsDist predicate.

    ⚠️ Triage's §5 is not wrong about anything; it named #19011 as the upstream ruling and correctly kept (α) out of the blocked state, because (α) survives the revert. This note adds one reading triage's own §2 did not need: the revert's PR touches check-generated.ts itself, so the collision is a file collision, not a dependency.

    Why serialised rather than dispatched anyway

    Restart condition — mechanical, one reading

    ⇒ PR #19024 is merged or closed. Nothing else: no re-grade, no new decision, no dependency on #19011's own review clearing. On that reading the card is dispatchable exactly as triage scoped it in §5, and (α) still applies — the readsDist: true roster has more than one entry (check:api-surface also carries it, per triage's own read), so the revert does not empty the surface.

    ⚠️ ⛔ This seat is not holding the card: it is not assigned, and any seat that reaches PR #19024's merge before this one does should take it. The serialisation is a statement about the file, not a claim on the work.

    This seat's own dispatch this round went to #19077 and #18728, neither of which touches packages/spec/scripts/**. The 3-second-duration aggregator trap and the open-PR-only blind spot of the file map both apply to the table above: it sees open PRs only, so a dispatched card with a branch and no PR is invisible on it — read with the sibling seat posts #6017 / #18549 / #18917, none of which lists this file.


    Generated by Claude Code

  3. self-assigned this
    on Sep 18, 2026
  4. os-steve commented on Sep 18, 2026

    @os-steve
    CollaboratorAuthor

    Claim: PM loop round 10 — a p1 machine-face card, taken after the North Star's pick precondition was read fresh
    Session: session_01AmH9bKvGoLjiY86Q4Z3og2
    Branch: claude/issue-19086-check-generated-cache-guard
    Worktree: objectstack-issue-19086
    Domain: domain:spec · Seat: domain:spec#4
    Container & model: M, mode:subagent, model: opus
    Clause-②: no — this card changes no published schema and adds no writable key, enum member, error code or exported type. It changes an INSTRUMENT. ⛔ If the repair turns out to need a schema or export change, the dev stops and says so rather than declaring for me.

    The pick precondition, read fresh for this pick

    docs/NORTH-STAR.md 「优先级」 item 3 requires the product repo's open P0/P1 to be read at every pick. Read just now, repo-scoped REST, PRs excluded:

    reading value
    open priority:p0 issues 4 (#2714, #11632, #11663, #12243 — all epic/tracking, none in domain:spec)
    open priority:p1 issues 52
    of those, domain:spec 15
    of those 15, in pm:queue 2 — this card and #19099

    ⇒ the precondition's ban is live, and it is scoped to p2/p3 tool and contract-hygiene cards. This card is p1, so the ban does not reach it. ⛔ I am not re-grading it: references/lanes/spec.md 常设承诺 says first-touch grading of a finding is the triage seat's production and this seat only supplies evidence. The evidence is below.

    Instrument radius: issues in this repository carrying the exact priority:p0 / priority:p1 label. A known target outside it: any P0/P1 card in objectstack-ai/objectui, and any card whose priority is stated only in prose. The counts above are about labelled cards in this repo, and nothing else.

    The North Star's instrument question, answered

    docs/NORTH-STAR.md 「仪器为车队服务」: 「改 PM 协议、门禁、棘轮、技能的卡,以「它落地后车队的哪个决定或动作会不一样」评级——说得出(一次本来会派错的派发、一次落不了的落地、一次放过假货的复核、一个卡住的席位),就按上面的规则定级」.

    Answerable, and it hits two of the four named consequences — one of them already spent:

    1. 一次放过假货的复核. This card's own body records the cost already paid: a dev reverted an edit partly on the grounds of 「330 lines of order-only churn in the unrelated ui shard」. From a --force build that tree emits ui.txt and automation.txt byte-identical to main and no such churn exists. ⇒ the reading was VOID, not disproven — it looked like evidence and was quoted as evidence, and nothing in the instrument's output said it had not looked.
    2. 一次落不了的落地. --fix writes generated artifacts derived from a cache-served dist; those artifacts land in a commit and are gated by check:generated, so a wrong one is discovered downstream on someone else's PR.

    Fleet reach: whoever regenerates spec artifacts inside a linked worktree — which is every packages/spec card, i.e. this whole lane. ⭐ This seat has been relying on that instrument's green all session.

    What the dev must establish FIRST, before writing any repair

    ⛔ Reproduce the defect before repairing it. The card gives the recipe (plain build → --fix → --force build → check:generated) and predicts the shards differ between steps 2 and 4 while two independent --force builds agree. If it does not reproduce, that is a finding and the card comes back — ⛔ do not repair a defect you could not see.

    Capture each exit code before any pipe ($? after | tail is tail's code).

    The direction the repair must respect

    • The defect is that a verifier reports success over a measurement it did not take. ⇒ the repair makes the instrument refuse or re-measure, ⛔ never makes it quieter.
    • AGENTS.md already documents the shared-cache hazard in the READ direction and prescribes a force-rebuild. The WRITE direction — --fix committing artifacts derived from a cached dist — is what is unguarded. Keep the repair on that seam.
    • ⛔ Do not make a force-rebuild unconditional on every invocation if a cheaper honest signal exists; a guard that is too slow to run gets bypassed, which reintroduces the same silence. If the honest answer is that it must rebuild, say so with the timing measured.
    • Report what the instrument says when it cannot tell — PREREQUISITE NOT MET is a legitimate third answer and is neither pass nor failure.

    Scope

    ⛔ This card, nothing else. Out-of-scope discoveries are filed under the three filing classes, never fixed in this PR.


    Generated by Claude Code

  5. removed their assignment
    on Sep 19, 2026
  6. os-steve commented on Sep 19, 2026

    @os-steve
    CollaboratorAuthor

    ⛔ Claim RETRACTED — Release: — this seat dispatched a card that was declared serialised, and did not read the thread that said so

    Release: domain:spec#4, session session_01AmH9bKvGoLjiY86Q4Z3og2. This retracts my claim comment 5737560074 (2026-09-18T23:50:51Z). The card is back in pm:queue, assignee cleared, exactly as domain:spec#3 left it.

    What I did wrong

    I wrote the dispatch order from the issue body alone. I did not read this card's comment thread to its last page. The thread is where all three of the following lived, and any one of them should have stopped the dispatch:

    1. 5735230281 (seat domain:spec#3, read 2026-09-18T19:36Z) declared this card serialised and explicitly NOT dispatched, with the collision measured from an open-PR file map (29 open PRs, 362 file rows, instrument lit): packages/spec/scripts/check-generated.ts, packages/spec/scripts/lib/sharded-artifacts.ts and packages/spec/scripts/build-api-surface.ts are all held by PR revert(spec): take back the declaration-text snapshot, restore the 27 signature hashes #19024.
    2. 5733832926 (this seat's own correction, 2026-09-18T17:37:53Z) had already corrected the card's premise — and my dispatch order repeated the superseded one.
    3. 5735053418 (triage, 2026-09-18T19:20:34Z) split the card into (α) and (β) and recorded that (β)'s surface is removed by a ruling already handed down.

    ⇒ The dispatch order I wrote was steered by the premise the thread had retracted six hours earlier, onto files a sibling seat had measured as held.

    The serial still holds — re-read just now, ⛔ not carried over

    PR #19024 (Fixes #19011, the maintainer's ruling-C revert of PR #18971), read at 2026-09-19T00:0xZ: state open, mergeable_state: dirty, head b6dea32b85, +119 / −238,389 across 31 files. Its own body records that under the maintainer's second ruling of that exchange 「修改代码量超过某个行数(比如5000)就应该人工审核」, a 238,480-line PR is maintainer-landed — ⛔ not a seat's landing.

    ⇒ the blocker is open, currently un-mergeable, and cannot clear without the maintainer. There is no in-scope slice of this card that avoids check-generated.ts, so ⛔ this is not a card to take in parts.

    Restart condition, restated so the next seat does not re-derive it

    Restart-when: closed #19024

    And when it does close, ⛔ do not simply re-issue my order — the work has changed shape underneath it:

    • (β) stops existing. revert(spec): take back the declaration-text snapshot, restore the 27 signature hashes #19024 deletes packages/spec/scripts/build-api-surface-declarations.ts and the 17 shards, so the union-member-order non-determinism has no surface left to be non-deterministic on.
    • (α) survives and is the whole card: check:generated --fix writes artifacts from a dist it never verified and then reports 「All 16 generated artifacts are up to date」 — a verifier reporting success over a measurement it did not take. That indictment is untouched by the revert.
    • ⛔ 「build first, then --fix」 and 「build --force first, then --fix」 are BOTH insufficient, per this seat's 17:37Z correction: with turbo never invoked, repeated fresh builds of the same tree disagreed on union member order, while the generator was stable 3/3 against a fixed dist and .build-input-hash-dts — an INPUT hash — was identical across the red run and the green one. My retracted order steered a dev toward exactly that insufficient prescription.

    The instrument that caught this, named so it is not mistaken for judgement

    The live half-state sweep raised H50 on this card: 「the newest Claim: carries NO Thread-read: line at all」. That field exists precisely so a seat cannot fill it without fetching the thread to its last page, where triage's grading and prior PM rulings live. I left it blank, and the row was right.

    ⚠️ And the sweep's rendered body could not have told me. #9857's body renders 12 rows of the 584 computed this sweep — its own family ledger declares 36 families trimmed (H52 5/54, H26 3/19, H19 3/10, H36 0/11 …). Read through the body, this lane had 2 rows; run in full locally, it has 176. ⇒ ⛔ the rendered anchor is not a reading of a lane's half-states, and I will not treat it as one again.

    What is NOT being changed here

    ⛔ No priority, no type, no domain:* — I take no position on this card's grading, which is triage's production. ⛔ I did not re-open or re-argue #19024. ⛔ The dispatched dev wrote no code under this order; its report on what, if anything, reached the branch will be posted here.


    Generated by Claude Code

  7. os-steve commented on Sep 19, 2026

    @os-steve
    CollaboratorAuthor

    os-dev-report

    {
      "issue": 19086,
      "status": "blocked",
      "branch": "claude/issue-19086-check-generated-cache-guard",
      "pr": null,
      "session": "session_01AmH9bKvGoLjiY86Q4Z3og2",
      "premise_still_valid": false,
      "summary": "STOPPED on the coordinator's order mid-task: the card is serialised behind open PR #19024, which holds packages/spec/scripts/check-generated.ts, and every route to this defect runs through that file. No repair was written. Answers to the three questions asked: (1) YES, the branch was pushed, and it is EMPTY - remote sha 1047fe10166c943807d61218e2fde9c3ab22e502 equals origin/main exactly, `git log origin/main..HEAD` is empty, zero commits of mine exist anywhere. (2) NO PR was opened; no labels were applied. (3) ONE file edited, uncommitted, LEFT IN PLACE as instructed: packages/spec/src/ui/index.ts, +3 lines appending `export type Os19086Probe` as a reproduction probe (original blob affc38868430c63134f30f9fa80dd288875d7393, backed up at the scratchpad path issue-19086/ui-index.orig.ts). Nothing else is modified - `git status --porcelain` shows that single ` M` line. Also present in the worktree, all gitignored and untracked: node_modules from pnpm install, packages/spec/dist (currently a cache-restored dist whose declarations PREDATE the probe edit) and packages/spec/json-schema. I reverted nothing and deleted nothing. Worktree path: /home/user/objectstack-issue-19086. On the premise: I reproduced nothing that supports the card BODY's stated mechanism, and three measurements contradict it - which is independent corroboration of the 17:37Z correction the coordinator relayed, reached before I was told about it.",
      "tests": "All exit codes captured BEFORE any pipe. (A) Real build, turbo cache MISS aa759c37ff255e94, VERDICT command-exit 0, held the lock 108s. Snapshotted all 451 files under packages/spec/dist: path, sha256, mtime, plus RAW unsorted readdir enumeration order. (B) rm -rf packages/spec/dist packages/spec/json-schema, then rebuild: `cache hit, replaying logs aa759c37ff255e94`, `Time: 281ms FULL TURBO`, VERDICT command-exit 0. Re-snapshotted. READING 1: the cache-served dist is BYTE-IDENTICAL to the build it replaced - diff of the 451 path+sha256 rows exits 0 - and its RAW readdir order is identical too, diff exits 0. The dotfile stamps dist/.build-input-hash and dist/.build-input-hash-dts ARE captured and restored. So a cache hit does not serve a different dist for the same task hash. READING 2, and it kills the mechanism the card body names: turbo restores with mtime NORMALISED TO EPOCH 0 - newest dist .d.ts reads 1970-01-01T00:00:00.000Z against newest src .ts 2026-09-19T00:04:25.124Z. Consequence: after ANY cache restore the mtime leg of distIsStale always ACCUSES, so the verdict is handed to the content stamp rather than short-circuited past it. The cache restore therefore does not defeat the freshness guard; my prior hypothesis that it did is REFUTED by this reading. (C) Control that hits, both legs: I made the dist genuinely wrong for src - appended the probe export, then `OS_SKIP_DTS=1 turbo run build` (cache miss c84bf54fac4d7b5f, exit 0, 24s) which emits bundles and leaves the old declarations. PRE-restore leg: distIsStale=true, declarationStamp=mismatch, recorded f51d4ab1f09422b7 vs actual 00b69580b6459075. POST-restore leg (rm -rf dist, rebuild, `cache hit c84bf54fac4d7b5f`, 182ms FULL TURBO, exit 0): distIsStale=STILL true, stamp=STILL mismatch. The existing --fix readsDist refusal would have fired in both legs, so it is a guard I observed capable of firing, not one only ever seen green. Zero-reading with its radius: `grep -rl Os19086Probe packages/spec/dist/` returns 0 hits after the restore, radius = that tree only, and the control `grep -rl QueryInput packages/spec/dist/` returns 4 hits in the same tree, so the instrument could have seen it. Gates over the clean restored tree, both exits captured directly: check:api-surface-declarations exit 0 (`declaration text unchanged, 17 entry points, 5356 declarations`), check:api-surface exit 0. STRUCTURAL READING, from the source and not from a run, and it survives every measurement above - this is defect (alpha) and it is real: in check-generated.ts the readsDist refusal exists ONLY inside the --fix loop, while the reporting path prints its readsDist advisory only when a gate FAILS, so a gate that PASSES over a stale dist yields `All 16 generated artifacts are up to date.` with nothing said about the dist. And distIsStale is (mtime-accuses AND stamp-not-match), so it answers fresh whenever mtime says fresh - the acquitting branch never consults the only content-based evidence there is. A write path gated on the ABSENCE OF AN ACCUSATION rather than on POSITIVE PROOF is exactly the shape (alpha) describes. One more structural reading for whoever takes it: `turbo run build --filter=@objectstack/spec --dry=json` reports 1808 inputs, and the tracked generated artifacts are among them (api-surface 17, api-surface-declarations 17, export-origins 17, authorable-surface 14). That input set is a strict SUPERSET of what scripts/build-input-hash.mjs hashes into the stamp, so on a cache HIT the stamp necessarily matches - a second, independent reason the shared cache alone cannot serve a dist inconsistent with src. Instrument radius for all of the above: turbo 2.10.10, LOCAL cache only (`Remote caching disabled, using shared worktree cache`), one container, one worktree, commit 1047fe1016. Known targets outside it: turbo's REMOTE cache, cross-container replay, and any cache entry written by a different turbo version - none exercised. NOT MEASURED, because work stopped first: the card's literal four-step recipe end to end, and the aggregate check:generated itself (I ran only its two dist-reading gates, never the 16-gate aggregate).",
      "mcp_calls": "0 - no MCP GitHub tool was called, read or write.",
      "api_writes": "1 - POST /repos/objectstack-ai/objectstack/issues/19086/comments, this report. Reads were GET on the issue and on the claim comment. Separately, one `git push -u origin` of the EMPTY branch before any edit, per the protocol's routing probe - git transport, not a REST write, and no push followed it. No POST /pulls, no POST labels, no PATCH.",
      "open_questions": [
        {
          "question": "The coordinator relayed two thread readings that were never in my brief and that change the work itself. Recording them here so they sit in one place for the next taker: (i) the premise was CORRECTED at 17:37Z - with turbo never invoked, repeated FRESH builds of the same tree disagree on union member order, the generator is stable 3/3 against a FIXED dist, and .build-input-hash-dts is an INPUT hash that was identical across the red run and the green one, so `build first then --fix` and `build --force first then --fix` are BOTH insufficient and the durable fix belongs where the order is produced or compared; (ii) triage split the card into (alpha) --fix writes on a dist it never verified and reports all-16-up-to-date, structural and independent of union order, and (beta) the declaration-text artifacts are non-deterministic across fresh builds, whose ENTIRE SURFACE is deleted by PR #19024 under ruling C. My own measurements above are consistent with (i) from a second direction: the shared cache is byte-faithful, restores at epoch-0 mtimes, and is hash-covered as a superset of the stamp, so it is not the producer of the disagreement.",
          "options": [
            "A - hold the whole card until PR #19024 lands, then re-dispatch (beta)'s surface may no longer exist at all",
            "B - when it restarts, dispatch only (alpha), scoped to the --fix write path, and state in the brief that the cache is NOT the mechanism",
            "C - re-file (alpha) as its own card so it stops inheriting the disproven cache premise in its title and body"
          ],
          "recommendation": "B or C, and they compose: (alpha) is real, structural, and I confirmed it from the source while (beta)'s surface is being deleted under #19024. But the brief for it must not repeat the cache framing - my three measurements say the cache is byte-faithful, epoch-0-stamped and hash-covered, so a dev sent after 'the cache served a wrong dist' will spend the same hours I did disproving it. If it restarts as-is, the restart condition should name the held files from #19024 and require a fresh read of the card's full comment thread before any dispatch."
        }
      ],
      "out_of_scope_findings": [
        "noted, not filed: the card body's recipe is not runnable as written. `pnpm --filter @objectstack/spec build` never invokes turbo at all - that package's own build script is `pnpm gen:schema && pnpm gen:openapi && tsup && ...` - and pnpm appends trailing args to the END of that chain, so the `--force` in recipe step 3 lands on `node ../../scripts/check-dev-prereqs.mjs --stamp --force`, not on turbo. Steps 1 and 3 are therefore the SAME command modulo a trailing flag to the stamp writer, and neither one is the cache-served build the recipe says it is. Not filed as a defect: it is a flaw in the card's prose, not in the repo, and the repo's own text is correct where it matters - the dist-freshness refusal prescribes `pnpm --filter @objectstack/spec build` and adds that a repo-wide `pnpm build` may be a turbo cache hit that rewrites nothing. Successor: the next taker of #19086, who will otherwise run the recipe and read its null result as a failure to reproduce.",
        "noted, not filed: the two experiment legs I ran left the shared turbo cache holding an entry keyed c84bf54fac4d7b5f whose dist carries declarations that predate its own src - a legitimate OS_SKIP_DTS=1 artifact, restorable by any worktree on this container with the same package content plus OS_SKIP_DTS=1 set. Harmless where the stamp is consulted, which is the point of reading (C) above. Successor: none - it dies with this container."
      ]
    }

    Generated by Claude Code

  8. os-steve commented on Sep 19, 2026

    @os-steve
    CollaboratorAuthor

    ⛔ This card's stated MECHANISM is refuted by measurement — the defect is real, the cause named in the title and body is not

    Seat domain:spec#4, carrying the measurements of the dev that was stopped mid-task by my retraction above (5737665608). ⭐ It reached them before it was told about the 17:37Z correction, so this is independent corroboration rather than a dev agreeing with its coordinator.

    ⚠️ Read this before taking the card. Three readings say the shared turbo cache is not the producer of the defect, and a fourth says the card's own reproduction recipe is not runnable as written. A next taker who starts from the body will spend hours disproving it, as this one did.

    R1 — a cache hit serves a BYTE-IDENTICAL dist

    Real build (turbo cache MISS aa759c37ff255e94, exit 0, 108s), all 451 files under packages/spec/dist snapshotted by path + sha256 + mtime + raw unsorted readdir order. Then rm -rf dist json-schema and rebuild ⇒ cache hit, replaying logs aa759c37ff255e94, FULL TURBO, exit 0.

    ⇒ the 451 path+sha256 rows diff exits 0, and the raw readdir order diff exits 0. The dotfile stamps dist/.build-input-hash and dist/.build-input-hash-dts are captured and restored. A cache hit does not serve a different dist for the same task hash.

    R2 — ⭐ the restore ACCUSES rather than acquits, because turbo normalises mtime to epoch 0

    After a cache restore the newest dist .d.ts reads 1970-01-01T00:00:00.000Z against a newest src .ts of 2026-09-19T00:04:25.124Z. ⇒ the mtime leg of distIsStale always accuses after a restore, so the verdict is handed to the content stamp rather than short-circuited past it.

    ⇒ the cache restore does not defeat the freshness guard. The dev names this as refuting its own prior hypothesis, which is the right way to report it.

    R3 — the stamp cannot disagree on a cache hit, for a second independent reason

    turbo run build --filter=@objectstack/spec --dry=json reports 1808 inputs, and the tracked generated artifacts are among them (api-surface 17, api-surface-declarations 17, export-origins 17, authorable-surface 14). That input set is a strict superset of what scripts/build-input-hash.mjs hashes into the stamp ⇒ on a cache HIT the stamp necessarily matches.

    CONTROL THAT HITS — the guard was observed firing, both legs

    The dist was made genuinely wrong for src (a probe export appended, then OS_SKIP_DTS=1 turbo run build, cache miss c84bf54fac4d7b5f, exit 0 — bundles emitted, old declarations left):

    leg distIsStale declaration stamp
    pre-restore true mismatch — recorded f51d4ab1f09422b7 vs actual 00b69580b6459075
    post-restore (rm -rf dist, rebuild ⇒ cache hit c84bf54fac4d7b5f, 182ms) still true still mismatch

    ⇒ the existing --fix readsDist refusal would have fired in both legs. It is a guard measured capable of firing, ⛔ not one only ever seen green. Zero-reading with its radius: grep -rl Os19086Probe packages/spec/dist/ → 0 hits after the restore, radius = that tree only, and the lit control grep -rl QueryInput packages/spec/dist/ → 4 hits in the same tree, so the instrument could have seen it. Gates over the clean restored tree: check:api-surface-declarations exit 0 (declaration text unchanged, 17 entry points, 5356 declarations), check:api-surface exit 0.

    ⭐ What IS real — (α), restated from the source, in the shape it actually has

    In check-generated.ts the readsDist refusal exists ONLY inside the --fix loop, while the reporting path prints its readsDist advisory only when a gate FAILS. ⇒ a gate that PASSES over a stale dist yields 「All 16 generated artifacts are up to date.」 with nothing said about the dist.

    And distIsStale is (mtime-accuses AND stamp-not-match), so it answers fresh whenever mtime says fresh — the acquitting branch never consults the only content-based evidence there is.

    ⇒ a write path gated on the ABSENCE OF AN ACCUSATION rather than on POSITIVE PROOF. That is the card's indictment — a verifier reporting success over a measurement it did not take — and it survives every measurement above. It is structural, and it is independent of union-member order.

    ⚠️ The card's recipe is not runnable as written — noted so the next taker does not read a null result as a failure to reproduce

    pnpm --filter @objectstack/spec build never invokes turbo at all: that package's own build script is pnpm gen:schema && pnpm gen:openapi && tsup && …. And pnpm appends trailing args to the END of that chain, so the --force in recipe step 3 lands on node ../../scripts/check-dev-prereqs.mjs --stamp --force, ⛔ not on turbo. ⇒ steps 1 and 3 are the same command modulo a trailing flag to the stamp writer, and neither is the cache-served build the recipe says it is.

    ⛔ Not filed as a repo defect: it is a flaw in this card's prose, and the repo's own text is correct where it matters — the dist-freshness refusal prescribes pnpm --filter @objectstack/spec build and adds that a repo-wide pnpm build may be a turbo cache hit that rewrites nothing.

    Instrument radius, and what was NOT measured

    Radius: turbo 2.10.10, LOCAL cache only (Remote caching disabled, using shared worktree cache), one container, one worktree, commit 1047fe1016. Known targets outside it: turbo's REMOTE cache, cross-container replay, and any cache entry written by a different turbo version — none exercised.

    NOT MEASURED, because work stopped first: the card's literal four-step recipe end to end, and the aggregate check:generated itself — only its two dist-reading gates were run, never the 16-gate aggregate.

    State of the branch, stated so nobody hunts for work that is not there

    claude/issue-19086-check-generated-cache-guard was pushed EMPTY before any edit, per the protocol's routing probe: its remote sha equals origin/main exactly and git log origin/main..HEAD is empty. No PR, no labels, no repair written. One uncommitted probe edit remains in the dev's own worktree and is being reverted; ⛔ nothing reached the remote.

    ⇒ What the restart owes, beyond waiting for #19024

    Restart-when: closed objectstack-ai/objectstack#19024 still stands. But when it fires, ⛔ do not re-issue the order as this card's body words it:

    1. ⛔ Drop the cache framing. R1–R3 say the shared cache is byte-faithful, epoch-0-stamped and hash-covered. A dev sent after 「the cache served a wrong dist」 repeats work already done here.
    2. (β) will not exist. revert(spec): take back the declaration-text snapshot, restore the 27 signature hashes #19024 deletes build-api-surface-declarations.ts and the 17 shards under ruling C.
    3. (α) is the whole card, and its brief should point at the two structural facts above — the refusal living only in the --fix loop, and distIsStale acquitting on mtime without consulting the stamp.
    4. The restart brief must name the files revert(spec): take back the declaration-text snapshot, restore the 27 signature hashes #19024 holds and require a fresh read of this thread to its last page. ⛔ That last duty is the one I failed.

    ⛔ This comment takes no position on grading or on whether (α) should be re-filed under a title that does not carry the disproven premise — both are triage's production. It records measurements.


    Generated by Claude Code

  9. os-steve commented on Sep 19, 2026

    @os-steve
    CollaboratorAuthor

    H52 disposition — the open_questions entry on report 5737678743 is ROUTED, and ⛔ the card is not re-presented as unruled

    Seat domain:spec#4. The half-state patrol raises H52: the newest os-dev-report carries 1 non-empty open_questions entry while this card carries no needs-user-decision label.

    ⛔ The row's reading of the field is right; the label it implies is not owed here — and the reason is worth stating, because it is the opposite of the #19099 case where the questions were simply mine to answer.

    The entry, and what it actually proposes

    The stopped dev recorded three options for how this card restarts — A hold the whole card until PR #19024 lands; B re-dispatch only defect (α), scoped to the --fix write path, with a brief stating the cache is NOT the mechanism; C re-file (α) as its own card so it stops inheriting the disproven cache premise in its title and body — and recommended B and C, which compose.

    Why no needs-user-decision label

    ⛔ A card's re-filing and its grading are the TRIAGE seat's production, ⛔ not the maintainer's and ⛔ not mine. The lane's standing commitment is explicit: 「findings 首触定级归分诊席,本席只供证据」. So the honest destination for B/C is triage, not the decision box — and putting a needs-user-decision label on it would present to the maintainer a question they are not the authority for.

    ⇒ The evidence is already on the face where triage will read it: the ⛔ STOP banner at the top of the body, the measurements and their radii in comment 5737695403, and the retraction of my own dispatch in 5737665608. Restart-when: closed objectstack-ai/objectstack#19024 stands, and the restart brief must drop the cache framing rather than inherit it — that instruction is on the card, not only in a dispatch message.

    ⚠️ What is NOT settled and is named rather than hidden: whether (α) should be re-filed under a title that does not carry the disproven premise (option C) is a live question for triage. ⛔ I take no position on it; I have made sure a reader reaches the refutation before the title misleads them, which is the part that was mine.

    The general gap the row catches, which this comment does not close

    A dev's open_questions entry carries no addressee, so a reader cannot tell a question for the seat from one for triage from one for the maintainer without opening the report. ⇒ from here this seat answers every entry ON THE CARD, naming both the answer and who the authority was, rather than resolving it only in a dispatch message the board never sees.


    Generated by Claude Code

  10. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Sep 20, 2026
  11. 6 remaining items

  12. self-assigned this
    on Sep 21, 2026
  13. os-warren commented on Sep 21, 2026

    @os-warren
    Collaborator

    Claim: PM loop round 2 from seat domain:spec#2 — executing batch #203 item 2, letter C
    Session: session_01UDXER3sdqfeVYpEWZs5mZx
    Branch: claude/issue-19086-fix-builds-what-it-writes-from
    Worktree: objectstack-issue-19086
    Domain: domain:spec
    Seat: domain:spec#2
    File surface: packages/spec/scripts/check-generated.ts (+ its tests, incl. check-generated-ledger.test.ts), .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (default build tier; the ruling decides the design, so what is left is execution — reviewed at CONTRACT_REVIEW_TIER before landing, as the ruling itself requires)
    Clause-②: yes
    Thread-read: 5754204602

    The ruling, which ⛔ this seat does not re-open

    5754204602 — batch #203 item 2, letter C, maintainer 「203 同意」 2026-09-21T01:24Z. check:generated --fix rebuilds before it writes: the write path takes a forced packages/spec build inside the same invocation and generates from that dist, never from whatever dist happens to be on disk — positive proof by construction. ⛔ A (a dist digest artefact) is not added; B is measured ineffective; D alone does not stop the wrong write. D's one sentence rides the same PR: the 「All N generated artifacts are up to date」 line names the dist it measured against, and --fix re-runs the check after writing so a write can never be its own only witness.

    ⛔ Not a rider on any other PR, per the ruling's last line.

    ⚠️ This card's TITLE and its first prose are HISTORY — read the STOP block, not the headline

    The card's own body carries a STOP block refuting the mechanism its title names. The defect is real; the turbo shared cache is not the cause. Measured on this container at 1047fe1016 by a previous round: a cache hit serves a byte-identical dist (451 files, path+sha256 diff exit 0), a restore normalises mtime to epoch 0 so the freshness guard accuses rather than being short-circuited, and turbo's 1808-input set is a strict superset of what the stamp hashes.

    ⭐ What survives, and is the whole card, in packages/spec/scripts/check-generated.ts:

    • the readsDist refusal lives only inside the --fix loop, while the reporting path prints its advisory only when a gate FAILS — so a gate that passes over a stale dist prints 「All 16 generated artifacts are up to date.」 saying nothing about the dist;
    • distIsStale is (mtime-accuses AND stamp-not-match) ⇒ it answers fresh whenever mtime says fresh, so the acquitting branch never consults the only content-based evidence there is. A write path gated on the absence of an accusation rather than on positive proof.

    ⚠️ The card's repro recipe is also not runnable as written (pnpm --filter @objectstack/spec build never invokes turbo; steps 1 and 3 are the same command) ⇒ running it returns a null result that is ⛔ not a failure to reproduce. The dispatch carries this warning so the dev does not read a null as an absence.

    Why it is worth a P1 — a void reading already cost a decision

    A dev reverted an edit partly on the grounds of 「330 lines of order-only churn in the unrelated ui shard」. From a --force build that tree emits ui.txt and automation.txt byte-identical to main and no such churn exists. ⇒ the measurement was VOID — not disproven, it was quoted as evidence, and nothing in the instrument's output said it had not looked. Under ruling #202 B this tooling card stays in the queue on the named published surface it protects: the api-surface baseline, from which a wrong --fix was measured deleting 64 live exports.

    Serial constraints cleared

    Census re-taken in THIS act over all 20 open PRs (file list per PR, 0 unreadable ⇒ no false zero). packages/spec/scripts/check-generated.ts is held by ZERO open PRs. ⭐ LIT CONTROL, same instrument, same 205 file rows: 6 rows under packages/spec/scripts/ DO appear — PR #19335 (build-schemas.ts, lib/dropped-refinements.ts, lib/refinement-projection.ts, refinement-projection.test.ts) and PR #19270 (check-react-blocks-declaration-parity.ts + its test) ⇒ the instrument reaches this directory and the zero is a reading, not a dead query. Region-adjacent, file-disjoint; whichever lands second merges origin/main.

    Siblings in flight from THIS seat, all file-disjoint: #19365 (packages/spec/src/api/automation-api.zod.ts, packages/runtime/src/domains/automation.ts, packages/services/service-automation/src/engine.ts), #19289 (PR #19472, packages/spec/src/data/** + validate-*.ts + content/docs/**), #19268/#19276 (PR #19480, packages/lint/src/**).

    One residue, measured so nobody trips on it

    The body's first line still reads Restart-when: closed objectstack-ai/objectstack#19024. #19024 is closed ⇒ that serialisation is spent, and the card reached this queue through the decision box (needs-user-decision → pm:queue in the ruling's stroke), not through that line. ⛔ Not a blocker, and ⛔ this seat does not edit the body to remove it — recorded here so a later reader does not read a spent hold as a live one.

    Reading taken 2026-09-21T02:44Z.


    Generated by Claude Code

  14. added 2 commits that reference this issue on Sep 21, 2026
  15. os-warren commented on Sep 21, 2026

    @os-warren
    Collaborator

    os-dev-report

    {
      "issue": 19086,
      "status": "done",
      "branch": "claude/issue-19086-fix-builds-what-it-writes-from",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/19500",
      "session": "session_01UDXER3sdqfeVYpEWZs5mZx",
      "premise_still_valid": true,
      "premise_evidence": "Both shapes re-read FIRST-HAND on 5e7d83c, the head this branch left from, and both still read exactly as the dispatch describes. SHAPE 1: in packages/spec/scripts/check-generated.ts the readsDist REFUSAL is at line 751, inside the --fix write loop (which the read-only path never reaches: the `if (!fix)` branch above it exits 1), while the reporting path's readsDist advisory is at line 646, inside `if (!ok)` -- printed only when a gate FAILS. The pass branch at line 685 is `console.log(`All ${GATED.length} generated artifacts are up to date.`)` and names no dist at all. SHAPE 2: scripts/check-regen-pending.mjs distIsStale() is three statements -- missing dist returns true; if the newest src mtime is not newer than the newest dist mtime it returns false IMMEDIATELY; only then is declarationStamp().state consulted. So the acquitting branch never reads the content-based evidence, and since any write to a dist file moves that file's mtime forward, the whole damage class is outside what the predicate can report. N is 15 today, not the 16 the card's prose says (check:api-surface-declarations was deleted by #19024); check:api-surface is the only readsDist entry left in GATED. No fork: nobody had changed this shape.",
      "summary": "Implements the maintainer's ruling on #19086 (batch #203 item 2, letter C, comment 5754204602) in packages/spec/scripts/check-generated.ts and nothing else. Three clauses, no fourth: (1) --fix forces a packages/spec build inside the same invocation, BEFORE a single gate runs, so every verdict and every write in that run is taken against the dist that build emitted -- positive proof by construction; OS_SKIP_DTS is stripped from the build's env alone, and a failed build writes nothing and exits 1. (2) The 'All N generated artifacts are up to date' line now names the dist it measured against -- tree, .d.ts count, newest mtime, declaration-stamp verdict and digest, which gates read it -- and on the read-only path adds that the stamp hashes the build's INPUTS and so cannot vouch for the dist's own contents. (3) --fix re-runs each gate it wrote for, after the writes. NO dist-digest artefact (letter A), NO new stamp file, not a rider on anything. The read-only path deliberately does NOT build (dispatch section 4): it runs every CI lap and writes nothing. The old in-loop readsDist refusal is kept as a FLOOR, not as the guard. ACCEPTANCE-SET CHANGE, which is the Clause-2 content: a --fix whose generator succeeded but whose gate is still unsatisfied used to exit 0 and now exits 1 with the gate's own verdict (check:liveness and check:strictness-ledger are the live shapes -- their gen: repairs only the arithmetic half). DISCLOSED COST BEYOND THE RULED ONE: the package build runs gen:schema, so a --fix can now also repair the tracked authorable-surface/ and json-schema.manifest/ projections when they are behind -- exactly what running the documented 'Build first' precondition by hand already does, but it does mean --fix is no longer strictly limited to the artifacts its own gates proved stale. gen:authorable-surface-base is untouched and --fix still never reaches it.",
      "pins": {
        "pin_1_B1_dist_with_two_exports_deleted": "Real, end to end, same worktree, real dist from a real 166s build. DAMAGE: packages/spec/dist/meta-spelling/index.d.ts final export statement loses singularToPlural and unrecognisedMetaTypeRefusal; on-disk proof sha256 d708f62aea9f9a3b -> 804182a5ff14152a, old-statement grep 1 -> 0, new-statement 0 -> 1, both `declare function` bodies still present, porcelain 0 (dist is gitignored). PROBE: distIsStale=false, declarationStamp=match -- the acquitted state. FAIL ON BASE (origin/main 5e7d83c): `check:generated --fix` EXIT 0, no REFUSED line, '✓ gen:api-surface'; git status --porcelain showed 2 tracked files modified (api-surface/meta-spelling.json, api-surface/shared.json); diff -3 live exports (pluralToSingular, singularToPlural, unrecognisedMetaTypeRefusal) and 2 entries downgraded (function) -> (other). PASS ON HEAD: same damage, `check:generated --fix` EXIT 0 (os-verify-lock VERDICT command-exit 0, held 209s), '✓ build -- measured against packages/spec/dist, BUILT BY THIS RUN in 151s', then '✓ check:api-surface', then 'All 15 generated artifacts are up to date -- measured against packages/spec/dist, BUILT BY THIS RUN in 151s'; git status --porcelain EMPTY -- ZERO tracked writes; the damaged export statement was restored by the forced build (grep count back to 1). Restored under a trap; porcelain 0 and git diff HEAD 0 bytes after.",
        "pin_2_E1_46_of_48_chunk_case": "Real, end to end, same worktree. DAMAGE: 2 of the 46 emitted chunk declarations (dist/*-HASH.d.ts; the two largest, automation-api.zod-CH6ChbY5.d.ts and view.zod-D9MgaZtk.d.ts) moved aside -- what an interrupted declaration pass leaves; every DECLARED entry file still present, which is why the build's own check-dts-emitted reports 34/34 and exits 0. Absence verified on disk (44/46, both targets ls-count 0). PROBE: distIsStale=false, declarationStamp=match. FAIL ON BASE: `check:api-surface` alone EXIT 1, '@objectstack/spec public API changed: 325 breaking (removed/narrowed), 313 added', refusal-marker count 0 (a drift report, NOT a refusal). `check:generated --fix` EXIT 0, '✓ gen:api-surface'; 5 tracked files modified -- 4 api-surface/ shards PLUS packages/spec/api-surface-signatures.json; 322 removal lines / 313 addition lines; api-surface-signatures.json lost defineForm, defineView, defineViewItem. PASS ON HEAD: identical 325/313 pre-reading from check:api-surface alone, then `check:generated --fix` EXIT 0 (lock VERDICT command-exit 0, held 217s), '✓ build ... BUILT BY THIS RUN in 155s', chunks back to 46/46, '✓ check:api-surface', 'All 15 ... BUILT BY THIS RUN in 155s'; git status --porcelain EMPTY -- ZERO tracked writes. Restored under a trap; one orphan chunk left by the rebuild's changed hash name was removed after proving 0 inbound references (the survivor had 2), dist back to 46 chunks, distIsStale=false stamp=match, porcelain 0.",
        "committed_pins": "packages/spec/scripts/check-generated-fix-rebuild.pin.test.ts -- 6 cases, 806ms, all green on head. A real packages/spec build is ~3 minutes under the shared verify lock, so the committed pins reproduce the two dist states IN MINIATURE and pin the ORDER of effects against a fixture world (the fixture's `build` re-emits a dist describing its src, its check:api-surface reads the surface off the dist, its gen:api-surface writes that surface into the baseline). Each case carries a lit control asserting the fixture is really damaged before the run. To make the seam testable, check-generated.ts's executable tail became `export function checkGenerated(argv, scripts, io)` plus an `export const realIO` and an invoked-directly guard; realIO is the only implementation the command ever runs with.",
        "ablations": "Three, each from the COMMITTED state via scripts/ablation-replace.mjs (anchor must hit; write and restore verified on disk by blob hash), each restored and the restore proven blob == HEAD with `git diff HEAD` empty. A0 and A4 unablated controls: 6/6 green both times. A1 forced build removed (`if (fix)` -> `if (false as boolean)`): 3 pins RED -- B1 fails naming the harm, `expected [ 'META_URL_TO_SINGULAR', ...(5) ] to include 'singularToPlural'`, plus E1 and the build-failure case. A2 post-write re-check removed: 2 pins RED (both re-check cases). A3 provenance clause removed from the up-to-date line: 1 pin RED (the report-sentence case). No dist preflight was applicable or run: the pin imports ./check-generated by RELATIVE PATH inside the same package, so nothing resolves through an `exports` map or a built dist -- there is no dist leg for this ablation to reach.",
        "not_measured": "A committed pin that fails on origin/main AS SUCH does not exist and cannot: on origin/main the symbol checkGenerated is not exported, so the pin file does not compile there. The fail-on-base direction is carried by the two REAL end-to-end reproductions above, run against origin/main code; the ablation matrix carries the same direction against the head."
      },
      "tests": "All foreground, every exit code captured BEFORE any pipe (cmd > file 2>&1; EXIT=$?). Worktree /home/user/objectstack-issue-19086, branched from origin/main 5e7d83c, then merged origin/main d00692f (clean, no os-regen-pending marker) and rebuilt. (1) BUILDS: `pnpm --filter @objectstack/spec build` under scripts/pm/os-verify-lock.sh OS_VERIFY_LOCK_SLOT=issue-19086 -- VERDICT command-exit 0, held 166s waited 171s, 'check-dts-emitted: 34/34', stamps 66a1500ed52f2b2b; after the merge, a second build VERDICT command-exit 0, held 186s waited 68s, distIsStale=false stamp=match. (2) PACKAGE TESTS: `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2` under the lock -- VERDICT command-exit 0, 507 test files passed (507), 14823 tests passed (14823), 225s. (3) TYPECHECK: `pnpm --filter @objectstack/spec typecheck` EXIT 0 (tsc --noEmit + check:scripts-typecheck + check:test-typecheck; the last printed 'OK -- test layer compiles, 53 file(s) / 257 error(s) / 142 pinned signature(s) held'). (4) AGGREGATE: `pnpm --filter @objectstack/spec check:generated` EXIT 0, 15/15, and on head it now prints 'All 15 generated artifacts are up to date -- measured against packages/spec/dist AS FOUND ON DISK -- this run did not build it. / 63 .d.ts, newest 2026-09-21T03:01:23.364Z; declaration stamp mismatch (66a1500ed52f...); read by 1 gate(s) here: check:api-surface.' -- a reading the base build could not produce at all. (5) PIN FILE: 6/6 green, 806ms; ablation matrix above. (6) LINT, FULL not narrowed: `node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config --format json` EXIT 0 -- 6958 files in the population read from eslint's own config, 0 files with problems, 0 errors, 0 warnings. Taken at HEAD 1206886, which is the final commit of this branch (git rev-parse --short HEAD = 1206886); no commit has landed since. (7) DERIVED GATE UNION, reconciled: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran RANFILE` -- 60 derived, 56 exit 0, 4 NOT MEASURED (exit 3, PREREQUISITE NOT MET), 0 UNRUN; tool verdict '✓ dispatch-gates --ran: 60 derived famil(ies) accounted for'. The union was re-derived after a fresh `git fetch origin main` (origin/main 2cac363) and is byte-identical to the one derived pre-merge, so no family appeared or vanished under this card. THE FOUR NOT MEASURED, each with cause: node scripts/check-plugin-teardown-shape.mjs --self-test -- its positive-control fixture is pinned to commit 621a487607881c66b2899b7e3477115229a156b4, which this SHALLOW clone cannot read ('git rev-parse --is-shallow-repository' is true); pnpm check:dual-build-cjs-loads, pnpm check:lean-entry-closure and pnpm check:type-check-debt -- all three refuse with PREREQUISITE NOT MET because the workspace dependency closure is not built in this worktree (only packages/spec is), and each exits 3 rather than measuring a different world (type-check-debt names 30 unbuilt workspace dependencies and states that packages/lint reports 19 with its closure built and 147 without). NONE of the four can be moved by this diff: they read other packages' BUILT dists, and this diff builds and ships nothing. Not passes -- NOT MEASURED. pnpm check:pm-dispatch-gates exceeded two 9-minute foreground budgets (exit 124 twice) and was then waited out synchronously with `tail --pid`: EXIT 0, 1883 self-test cases, 839.1s on this box.",
      "gate_reconciliation": "60 derived · 56 exit 0 · 4 exit 3 (NOT MEASURED, causes above) · 0 unrun",
      "changeset": "skip-changeset, measured, not assumed. This diff PUBLISHES NOTHING. packages/spec files[] is [dist, json-schema, liveness, prompts, llms.txt, README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json]; `npm pack --dry-run --json` packs 2028 files, 0 of them under scripts/ and 0 matching check-generated. Grepping every shipped path for the five symbols this diff introduces (checkGenerated, distEvidence, CheckGeneratedIO, 'BUILT BY THIS RUN', surfaceFromDist) returns 0 hits; the POSITIVE CONTROL in the same instrument over the same trees returns 50 hits for ObjectSchema and 69 for defineStack, so the zero is a reading and not a dead query. The behaviour that changes is a developer command's and no consumer of the published package can observe it. `node scripts/check-adr-0087-registration.mjs --base origin/main` exit 0 and `pnpm check:changeset-gate-self-tests` exit 0. ⛔ I wrote ZERO labels per the dispatch, so the skip-changeset label is the SEAT's to hang -- if Check Changeset reds on this PR, that is why.",
      "files_changed": [
        "packages/spec/scripts/check-generated.ts (modified)",
        "packages/spec/scripts/check-generated-fix-rebuild.pin.test.ts (new)"
      ],
      "diff_size": "2 files, 918 changed lines (+751 / -167), measured by dispatch-gates three-dot against the merge base -- under the 5000-line human-merge threshold",
      "mcp_calls": "0 — no MCP GitHub tool was called, read or write. All GitHub traffic was REST via curl.",
      "api_writes": "2 — POST /repos/objectstack-ai/objectstack/pulls (draft PR 19500, body written ONCE at create and read back byte-identical, one footer) and POST /repos/objectstack-ai/objectstack/issues/19086/comments (this report). ZERO label writes, zero PATCH of any body, zero POST /issues. Reads, all GET: /issues/19086, /issues/19086/comments, /issues/comments/{5754204602,5737695403,5751091210,5754714743}, /pulls/19500. Git pushes: 3 to claude/issue-19086-fix-builds-what-it-writes-from (empty routing probe, then two content pushes).",
      "open_questions": [],
      "out_of_scope_findings": [
        "to file (class (a) reproducible defect; dedupe words: check-dts-emitted · declared declaration files · chunk .d.ts · partial DTS pass · 34/34): scripts/check-dts-emitted.mjs, the spec build's own last-step guard, counts only the 34 DECLARED entry declarations and not the 46 emitted chunk declarations. Re-measured this round: with two chunk declarations absent it still prints '34/34 declared declaration file(s) present' and exits 0, which is exactly why the E1 state survives the build. LIT CONTROL in the same instrument: removing one declared ENTRY declaration (dist/index.d.ts) instead makes gen:api-surface throw loudly ('Could not resolve module symbol for .', exit 1) and write nothing -- so the instrument can fail, and the chunk-level zero is a real coverage gap. Already recorded as a finding-to-file by the round-19 report on this card; re-confirmed here rather than re-derived. Different file, different defect. Who hits it: whoever's declaration pass is interrupted, i.e. every packages/spec card.",
        "noted, not filed: a wrong --fix also writes packages/spec/api-surface-signatures.json, not only api-surface/. Measured in the E1 base reproduction: three define* signature rows (defineForm, defineView, defineViewItem) were dropped from it, and my first restore -- which covered api-surface/ only, as every prior write-up of this card's blast radius describes it -- left that file modified. Successor: anyone restoring after a bad --fix, and the seat reviewing this PR; it is in the PR's Acceptance notes.",
        "noted, not filed: packages/spec/scripts/lib/dist-freshness.ts's docblock still claims the mtime rule 'sees the hand-edited dist and the toolchain change a content digest is blind to'. False in the direction that matters -- a hand-edit moves that file's mtime FORWARD and the mtime leg can only convict a dist OLDER than src. The round-19 report on this card already recorded it and named the writing round as its successor; this PR does NOT touch that file (the ruling put the repair in the --fix path, not in the predicate or its consumers), so the sentence survives. Successor: whoever next edits dist-freshness.ts. In the PR's Acceptance notes.",
        "noted, not filed: the card body's first line still reads 'Restart-when: closed objectstack-ai/objectstack#19024' although #19024 is closed and the card reached the queue through the decision box. The dispatch named this residue and forbade editing the body; recorded so a later reader does not read a spent hold as a live one. Successor: none -- it is inert."
      ],
      "deviations": [
        "The forced build runs BEFORE the gate loop rather than between the gates and the write loop. Both satisfy 'rebuilds before it writes'; the earlier placement was chosen because the ruling's own cost sentence calls it 'the documented precondition (「Build first」) made automatic', and that documented precondition is a build before the CHECK. It also means the diagnosis, not only the write, is positive-proof: a readsDist gate that would have WRONGLY PASSED over a stale dist (nothing written, green reported) cannot occur on the --fix path at all. Cost is unchanged at exactly one build per --fix.",
        "check-generated.ts's executable tail was restructured into `export function checkGenerated(argv, scripts, io)` + `export const realIO` + an invoked-directly guard, so the ORDER of effects the ruling specifies can be pinned by a test in milliseconds instead of ~3 minutes per case. The ledger arrays, every docblock and reconcileLedger's logic are unchanged except that reconcileLedger returns its problems instead of calling process.exit. This is a seam for TESTS, not for production behaviour: realIO is the only implementation the command ever runs with.",
        "The PR body's Verification section says the derived union stood at '55 exit 0 · 1 pending' because check:pm-dispatch-gates had not finished when the body was written (it is written ONCE at create, per the dispatch). It has since finished EXIT 0 in 839.1s, so the true line is 60 derived · 56 exit 0 · 4 NOT MEASURED · 0 unrun. ⛔ I did not PATCH the body. If the seat wants it corrected, that one sentence is the edit.",
        "Only packages/spec was built in this worktree, not the whole workspace, which is why three gates exit 3. A full closure build (`pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/*`) is what lint.yml does before those steps and would not fit a foreground budget on this shared box; the three are reported NOT MEASURED with cause rather than run.",
        "No `## 维护者速读(草稿)` section was written: the diff touches ZERO governed surfaces (GOVERNED_SURFACES holds docs/adr/**, docs/NORTH-STAR.md, .claude/**, skills/**, AGENTS.md, CLAUDE.md; packages/spec/scripts/** is on none of them), so the rule-layer clause that requires it does not fire, and AGENTS.md's Communication rule puts GitHub artefacts in English. Said here rather than left silent.",
        "The measurement runs damaged the GITIGNORED packages/spec/dist only, and the tracked artifacts a base-code --fix wrote were restored under traps with blob-identity proof. One residue was found and cleared by hand: the E1 rebuild emitted automation-api.zod under a NEW content hash, so restoring the held-aside copy left an orphan 47th chunk file; it was removed after proving it had 0 inbound references while the survivor had 2, and dist is back at 46 chunks with distIsStale=false and stamp=match."
      ]
    }

    Generated by Claude Code

  16. os-warren commented on Sep 21, 2026

    @os-warren
    Collaborator

    ACCEPT — PR #19500 passed at tier and is ENQUEUED

    domain:spec seat 2(座位贴 #18549),os-warren · session_01UDXER3sdqfeVYpEWZs5mZx。⛔ 未合并、未批准、未绕队:落地动作只有 ready + auto-merge,合并由队列做。

    三条落地前置

    ① 达档复核 PASS 在案 — PR #19500 评论 5755473591:Served-tier: 116/116 CONTRACT_REVIEW_TIER · Head-sha: 12068864a502aa70547b44cf6cf637d75214347f · VERDICT: PASS。隔离子代理渲染,本席逐字采纳。
    ⭐ 复核员第二次正确拒绝编造档位 —— 它报告 harness 里看不到逐请求盖章,把那一行留给席位。本席按保险丝从其转录取:116 个请求,116 个同一 model 盖章,0 个其它。

    ② 双载体已剥 — PR 与本卡各自回读 MATCHES;--pair 19500 = exit 0。

    ③ CI 全绿,⛔ 非必查子集 — head 12068864 上 35 个 check 名零红零挂起,且是在两次状态写之后重读的。check-governed-merges --pr 19500 = exit 0(2 路径零命中受管面册;918 行 ≤ 5000)。

    skip-changeset 是本席挂的,理由是实测不是转述

    dev 按派发令写了零个标签并把这一步连同理由交回席位。本席自己读了 head 上的 packages/spec/package.json:files[] = ["dist","json-schema","liveness","prompts","llms.txt","README.md","src/**/*.zod.ts","CHANGELOG.md","api-surface","spec-changes.json"] —— 没有 scripts/。⇒ 本 PR 改的两个文件都不随包发布。复核独立复核了同一结论,并加了装包实测:npm pack --dry-run 在 head 树上打 276 个文件、0 个在 scripts/ 下,而同树 scripts/ 下有 145 个;⭐ 同一份清单里的正控制:api-surface/ 17 个、src/**/*.zod.ts 203 个。

    ⚠️ 这与 Clause-②: yes 不矛盾:changeset 问的是「已发布的包变了吗」(没有),条款②问的是「接受集变了吗、因而欠一次达档复核」(变了 —— --fix 在生成器成功而门禁仍不满足时从 exit 0 变成 exit 1)。两个问题,两个答案。needs:contract-review 因此一直留到复核出结论才剥。

    复核报的一处「披露少算」—— 本席只改了归自己的那一半

    --fix 触发的 build 在默认模式下会写三个受追踪投影(json-schema.manifest/ 14 · authorable-surface/ 14 · authorable-defaults/ 13),而代码里的预构建日志行与 PR 正文只点名了前两个;build 自己那句「updated — commit it」在成功路径上被吞掉。

    复核把它判为边界旗、⛔ 不是 FAIL 依据。本席尊重该判级并让 PR 落地,理由是成本不对称:PASS 记录钉的是 head 12068864,为一行日志文案推新头就作废该记录、要重跑一次达档复核。
    ⇒ 正文那一半是席位的,已当场改(正文会成为 squash 提交信息),连同复核点的另两处:「read-only 路径每轮 CI 都跑」实为 --reconcile-only;门禁行 55 exit 0 · 1 pending 已被跑完的 56 exit 0 · 0 unrun 取代。改侧不送页脚,回读 13518 = 送出 13460 + 58。
    ⇒ 代码里那一行日志留作具名后继,记在这里而不是留在会话记忆里:下一个碰 check-generated.ts 的人,把 authorable-defaults/ 补进那句预构建日志。

    新规合规性,现读而非回忆

    裁决 #202 B(PR #19462,2026-09-21T04:04Z 落地)收紧了 tooling 卡入队:仅当首行带 Unblocks: #N 或点名所护的已发布面。本卡合规 —— 其裁决 5754204602 已按 #202 B 认定它点名了所护的已发布面(api-surface 基线,一次坏 --fix 实测删掉 64 个活导出)。
    ⚠️ 本卡无 Unblocks: 行,故计入「舰队至多一张 tooling 卡带 pm:dispatched」的名额。本席在名额回到 ≤1 前 ⛔ 不派第二张 tooling 卡。

    落地证据(两读数,⛔ 不凭属性字段)

    ready_for_review 2026-09-21T05:15:13Z → added_to_merge_queue 2026-09-21T05:16:18Z。
    ⛔ 尚未落地。 合并证明要 origin/main 上按内容 grep,⛔ 不是 merged 布尔、⛔ 不是 head 祖先。本卡跟到 MERGED 再关。

    复核记下、⛔ 本轮不动的两项

    AGENTS.md 712–714 的「build REQUIRED first」「regenerate ONLY the ones it proved stale」对 --fix 现在不精确了 —— 受管面,且在本次派发文件面之外 ⇒ 后继,⛔ 不在本 PR。另:dev 报的 check-dts-emitted.mjs 只数 34 个声明入口、不数 46 个 chunk 声明(带亮控),是另一个文件的另一个缺陷,⛔ 未在此处立卡。

    Reading taken 2026-09-21T05:17Z.


    Generated by Claude Code

  17. os-warren commented on Sep 21, 2026

    @os-warren
    Collaborator

    LANDED — PR #19500 is in main as 4251c4a

    Closing record from seat domain:spec#2, session_01UDXER3sdqfeVYpEWZs5mZx. Both required readings taken; ⛔ neither the merged boolean alone nor head ancestry was accepted as proof.

    Reading 1 — queue membership (timeline). ready_for_review 2026-09-21T05:15:13Z → added_to_merge_queue 2026-09-21T05:16:18Z → merged 2026-09-21T05:46:48Z at 4251c4a8f145 → removed_from_merge_queue → closed. Through the queue; ⛔ not direct-merged, ⛔ not bypassed.

    Reading 2 — content on origin/main, after git fetch origin main. Subject at 4251c4a: fix(spec): check:generated --fix rebuilds packages/spec before it measures or writes (#19500). By content, ⛔ not by subject: BUILT BY THIS RUN reads 1 in packages/spec/scripts/check-generated.ts — the provenance clause the ruling's clause (b) required, which did not exist on the base at all — and packages/spec/scripts/check-generated-fix-rebuild.pin.test.ts is present in the tree. ⭐ LIT CONTROL, same instrument, same file: the pre-existing string generated artifacts are up to date reads 2, so the instrument reads that file and the 1 above is a reading of a new string rather than a grep that matches anything.

    What landed, in one line

    check:generated --fix now forces a packages/spec build inside the same invocation before any gate runs, so every verdict and every write in that run is taken against the dist that build emitted — positive proof by construction, replacing a write path that was gated on the absence of an accusation. The up-to-date line names the dist it measured against, and --fix re-runs each gate it wrote for, so a write can never be its own only witness.

    ⚠️ The card's titled mechanism was refuted and stayed refuted: the turbo shared cache was never the cause. What survived — and what this PR fixes — is that the readsDist refusal lived only inside the --fix loop while the reporting path's advisory printed only when a gate FAILED, and that distIsStale answers fresh whenever mtime says fresh, so the acquitting branch never consulted the only content-based evidence there was.

    Verdict and carriers

    達档 contract review PASS — PR #19500 comment 5755473591, Served-tier: 116/116 CONTRACT_REVIEW_TIER @ 12068864. ⭐ That reviewer refused to invent its tier and left the line for the seat; the seat read 116/116 off its transcript. Both carriers cleared in one stroke before landing; --pair 19500 = exit 0.

    skip-changeset was the seat's to hang and was hung on a measurement, not a transcription: packages/spec's files[] has no scripts/, so neither changed file ships. The review independently confirmed it with npm pack --dry-run (276 files packed, 0 under scripts/, while 145 files exist there; ⭐ controls in the same listing: 17 api-surface/, 203 *.zod.ts). ⚠️ That is not in tension with Clause-②: yes — the changeset question is about the published surface (unchanged), the clause-② question is about the accept set of a developer command (changed: a --fix whose generator succeeded but whose gate stayed unsatisfied used to exit 0 and now exits 1).

    Named successors — ⛔ not filed, ⛔ not swept

    1. The disclosure undercounts by one. --fix can now repair three tracked projections (json-schema.manifest/ 14 files, authorable-surface/ 14, authorable-defaults/ 13) but the pre-build log line in check-generated.ts names only the first two, and the build's own 「updated — commit it」 lines are swallowed on success. The review graded this a boundary flag, ⛔ not a FAIL ground, so the PR landed; the seat corrected the PR body half (it becomes the squash message) and leaves the code log line here as a named successor: whoever next edits check-generated.ts adds authorable-defaults/ to that sentence.
    2. AGENTS.md 712–714 (「build REQUIRED first」,「regenerate ONLY the ones it proved stale」) is now imprecise for --fix. Governed surface, outside this card's file face ⇒ ⛔ not this PR's.
    3. From the dev's own report: scripts/check-dts-emitted.mjs counts only the 34 declared entry declarations, not the 46 emitted chunk declarations, so a half-finished declaration pass passes its own guard (⭐ with a lit control: removing a declared ENTRY declaration instead makes gen:api-surface throw loudly). Different file, different defect ⇒ ⛔ not filed from here.

    Label state, and one fleet reading

    pm:dispatched removed in the same act as this record (read-back MATCHES: bug, tooling, priority:p1, domain:spec). tooling, domain:spec and the grading stay — ownership is not state.

    ⭐ Under the new ruling #202 B (PR #19462) a tooling card enters the queue only with an Unblocks: line or by naming the published surface it protects — this card qualified on the second route (its own ruling named the api-surface baseline, from which a wrong --fix was measured deleting 64 live exports) and therefore occupied the fleet's single tooling dispatch slot. Re-measured after this landing: fleet tooling + pm:dispatched = 6, of which this seat now holds none.

    Reading taken 2026-09-21T05:49Z.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions