docs(scripts): the published-README baseline's $comment describes its contract instead of a seed count - #9764
Conversation
…tract, not a seed count (#9649) The block claimed the file was "seeded from the five instances #9532 measured". It shipped with 16 entries (PR #9546) and has since shrunk to 0 through #9602, #9615 and #9581, so the sentence was wrong on day one and the gap kept changing meaning as the ledger shrank. Rewritten to state the contract rather than a number: what an entry is (one known instance still awaiting repair, debt not exemption), that the count is read from `entries` and never asserted in prose, that `entries: []` is the success state rather than a corrupt or deletable file, and that absence from the file means measured-and-clean rather than unscanned -- which is what the plugin-audit negative control now says for every package. No baseline entry is added, removed or edited. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
✅ PM ACCEPT — #9649 / PR #9764Verified independently: 1 file ⭐ H1 — answered decisively, and the answer became the fixThe file cannot be deleted, and I confirmed the mechanism myself at if (!existsSync(abs)) {
throw new Error(`${BASELINE_REL} is missing; ${SELF} cannot tell debt from a new defect.`);
}
const parsed = JSON.parse(readFileSync(abs, 'utf8'));
if (!Array.isArray(parsed.entries)) throw new Error(`${BASELINE_REL}: \`entries\` must be an array`);Absence is a hard "cannot verify" refusal — exit 1, never a clean pass. And the only shape constraint anywhere is That is the pair of facts that makes this card's answer non-obvious: the ledger is empty and mandatory. I asked "if it must exist, say why — nobody has written that down," and you put it in the second paragraph. The The rewrite is right on every constraint: contract, not count; the old sentence kept as the worked example of why counts do not belong in prose; empty stated as the success state with an explicit do-not-delete. ⭐ And the negative-control catch is the best thing here
A correct, deliberately-written note that became an instance of the very defect being fixed, because the file around it changed state. Nobody would find that by reading the card. Keeping the control but re-framing it — rather than deleting it or leaving it — preserves the guarantee (absence from the file never means unscanned) while removing the dependence on a populated ledger. That guarantee matters operationally: #9581's dev proved by positive control that plugin-audit is scanned, not skipped. Its silence is measurement. With the ledger now empty, the prose had stopped saying so. H3 — one other hit, and it is the same shape one ledger over#9766: H4 — filed rather than built, and it is #9747's shape exactly#9767: with the ledger empty, the gate's green line always ends "0 of the findings are call sites" — a ratio over an empty set, in the clause whose job is to say "clean, not unmeasured." That is precisely the ambiguity #9747 is about — a gate reporting On the gate not runThe scanning half is build-dependent (reads every workspace package's built type entry, and refuses rather than skips when Naming an unrun gate with its reason, its bound, and proof the tree was contended is the standard I want; it is now the norm across this lane's reports today. Verdict: ACCEPT. Arming once the two running gates converge. Generated by Claude Code |
…READ, not a ratio over an empty set (#9767) With the ledger at `entries: []` -- the success state PR #9764 recorded -- the green line ended `0 of the findings are call sites`: a ratio over an EMPTY SET, printed by the one clause whose whole job is to say "clean" rather than "unmeasured". It is #4690's ambiguity in output rather than in a verdict: "I scanned 60 documents and found nothing" and "I scanned nothing" rendered byte-identically, and the call-site half is the half most likely to quietly stop matching, being a text scan over prose. Each half now states its INPUT VOLUME, which no clean tree can make vacuous: 0 known instance(s) still in scripts/published-readme-exports.baseline.json. Import half: 283 documented symbol(s) checked against the exports their package publishes. Call-site half: 8 documented `X.y(...)` call(s) checked, on 225 import-bound name(s). A zero in "0 documented call(s) checked" is an alarm a reader can act on; a zero in "0 of the findings are call sites" said nothing at all. The ledger clause is kept verbatim -- "N known instance(s) STILL in <file>" carries the shrink-only direction in "still" -- and with a NON-EMPTY ledger it keeps the call-site split the old clause carried, now with the denominator that clause never printed: `2 known instance(s) still in <file> (1 of the 2 at a call site)`. The counts come from an accumulator `analyzeDocument` fills as it works: the same pass, no second scan. What the gate CHECKS, the population it reads, its baseline handling and its exit codes are unchanged; `analyzeDocument` still returns a plain array of findings, so every existing pin holds. Pinned in --self-test next to the remedy pin and for the same reason (the counts are interpolated, so a source scan proves nothing about the message): the rendered body for a scanned tree, the non-empty-ledger split with its denominator, and the regression itself -- a tree that read hundreds of claims and a tree that read nothing must not print the same body. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
…READ, not a ratio over an empty set (objectstack-ai#9767) (objectstack-ai#9815) With the ledger at `entries: []` -- the success state PR objectstack-ai#9764 recorded -- the green line ended `0 of the findings are call sites`: a ratio over an EMPTY SET, printed by the one clause whose whole job is to say "clean" rather than "unmeasured". It is objectstack-ai#4690's ambiguity in output rather than in a verdict: "I scanned 60 documents and found nothing" and "I scanned nothing" rendered byte-identically, and the call-site half is the half most likely to quietly stop matching, being a text scan over prose. Each half now states its INPUT VOLUME, which no clean tree can make vacuous: 0 known instance(s) still in scripts/published-readme-exports.baseline.json. Import half: 283 documented symbol(s) checked against the exports their package publishes. Call-site half: 8 documented `X.y(...)` call(s) checked, on 225 import-bound name(s). A zero in "0 documented call(s) checked" is an alarm a reader can act on; a zero in "0 of the findings are call sites" said nothing at all. The ledger clause is kept verbatim -- "N known instance(s) STILL in <file>" carries the shrink-only direction in "still" -- and with a NON-EMPTY ledger it keeps the call-site split the old clause carried, now with the denominator that clause never printed: `2 known instance(s) still in <file> (1 of the 2 at a call site)`. The counts come from an accumulator `analyzeDocument` fills as it works: the same pass, no second scan. What the gate CHECKS, the population it reads, its baseline handling and its exit codes are unchanged; `analyzeDocument` still returns a plain array of findings, so every existing pin holds. Pinned in --self-test next to the remedy pin and for the same reason (the counts are interpolated, so a source scan proves nothing about the message): the rendered body for a scanned tree, the non-empty-ledger split with its denominator, and the regression itself -- a tree that read hundreds of claims and a tree that read nothing must not print the same body. Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja Co-authored-by: Claude <noreply@anthropic.com>
…tamps instead of vouching it was never edited (objectstack-ai#19298) Fixes objectstack-ai#18892 Clause-②: no The `--pair` green line and the `C2-CORRECTION` note under it both testified that the claim comment 「is NOT edited and still reads as it was written」. Nothing in this file had ever read an edit. `created_at` and `updated_at` both arrive on the `/issues/comments` row `applicableCorrection` already holds, so the reading cost no request — it simply was not taken. Triage's grading (5725446014) chose option **A** over the card's B and C: read the two fields, say it in the message, ⛔ never fail on it. ## What changed — `scripts/pm/check-clause2-carriers.mjs` only - **`claimEditReading(row)`** — module-local, ⛔ no new `export` and no registration (Clause-② declared `no`). It turns the claim comment's own two stamps into **three** readings, never two: - `updated_at` differs from `created_at` ⇒ **WAS EDITED**, naming the instant so a reader can open that comment's edit history; - the two are equal ⇒ a **measured UNEDITED**, naming both stamps it compared; - a row carrying neither stamp ⇒ **NOT READ**, said as a gap — ⛔ never as "unedited", which is the sentence this card was filed against. - **The correction note states that reading** in place of the assertion. **Report-only**, per the ruling: no exit moves, and an edited claim still reads `declared` at the seat's own value. An edit is a legitimate act; the silence was the defect. - **The green line's parenthetical** now describes what the reading above actually states, and names the stream that reading prints on. ## The pin was proven red BEFORE the fix existed Two commits, in that order, exactly as the card required (「⛔ 先让 pin 红」). `0d29892` — the pin alone, against the unfixed message: ``` $ node scripts/pm/check-clause2-carriers.mjs --self-test # exit 1 ✗ ⭐ the EDITED specimen is REPORTED in the note, in as many words ✗ …naming the edit instant beside the creation one, so a reader can open that comment's history ✗ ⭐ an UNEDITED claim reads as a MEASURED unedited, naming both stamps it compared ✗ ⭐ a row carrying NO `updated_at` reads NOT READ — ⛔ never "unedited", which is this defect one room over ✗ ⭐ the three readings are three DIFFERENT sentences — one sentence for all three was the defect ✗ ⭐ the green line points at that reading and says what it STATES, ⛔ never testifying to the edit itself ✗ ⛔ …and the clause that asserted an unread fact is gone from the printed line ✗ ⛔ NEGATIVE, on the SOURCE: no branch of this file asserts the unread 「NOT edited」 any more ✗ ⛔ CONTROL — the same source read is not empty or misdirected: it reaches the reader this card added ✗ check-clause2-carriers self-test: 9 of 1071 case(s) failed. ``` `0128cbb` — the reader: ``` $ node scripts/pm/check-clause2-carriers.mjs --self-test # exit 0 ✓ check-clause2-carriers self-test: 1071 cases pass (…) ``` The unedited control and the three report-only cases pass on **both** commits, so the battery is not a one-sided pin. The source-scan negative and its control are assembled at runtime (the objectstack-ai#16770 idiom), because a literal would have made the scan hit itself — the first draft did exactly that and passed vacuously; it is fixed and the control now proves the read reaches the file. ## Measured on the specimen the card names — offline, via `--pair-json` objectui's claim `5724909959` on card objectstack-ai#9764: `created_at` `2026-09-18T03:54:37Z`, `updated_at` `2026-09-18T04:23:23Z`. Replayed with the real API payloads, thread truncated to the instant the card measured: **Before** (`adf4b18`): ``` ℹ️ C2-CORRECTION — … And it supersedes claim comment 5724909959's own declaration, which is NOT edited and still reads as it was written. ``` **After** (`0128cbb`): ``` ℹ️ C2-CORRECTION — … And it supersedes claim comment 5724909959's own declaration, and⚠️ it WAS EDITED at `2026-09-18T04:23:23Z` (`created_at` `2026-09-18T03:54:37Z`) — REPORTED and ⛔ never a failure: read its edit history before taking the declaration under it for the one the seat first wrote. ``` And the sharper half — two `--pair-json` documents differing **only** in `updated_at`, both reaching exit 0: | | before | after | |---|---|---| | edited claim | exit 0, note says `NOT edited` | exit 0, note says `WAS EDITED at …` | | unedited claim | exit 0, note says `NOT edited` | exit 0, note says `reads UNEDITED — … both …` | Before this PR the two runs were **byte-identical on every stream**. That identity is now impossible, and the exit register did not move in either row. ## Acceptance notes - **The PM's mechanism assumption 3 is REFUTED, and measured so.** The pass path does **not** print three lines with no per-item readings: `renderPair`'s `rows.length === 0` branch prints every note (`ℹ️ C2-CORRECTION — …`) immediately above the green line. So 「the reading above」 resolves today and was ⛔ not dropped. What is true is that the **note goes to stderr while the green line goes to stdout**, so a seat capturing stdout alone holds the pointer without its referent — which is the likeliest reading of the card's 「只打三行」. The parenthetical now names the stream, which is the cheapest thing that makes the pointer executable. No reading was deleted. - **A `--pair-json` document that omits `updated_at` changes reading**, from a silent "unedited" to an explicit NOT READ. That is the intended direction (absence must be loud) and affects hand-written fixture documents only; the live `/issues/comments` rows always carry both fields, unprojected — `readCardComments` hands the raw rows through. - **noted, not filed** — the *plain* branch of the green line (declaration read from the claim comment itself) states nothing about editing, so nothing false lives there and no reading was added to it. A claim comment could still have been edited into carrying its own declaration; the gate says nothing about that in either direction today. Extending the reading to that branch is a capability decision this card did not rule on, and adding it would have meant a new note on every corrected-free pair. Succeeding reader: whoever takes the escalation condition triage recorded on this card (a pair whose claim was edited *into* carrying the declaration ⇒ p1). - **noted, not filed** — the shallow checkout (`git rev-parse --is-shallow-repository` ⇒ `true`, 116 commits; this file reads as `+9132` insertions at the boundary commit) makes `git log -S` unable to answer whether the pass path ever failed to print its notes. Stated rather than answered; nothing in this PR rests on it. - **Net +39 lines** (9,925 → 9,964) against the card's +40 ceiling. One file. No changeset: `scripts/pm/**` publishes nothing, hence `skip-changeset`. - `scripts/pm/**` is not a governed path, so no 维护者速读 section is owed. ## Gates Derived from the worktree after the last commit, no paths passed: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `0128cbb` ⇒ **34 commands**, matching the dispatch-time derivation. Each is run with its exit code captured **before** any pipe; the full table, its `--ran` reconciliation and the head sha it was taken on are in this card's `os-dev-report` comment, which is the machine-read record. --- _Generated by [Claude Code](https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #9649
The baseline's
$commentclaimed the file was "seeded from the five instances #9532measured". It shipped with 16 entries and has since shrunk to 0, so the sentence
was wrong on day one and its gap kept changing meaning as the ledger shrank. This rewrites
the provenance prose to state the file's contract and never a count.
Measured on
origin/main, one row per commit that touched the file:1c6da6eaff01c0ee2d890b38f04cd455c83bNo baseline entry is added, removed or edited. The diff touches only the
$commentarray; the
"entries": []line is unchanged (git diff -U0 | grep entriesshows no+/- on it).
What the new prose says
exemption. How many there are is deliberately not written down, with the reason stated
inline (the old sentence is quoted as the worked example) and the instruction to read
the count from
entries. Provenance survives, countless: seeded by PR ci: a published README's symbol claims are checked against the package's built exports #9546 from whatFive more published service READMEs document a
.configure()entry point and classes that exist nowhere in the repo — the same defect class as #9517, unfixed by it #9532 measured, reconciled down by every repair since. Written so it stays true on thenext shrink, and on a re-grow.
entries: []is what a fully repairedtree looks like, and the file must survive it. See H1.
kept, because it is correct and load-bearing, but re-framed. Its old framing ("the
sixth instance ... is deliberately ABSENT") drew a contrast against a populated ledger;
with
entries: []everything is absent, so the contrast had collapsed into exactly thedefect this card is about. The fact is preserved (repaired by PR docs(plugin-audit): the published README stops documenting an API, a row shape and an action vocabulary that do not exist (#9517) #9531 ahead of the gate,
reported clean by the PR that added the gate) and generalised: absence means measured and
clean, never skipped — which is the single most useful sentence to a reader of an empty
ledger.
H1 — can the file be deleted now that it is empty? No, and that is the reason the block should carry
Read from the loader, not assumed.
scripts/check-published-readme-exports.mjs:649-657:Deleting the file is a hard failure, not a clean pass — the third of the PM's three
options, a "cannot verify" refusal.
run()callsloadBaseline()unconditionally(line 794) on every path that is not the already-fatal unbuilt early return, and the
program wrapper (lines 1218-1224) catches the throw and renders it as
✗ check:published-readme-exports — ...with exit 1. So the empty ledger is a legalterminal state whose file is still required, and #9581's dev is right that no non-empty
assertion exists anywhere: the only shape constraint is
Array.isArray(parsed.entries).That reason was nowhere in the file; it is now the second paragraph of the block.
H2 — is the
$commentload-bearing to any tooling? No — it is prose for humansgrep -rn '\$comment'overscripts/,.github/and the package sources returns threehits and none of them reads this file:
packages/spec/scripts/build-spec-changes.ts:104writes a
$commentinto its own generated artifact,packages/drivers/driver-memory/src/memory-filter-vocabulary-refusal.test.ts:205lists$commentamong Mongo-ish operators the filter vocabulary must refuse, andscripts/check-engine-double-contract.mjs:80mentions a sibling baseline's$commentina source comment. Nothing parses it.
Worth stating because one candidate consumer looks like it should be one and is not:
scripts/check-ratchet-remedy-authority.mjsenforces the ⛔ MAINTAINER-ONLY authoritytoken of #8435, but its sweep glob is
scripts/*.{mjs,mts}and it reads string-literalcontent of gate sources, never JSON ledgers. The token is kept in the block anyway — it
is true, and it is what a maintainer reads before touching an entry — but nothing would
have gone red if the rewrite had dropped it. So the rewrite is written as documentation
for a human, at the length that needs.
H3 — sibling ledger sweep: one more carries the same defect
Every JSON ledger under
scripts/, read for a provenance/state claim and checked againstwhat the file currently holds:
durability-degradation.baseline.jsonentries: []startup-registry-verdict.baseline.jsonentries: []where-matcher-conformance.baseline.jsonfiles: {}engine-double-contract.baseline.jsondurability-read-invention.baseline.jsonreviewed-legitimate(a), not a (b)query-options-erasure-baseline.jsondriver-memory-census.ledger.jsonerror-status-unpinned-baseline.jsonnote, contract only, no counti18n-coverage-baseline.json,role-word-baseline.json,slot-lookup-baseline.json$commentat allSo the pattern is real but small: 1 of 12 siblings carries a drifted claim, and two
already document an empty ledger the way this card asks for — which is why the rewrite
borrows their shape rather than inventing one. Fixed here: only this file.
H4 — what the gate prints when the ledger is empty
The success branch (lines 824-829) prints, verbatim:
Judgement: the first clause reads correctly at zero — "0 known instance(s) still
in ..." carries the shrink-only direction in the word "still", and zero reads as "none
left" rather than "nothing scanned", because the header line immediately above it reports
the population that was actually read. The weak half is the second clause:
memberCheckscounts call sites among all findings, so on a clean tree with an empty ledger it always
prints
0 of the findings are call sites— a statistic about an empty set, which is theone place a reader could hear "the call-site half measured nothing". That is the #4690
ambiguity one layer down, and it is a real but small defect in a line that is otherwise
fine. Not built here: it changes gate output, which needs its own self-test pin, and
this card is scoped to the
$comment. Filed as a finding instead.Verification
Local gates re-run on the final commit,
7ab69e6b4:node -e require(...)on the ledger — parses;entries: 0, 40 comment linespnpm check:nul-bytes—check-nul-bytes: OK (scanned 6237 text file(s) ... no raw ASCII control bytes)(plus its 75-assertion self-test)node scripts/check-published-readme-exports.mjs --self-test— green (extraction, scoping, resolution and both analysis directions pinned)pnpm check:cross-package-test-inputs—All 33 self-test cases passed. OK: 12 package(s) read outside themselves, all declaredDerived with
node scripts/pm/dispatch-gates.mjs scripts/published-readme-exports.baseline.json,which names exactly
check:cross-package-test-inputsandcheck:published-readme-exports.The scanning half of
check:published-readme-exportsis build-dependent (it reads everyworkspace package's built type entry, and refuses rather than skipping when
dist/isabsent) and was not run locally — a full workspace build for a JSON comment change is
not a proportionate local cost, and CI runs it once on a built tree. The change cannot
reach the scan: it alters only
$commentstring content, and the loader's sole contract onthis file (
Array.isArray(parsed.entries)) is verified above.Nothing publishes — a comment in a
scripts/ledger — so no changeset;skip-changesetapplied.
Generated by Claude Code
Generated by Claude Code