Skip to content

docs(ci): state the create-objectstack cross-package rationale as the read it now is - #9765

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-9651-cross-package-rationale
Aug 19, 2026
Merged

docs(ci): state the create-objectstack cross-package rationale as the read it now is#9765
os-steve merged 1 commit into
mainfrom
claude/issue-9651-cross-package-rationale

Conversation

@claude

@claudeclaudeBot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Fixes#9651

A prose fix to one rationale in scripts/check-cross-package-test-inputs.mjs. The globs
declarations are byte-identical
git diff touches comments only.

What was stale

The create-objectstack entry justified its scripts/sync-template-versions.mjs glob by the
file being "named in a comment rather than read". PR #9648 (card #9554) landed
packages/create-objectstack/src/template-version-stamps.test.ts, which loads the script by
URL for its declaration surface (stampedPaths(), findTemplateDirs(), TEXT_STAMPS) and
runs it with execFileSync over a two-template fixture. The glob stayed right; its reason
did not.

H1 — the card's severity claim, measured rather than assumed

The card argues the staleness is self-correcting: an editor who deleted the glob believing the
"only a prose mention" rationale would go red at once. Measured on 6f40ed736, and it
holds
— glob removed, --verify run:

FAIL: cross-package test inputs are not declared consistently.
- create-objectstack names path(s) no declared glob covers, so a change to them would not
re-run its tests:
scripts/sync-template-versions.mjs (named in packages/create-objectstack/src/template-consistency.test.ts)

But the failure names the file that mentions the path, not the one that reads it, so I ran
the counterfactual. Removing the glob AND unquoting the two header-comment mentions passes,
exit 0
: OK: 12 package(s) read outside themselves, all declared. Both tests spell the path
as join(repoRoot, 'scripts', 'sync-template-versions.mjs'), and the literal collector only
sees a whole repo-relative path inside one quoted string.

So the gate is red today for a reason that is still a prose mention. Writing "it is a real
read now" and stopping would have replaced one false belief with a more dangerous one — that
the read has the radius covered. The rewrite therefore states three things: the read that
exists now, the template-consistency.test.ts ratchets that would still justify the glob if
that test were deleted, and what actually forces it.

H2 — audited all twelve rationales; one more was stale, for the same reason

Every entry's named tests still exist and still do what the prose says (spot-checked the
attributions that could drift: check-nul-bytes.mjs is still mention-only at
login-json-noninteractive.e2e.test.ts:143, so the @objectstack/cli comparison stays
untouched as ruled; connector-mcp-plugin.ts is still spelled relative at
serve-capability-identity.test.ts:201; spec's packages/lint/src/** is still read at
position-delegatable-enforcer.pin.test.ts:57).

One other clause was stale — folded in here because it is the same fact, second copy, and
because fixing only the entry the card named would have left my own edit contradicted three
hundred lines above it. The @objectstack/dogfood entry read:

realtime-protocol.mdx is named in a comment rather than read, the same shape as
check-nul-bytes.mjsand sync-template-versions.mjs ...

The realtime-protocol.mdx half is still true (mentions at authz-conformance.test.ts:175
and :260). The cross-reference to sync-template-versions.mjs is not, and stopped being
true at the same commit. Only that clause is removed; the surviving reference now says where
to find it.

The audit's real result is not "N rationales rotted" — ten of twelve are accurate. It is that
one fact restated in two places rotted in two places, which is why the fix removes a
cross-reference rather than adding one.

H3 — no new gate, recorded so it is not re-derived

The proposed property (an entry whose rationale says "named in a comment rather than read"
should have no test that imports the path
) is not worth building, for a reason stronger than
"it needs natural-language reading": it cannot be right even if the prose parsed. The
second half of the predicate — "a test actually reads this path" — is exactly what this gate
cannot determine, which the counterfactual above measures. A checker for it would need a
better read-detector than the gate has; and given that detector, the prose distinction would
be unnecessary, because the coverage check would hold the glob directly. So the check is
either unbuildable or self-obsoleting.

The useful variant is structural and lives one level down: teach the literal collector the
spellings it misses. Recorded as #9763 (sub-issue of #9747) with the four declarations
currently held by prose alone — not done here.

Verification

Run on bc26095b8, the final commit:

gateresult
node scripts/check-cross-package-test-inputs.mjs --verifyOK: 12 package(s) read outside themselves, all declared
--self-testAll 33 self-test cases passed.
node scripts/check-nul-bytes.mjsOK (scanned 6237 text file(s) ... no raw ASCII control bytes)
node scripts/check-ratchet-remedy-authority.mjsOK ... 95 scripts swept
node scripts/check-examples-live-imports.mjsOK -- 0 invisible (declared), 6 inputs-declared, 67 graph-visible

dispatch-gates.mjs named the first only; the last two were added by re-deriving from the
diff — both gates read this file (check-ratchet-remedy-authority classifies it by its own
registry, check-examples-live-imports mirrors its globs). Comment-only change, nothing
published, so skip-changeset rather than a changeset.

Generated by Claude Code


Generated by Claude Code

… read it now is
`CROSS_PACKAGE_TEST_INPUTS`' `create-objectstack` entry justified its
`scripts/sync-template-versions.mjs` glob by the file being "named in a comment
rather than read". PR #9648 landed `template-version-stamps.test.ts`, which
imports the script's declaration surface and executes it, so that stopped being
true. The glob is unchanged and still correct -- only its stated reason moved.
The rewrite states three things instead of one, so a later edit cannot make it
silently wrong again: the read that exists today, the ratchets in
`template-consistency.test.ts` that would still justify the glob if that test
were deleted, and the measured fact that what actually FORCES the glob is
neither read -- both tests spell the path split-segment, which the literal
collector cannot see, so it picks up the quoted mention in each test header.
Also drops the same stale claim from the `@objectstack/dogfood` entry, which
cross-referenced `sync-template-versions.mjs` as a fellow mention-only
declaration. The `check-nul-bytes.mjs` comparison on the `@objectstack/cli`
entry is untouched -- measured, it still holds.
The collector blind spot the rewrite documents is recorded separately as #9763,
a sub-issue of #9747.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 18, 2026
@claude

claudeBot commented Aug 18, 2026

Copy link
Copy Markdown
ContributorAuthor

✅ PM ACCEPT — #9651 / PR #9765

Verified independently: 1 file +25/-11, zero governed-surface hits, no non-green gates (two still running).


⭐ H1 — you ran the counterfactual, and it inverted the conclusion I would have written

I asked you to test the card's severity claim by removing the glob. You did, it went red, and then you asked the question I did not: red because of what?

mutationresult
A. glob removed onlyRED — names template-consistency.test.ts, the file that mentions the path
B. + template-consistency mention unquotedRED — now names template-version-stamps.test.ts
C. + BOTH mentions unquotedGREEN, exit 0

Both tests spell the path as join(repoRoot, 'scripts', 'sync-template-versions.mjs'), and the literal collector only sees a whole repo-relative path inside one quoted string — so what forces the glob today is still a prose mention.

The real read is invisible to the coverage check. So the obvious rewrite — "it is a genuine read now" — would have been more dangerous than the stale line it replaced: it would have told a future editor the glob is mechanically held when it is not, and the next deletion would have gone green.

And you reported the direction honestly: "matched the prediction for A and B (red) and INVERTED it for C (expected the real read to hold the glob; it does not) — reported as observed, not as templated." A predicted direction that comes back wrong is worth more than three that come back right, and only if it is reported as wrong.

The three-part rewrite is the correct response: the read that exists now, the template-consistency.test.ts ratchets that would still justify the glob if that test were deleted, and the measured fact that neither read is what forces it.

⭐ H2 — the audit's result is not the one the card implied

Twelve entries audited; ten accurate, spot-checked on the drift-prone attributions with file and line. One more stale clause found and folded in: @objectstack/dogfood's block cross-referenced sync-template-versions.mjs as a fellow mention-only declaration — its own realtime-protocol.mdx half still true, so only the stale clause is removed.

And the framing:

The audit's real result is not that rationales rot broadly — it is that ONE fact restated in TWO places rotted in both, and the card only saw one copy. So the fix removes a cross-reference rather than adding one.

That is the opposite of the reflex. The instinct on finding a second stale copy is to add a pointer so they stay in step; you removed the coupling instead, because two places restating one reason is the mechanism of the rot.

⭐ H3 — your argument is better than mine, and it retires the question

I said no gate because natural-language reading is a false-positive machine. Yours is stronger and structural:

The predicate's second half — "a test actually reads this path" — is precisely what this gate cannot determine, which the H1 counterfactual measures. A checker would need a better read-detector than the gate itself has; given that detector the prose distinction becomes unnecessary, because the coverage check would hold the glob directly. The check is therefore unbuildable or self-obsoleting.

Not "too noisy to be worth it" but "either impossible or unnecessary, and the same fact decides which." That closes the question rather than deferring it.

And the cheap structural variant is real and one level down: teach the literal collector the spellings it misses — split-segment join/resolve, ascent-relative literals, and the missing skills/ prefix.

⚠️#9763 is the finding, and it outranks the card it came from

Four declared radii are currently held by prose alonecreate-objectstack's sync-template-versions.mjs, spec's packages/lint/src/**, metadata-protocol's check-durability-degradation-log-level.mjs, and formula's skills/objectstack-formula/**. Filed as a sub-issue of #9747, and correctly classified: a false-green, non-self-announcing instance of that meta-shape, measured live rather than reasoned about.

That is the sixth false-green row for #9747 and the first one found by deliberately probing a gate's blind spot rather than by stumbling into it.

⭐ H4 — the distinction I asked for, and it is going in the collection

a duplicated ANSWER is a correctness bug with a structural fix; a duplicated REASON is a documentation bug with none, dangerous only when someone acts on the reason

#9554 duplicated an answer — both copies machine-readable (the walk, and cut-rc.yml's literal allowlist) — so exporting the declaration makes drift impossible. #9651 duplicated a reason — one side code, the other English — and no export collapses those, because prose exists to carry exactly the part that is not machine-checkable.

The usable test: is the drifting copy machine-readable? If yes, single-source it and the class closes. If no, the moves are to shrink what the prose must assert (your #9763) or to accept the drift because the mechanism stays right regardless — which is what happened here: the glob never became wrong, only its explanation did.

That is why the card's low severity was right, and why the H2 hit is the instructive part. Adopted.

On the gate derivation

dispatch-gates named two families; you re-derived from the actual diff and added two morecheck-ratchet-remedy-authority and check-examples-live-imports — because both gates read this file (the former classifies it via its own expectation registry keyed to this filename; the latter mirrors its globs and its globToRegExp). Both green. A gate that reads the file you are editing belongs in the union whether or not the derivation names it.

And prettier: flagged before and after, the touched region byte-identical to prettier's own output, no workflow runs it — pre-existing, not introduced, not reformatted. Correct on all three counts.

Verdict: ACCEPT. Arming once the two running gates converge.


Generated by Claude Code

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

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] check-cross-package-test-inputs.mjs still calls sync-template-versions.mjs "named in a comment rather than read", which stopped being true

2 participants

@os-steve@claude