The two surfaces that merely restate the gate set should name pnpm verify instead of enumerating it — a restatement of a definition cannot be allowed to drift - #81
Conversation
`package.json`'s `verify` is the definition of the gate set. AGENTS.md's delivery-process Gates cell and the backlog's acceptance floor restated that list verbatim while carrying no per-command information: they exist to point at the bar, and a pointer to a definition should be the definition's name. Both now say `pnpm verify`, which cannot go stale because it *is* the list. This is hardening, not a repair. Every statement of the gate set in the tree was already correct; what changes is that two of them can no longer drift from it. The two fenced blocks in README.md and AGENTS.md keep enumerating, deliberately: their job is to show a reader what runs and what each gate catches, and those per-gate comments are the reason the blocks are worth reading. DESIGN.md's M1 acceptance cell stays at three commands, adjudicated on #73 as a record of what a delivered milestone was accepted against rather than an instruction. Neither cell gains a count or a pointer. A count is the precise mechanism that put five copies wrong in one edit, and naming `package.json` would add a second drift-capable coordinate for a fact `pnpm run` already resolves on its own. The backlog paragraph is re-flowed because the substitution shortened its first line mid-command; the re-flow is confined to that one paragraph. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
Found by the enumeration grep this card mandates, and absent from the card's own table of surfaces. CLAUDE.md's rule restated the composed spelling verbatim and carried no per-command information, so it is a restatement by exactly the criterion that moved the other two. It is also provably drift-capable rather than a statement that has never moved: it said three commands until 4560611 rewrote it, so it is one of the surfaces that went stale when CI gained a fourth gate. "must all pass" becomes "must pass": `all` bound to a plural list of commands and does not read over a single script name. Kept as its own commit so it can be dropped with one revert by anyone who reads the card's "no other file" as binding over the card's own criterion. The preceding commit is complete and self-sufficient without this one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
维护者速读(席位定稿)
改了什么:仓库里「只是指向门槛」的三处文字,从逐条枚举四条 gate 改成直接写 为什么改(加固,不是缺陷):改之前这些话全是对的,没有任何人会被误导。买到的只有一件事——指针不可能再和定义脱节。
要你定夺的一件事:是否保留第二个 commit 顺带一提: 风险与代价:零。纯 Markdown,7 加 8 删,3 个文件。没有任何 gate 读这三个文件——不是断言,是量出来的:三个文件同时毁成两行乱码,四条 gate 依然全部 exit 0,随后在 你要做的:① 看一眼三处新写法的语气合不合你意;② 决定留不留 Generated by Claude Code |
Fixes #78
AGENTS.md,CLAUDE.md) and is left OPEN as a draft for the maintainer to merge. PerAGENTS.md→ Delivery process → Governed surface, the seat reviews it, records an ACCEPT on the issue, and stops there — it does not merge and does not flip it out of draft. Same path PR #77 and PR #40 took.docs/backlog/README.mdis ungoverned, but it travels with the governed files in one PR, so the whole PR waits. See 维护者速读 at the bottom.⛔ Hardening, not a repair
Every statement of the gate set in the tree was already correct before this PR. Nothing was broken and nothing misled anyone. This PR buys exactly one thing: the surfaces that merely point at the bar can no longer go stale from the bar.
package.json:21is the definition;pnpm verifycannot drift because it is the list.What changed
Three cells, plus one paragraph re-flow. Seven insertions, eight deletions, markdown only.
AGENTS.md130 — delivery-process table, Gates row`pnpm verify`.— one line, cell shape and the row's trailing period keptdocs/backlog/README.md29–32 — the acceptance floor every dispatched card inherits`pnpm verify` green— paragraph re-flowed (see below)CLAUDE.md8–9 — the inlined ⛔ gate rule`pnpm verify` must pass— separate commit, independently revertableWhy
docs/backlog/README.mdneeded a re-flow, not a one-line editThe floor wrapped mid-command across lines 29–30. Substituting
pnpm verifyshortens line 29 by 54 characters, which would have left a ragged wrap reading as damage, so the whole paragraph was re-flowed at the file's existing width. It occupies the same four lines (29–32) as before, and the re-flow is confined to that one paragraph — nothing outside it moved.Why
CLAUDE.mdneededmust all pass→must passallbound to a plural list of commands. Over a single script name it is ungrammatical. This is required by the substitution, not an extra edit — the same class of check the card asked for on the acceptance floor. Both new readings are complete sentences:`pnpm verify` must pass before a change is reported as done.— and the following sentence's "the gates" still refers correctly to whatverifyruns.Every card inherits the same acceptance floor: `pnpm verify` green, gate output pasted in the PR, …— the same shape as the precedent already in the tree atdocs/backlog/14-release.md:14.🔎 The grep — and a surface the card does not name
Four spellings, whole tree, tracked files. Run because the card told me not to trust its list of two, and it was right to.
The composed spelling existed in exactly four places on
14c899f. One is the definition. Three — not two — were restatements:CLAUDE.md:8is a restatement by the card's own criterion — it names the set only to say "this is the bar" and carries zero per-command information; its second sentence ("Metadata mistakes fail silently at runtime; the gates are the only place they surface early") is a rationale for the set, not information about any one gate. It is on no exclusion list in the card, and it is not a demonstration or a historical record.It is also provably drift-capable rather than a statement that has never moved — which is the distinction that keeps
DESIGN.md:354out.git diff 14c899f^1 14c899f -- CLAUDE.mdshows it said three commands until4560611rewrote it. It is one of the surfaces that went stale when CI gained a fourth gate.I moved it, in its own commit (
84d7642) so it can be dropped with a single revert by anyone who reads the card's "no other file" as binding over the card's own criterion. Commit3217a91is complete and self-sufficient without it. My reasoning for moving rather than filing: it is mechanically identical to the other two, it is the most-read file in the repository (auto-loaded into every Claude Code session, so a stale copy there misinforms the widest audience), no open PR or issue holds that file (zero open PRs at the time of writing), and it adds no process cost at all —CLAUDE.mdis governed, and this PR was already routed to the maintainer forAGENTS.md. Filing it instead would produce a second governed-surface card to do one line that is already in front of the maintainer's eyes.Every other hit, and why it stays
README.md47–50 fenced blockAGENTS.md29–34 fenced blocklintis not a second spelling ofvalidate. Its surrounding prose was deliberately rewritten count-free by PR #77 and is untouched hereDESIGN.md:354M1 acceptance cellpackage.json:21.github/workflows/ci.yml58/65/67/75docs/backlog/02-contract-domain.md43, 45 ·03-post-signature-domain.md26 ·11-i18n.md8pnpm validatereports 9 objects", "pnpm lintshows notitle-unresolvable"). Not statements of the setobjectstack.config.ts109/148 ·scripts/check-lint-i18n-gate.mjs· 11 files undersrc/**docs/backlog/14-release.md:14`pnpm verify` green; this is the precedent this PR generalisesDESIGN.md270, 333all three/all four/三条/四条, 30+ hitspnpm-lock.yaml@objectstack/verifyRepo-wide after this PR, the composed spelling survives only at its definition:
The card says: "Five places in the repository restated that list, and all five drifted at once when CI gained a fourth gate — fixed in one pass by #73 / PR #77", and its table names those five as
AGENTS.mdGates cell ·docs/backlog/README.md·README.mdfenced ·AGENTS.mdfenced ·DESIGN.md:354.The count is right; the membership is wrong in two directions.
git diff 14c899f^1 14c899f --statshows PR #77 touched exactly four files —AGENTS.md,CLAUDE.md,README.md,docs/backlog/README.md— and notDESIGN.md:DESIGN.md:354is in the card's table of five but did not drift and was not fixed by PR Four surfaces state the gate set as three commands; CI now enforces four — one PR must move all four, and three of them are governed #77. It still says three commands today, deliberately. It cannot be a member of "the five that drifted at once and were fixed in one pass".CLAUDE.md:8did drift and was fixed by PR Four surfaces state the gate set as three commands; CI now enforces four — one PR must move all four, and three of them are governed #77, and is absent from the card's table entirely. PR Four surfaces state the gate set as three commands; CI now enforces four — one PR must move all four, and three of them are governed #77's own summary table lists it as the second of its five surfaces.So the card swapped
CLAUDE.md:8out forDESIGN.md:354. The consequence is not cosmetic: it meantCLAUDE.md:8was never classified restatement-vs-demonstration by anybody. PR #77's dev enumerated it in their table but silently omitted it from the collapse/keep split in their judgement-call section — which is the recommendation this card adopted as option A. It fell through the seam between the two cards.For the record, the five that actually drifted and were fixed by PR #77 are:
README.mdfenced ·AGENTS.mdfenced block and prose ·AGENTS.md:130Gates cell ·CLAUDE.md:8·docs/backlog/README.md:29.DESIGN.md:354is a sixth, independent statement that never drifted.The judgement call: should either cell carry more than the bare name?
No count — directed, and I agree without reservation: a count is the precise mechanism that put five copies wrong in a single edit.
No pointer either. This was left to me, and my reading is that a pointer is a net loss on all three axes that matter here:
package.jsonadds a second fact that can go stale — trivially aspackage.json:21(a line number that is already wrong the moment a script is added above it), and non-trivially if the repo ever gains a workspace or a different runner. Replacing "which four commands" with "which file" shrinks the drift surface without closing it.pnpm verifycloses it.pnpm runalready resolves it. There is exactly one place a pnpm script can be defined. A reader who wants the expansion typespnpm run, and gets the live value rather than a documented one. A pointer restates a guarantee the tool already makes.AGENTS.mdspecifically, the pointer would aim somewhere worse than what is already on the page. The four gates with their explanations sit in the fenced block at lines 29–34, a hundred lines above the table. Sending a reader topackage.jsonsends them to the terser of the two copies in arm's reach.So: bare
pnpm verifyin both cells, plus the period theAGENTS.mdrow already carried. The one thing a reader loses is seeing the four names at that spot; both files show them elsewhere —AGENTS.mdin its own fenced block, and the backlog's readers are dispatched agents who readAGENTS.md.Gate output — all four exit 0, captured before any pipe
Run on
84d7642in a dedicated worktree. Each gate written to its own log,$?read directly into a variable on the next statement, never through a pipe:One green tail per gate:
The
validateandlintwarnings are pre-existing onmainand are not caused by this diff, which is markdown only. Independent corroboration rather than an assertion: PR #77 reportedlintas21 warning(s), 5 suggestion(s)on4560611andvalidateas✓ Validation passed. Both readings are byte-identical here. The 27validatecarrier-only field notices and the fourapproval-approvers-may-resolve-emptysuggestions concernsrc/objects/andsrc/flows/, neither of which this PR touches.Demonstration: no gate reads any file this PR touches
Asserting it is not evidence, so here is the measurement, matching the bar PR #77 set. All three documents were reduced to garbage at once, all four gates were run against the corrupted tree, and the tree was restored under
trap … EXIT INT TERMusing absolute paths fromgit rev-parse --show-toplevel, then verified by state — never by an exit code.Each mutation was proved to have landed by a marker count on disk (0 → 2 per file) and a byte-count change, because an editing tool's exit code is 0 on a zero-hit substitution. The restore is proved by comparing
git hash-objectagainstgit rev-parse HEAD:PATHfor each file plus an emptygit status --porcelain; an empty hash would have been read as failure, not as "nothing to compare".A second, independent line of evidence agrees with the ablation:
tsconfig.json:14is"include": ["objectstack.config.ts", "src/**/*"], andvalidate/lintwalk the metadata graph fromobjectstack.config.ts. No markdown is in any gate's input set. The ablation is the measurement; the tsconfig line is the explanation.Edits verified on disk by before/after spelling counts
Counted on disk, per file, never by an editor's exit code:
pnpm verifyAGENTS.mdCLAUDE.mddocs/backlog/README.mdREADME.mdDESIGN.mdpackage.jsonThe substitution script asserted each replacement at count exactly 1 and would have aborted with no partial credit on any other number; the table above is the independent on-disk confirmation.
Scope
Documentation only:
AGENTS.md,CLAUDE.md,docs/backlog/README.md. NoREADME.md, noDESIGN.md, nopackage.json, no.github/, nosrc/, noLICENSE, noCHANGELOG.md, nodocs/design/**. What the gates are and the order they run in are unchanged. Zero open PRs at the time of writing, so no file-surface conflict with anything in flight. No changeset (this repo has no changeset gate). No browser run owed — no surface a human touches changed. No control characters introduced (grep -naPover the three changed files, clean).Acceptance notes
14c899fbefore editing, not copied from the card's table.docs/backlog/README.mdprose ≤ 103,CLAUDE.mdprose ≤ 99); the new lines are 94–97 and 57–95 respectively.DESIGN.md:354still spells three gates. Adjudicated on Four surfaces state the gate set as three commands; CI now enforces four — one PR must move all four, and three of them are governed #73, deliberately untouched, and correct as a historical record. Nothing to do. Succeeding party: none — no PR or person should touch this line without aneeds-user-decisioncard..github/workflows/ci.ymlcould in principle become a singlepnpm verifystep. It should not — four separate steps give four separate failure signals in the CI UI, and the workflow is the enforcement rather than a restatement of it. Succeeding party: none.维护者速读(草稿)
改了什么:仓库里「只是指向门槛」的三处文字,从逐条枚举四条 gate 改成直接写
pnpm verify——AGENTS.md交付流程表的 Gates 格、docs/backlog/README.md每张卡继承的验收底线、以及CLAUDE.md里那条内联的 ⛔ 门槛规则。package.json的verify是定义,保持原样;README.md与AGENTS.md的两个代码块继续逐条列出(它们的职责是演示,每条后面的注释才是它们值得读的原因),DESIGN.md354 的 M1 验收格继续写三条(#73 已裁决,属历史记录)。为什么改(是加固,不是缺陷):改之前这些话全部是对的,没有任何人会被误导。买到的只有一件事:指针不可能再和定义脱节。
pnpm verify不会过期,因为它就是那张清单。DESIGN.md:354算作其中一处。实测 PR #77 只碰了四个文件,没有DESIGN.md;真正同时漂移的第五处是CLAUDE.md:8(它在 #77 之前写着三条),而卡片的表格里根本没有它。后果不是笔误:CLAUDE.md:8因此从来没被任何人判定过属于「restatement 还是 demonstration」,它掉进了两张卡之间的缝里。按卡片自己的判据它是 restatement,所以我把它一起改了,单独一个 commit(84d7642),你若不同意,一次 revert 即可去掉,前一个 commit 独立完整。风险与代价:零。纯 Markdown,7 加 8 删。没有任何 gate 读这三个文件——不是断言,是量出来的:三个文件同时毁成两行乱码,四条 gate 依然全部 exit 0,随后在 trap 下按
git hash-object与 HEAD blob 逐个比对还原并验证(见 Demonstration)。validate与lint的告警在main上本就存在,与本 PR 无关(lint的「21 warning(s), 5 suggestion(s)」与 PR #77 在4560611上的读数逐字一致)。回滚就是 revert,无迁移、无数据、无依赖。席位意见:(留给席位填写)
你要做的:① 看一眼三处新写法的语气是否合你意——尤其
CLAUDE.md那句为了语法把「must all pass」改成了「must pass」(all对单个脚本名不成立);② 决定是否保留第二个 commit(CLAUDE.md);③ 合并。本 PR 因含受管面而留给你合并,席位只记 ACCEPT、不合并、不撤草稿。🤖 Generated with Claude Code
https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
Generated by Claude Code