You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[finding] check-cross-package-test-inputs.mjs still calls sync-template-versions.mjs "named in a comment rather than read", which stopped being true #9651
Found while implementing #9554 (exporting sync-template-versions.mjs's declaration
surface). Not fixed there — out of scope, recording only.
What was observed
scripts/check-cross-package-test-inputs.mjs carries a rationale for the create-objectstack entry that turns on the file being mentioned rather than read:
'create-objectstack': {
// ...
// `sync-template-versions.mjs` is named in a comment rather than read, the
// same shape as `check-nul-bytes.mjs` above and settled the same way: a
// mention forces a declaration, and declaring the file is cheaper than
// rewording prose to dodge the scanner. Here the coupling is real on top of
// being cheap — ...
globs: ['content/**', 'scripts/sync-template-versions.mjs'],
},
As of PR #9648, that is no longer accurate. packages/create-objectstack/src/template-version-stamps.test.ts
genuinely imports scripts/sync-template-versions.mjs and executes it, so the file is now
read, not merely named. The declared glob is unchanged and still correct — it is now
justified by a stronger reason than the one the comment gives.
Severity: low, and deliberately stated as such
The declaration itself is right either way, and the failure this could lead to is
self-correcting: if a future editor removed the glob believing the "only a prose mention"
rationale no longer applied, check:cross-package-test-inputs would go red immediately,
because the test really does read outside its own package. So this is stale prose guarded
by a live mechanism, not an exposure.
Recording it because the comment is load-bearing documentation about why a declaration
exists, and this repo's gates are built on declarations matching reality.
Shape of a fix
Reword that block to say the coupling is now a real cross-package read (the test imports stampedPaths() and the TEXT_STAMPS table) rather than a mention, keeping the check-nul-bytes.mjs comparison for the entry above it, where it still holds.
branch: claude/issue-9651-cross-package-rationale (pushed, empty, from origin/main @ 6f40ed736)
worktree: /home/user/objectstack-9651
Scope: prose fix to the create-objectstack rationale in scripts/check-cross-package-test-inputs.mjs. The globs declaration is not changing. Also running the dispatched measurements: remove-the-glob severity check, a stale-rationale audit of every entry in the file, and a verdict on whether any narrow gate could hold a rationale to its coupling.
{
"issue": 9651,
"status": "done",
"branch": "claude/issue-9651-cross-package-rationale",
"pr": "https://github.com/objectstack-ai/objectstack/pull/9765",
"premise_still_valid": true,
"summary": "H1 (lead): the card's severity claim HOLDS — removing 'scripts/sync-template-versions.mjs' from the create-objectstack globs and running the gate goes RED immediately: \"create-objectstack names path(s) no declared glob covers ... scripts/sync-template-versions.mjs\". 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 what forces the glob today is still a prose mention. Writing only \"it is a real read now\" would have swapped one false belief for a more dangerous one, so the rewrite states three things: the read that exists now (#9648 imports stampedPaths()/TEXT_STAMPS and execFileSync's the script), 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. globs unchanged (ruling 1); the check-nul-bytes.mjs comparison on the @objectstack/cli entry untouched and re-measured as still true (ruling 2). H2: audited all 12 entries. Ten are accurate — every named test exists and still does what the prose says (spot-checked the drift-prone attributions: check-nul-bytes.mjs still mention-only at login-json-noninteractive.e2e.test.ts:143; connector-mcp still relative at serve-capability-identity.test.ts:201; spec's packages/lint/src/** still read at position-delegatable-enforcer.pin.test.ts:57). ONE more clause was stale and is folded in: @objectstack/dogfood's realtime-protocol.mdx block cross-referenced sync-template-versions.mjs as a fellow mention-only declaration. Its realtime-protocol.mdx half is still true (mentions at authz-conformance.test.ts:175 and :260); only the stale clause is removed. 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. H3: NO gate, and the reason is stronger than 'natural-language reading is a false-positive machine'. 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. The cheap structural variant is one level down and real: teach the literal collector the spellings it misses (split-segment join/resolve, ascent-relative literals, and the missing 'skills/' prefix). Filed as #9763, sub-issue of #9747, with the four declarations currently held by prose alone. H4: ADJACENT, not the same class, and the distinction is actionable. #9554 duplicated an ANSWER — both copies machine-readable (the walk, and cut-rc.yml's literal allowlist) — so it has a structural fix: export the declaration, delete the copy, drift becomes impossible. #9651 duplicated a REASON — one side is code, the other is English — and no export collapses those, because prose exists to carry exactly the part that is not machine-checkable. Shared parent: a declaration and reality drifting apart. Useful split: whether the drifting copy is machine-readable. When it is, single-source it and the class closes; when it is not, the only moves are to shrink what the prose must assert (my #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. Sharper framing for your 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. That is why the card's 'low' was right, and why the H2 hit is the instructive part — prose drift scales with how many places restate the reason, so the discipline that helps is 'state a reason once and cross-reference it'.",
"tests": "Local gate union run on the FINAL commit bc26095b8 (all green):\n node scripts/check-cross-package-test-inputs.mjs --verify -> 'OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' exit 0\n node scripts/check-cross-package-test-inputs.mjs --self-test -> 'All 33 self-test cases passed.'\n node scripts/check-nul-bytes.mjs -> 'OK (scanned 6237 text file(s) ... no raw ASCII control bytes).'\n node scripts/check-ratchet-remedy-authority.mjs -> 'OK ... 95 scripts swept ... Control corpus: 18 hand-classified scripts, set-equality audited both ways.'\n node scripts/check-examples-live-imports.mjs -> 'examples/** live-import inventory OK -- 0 invisible (declared), 6 inputs-declared, 67 graph-visible.'\n grep -naP '[control-byte class]' on the changed file -> clean\ndispatch-gates.mjs named only check:cross-package-test-inputs (2 families). I re-derived from the actual diff and ADDED check-ratchet-remedy-authority and check-examples-live-imports, because both gates READ this file (the former classifies it via its own expectation registry keyed 'check-cross-package-test-inputs.mjs' -> expect 'excluded'; the latter mirrors its globs and its globToRegExp). Both green.\nH1 reverse-verification, run from the committed-clean tree at 6f40ed736 and restored with 'git checkout -- .' (never git stash):\n A. glob removed only -> RED, exit 1, names scripts/sync-template-versions.mjs (named in packages/create-objectstack/src/template-consistency.test.ts)\n B. + template-consistency mention unquoted -> RED, exit 1, now names template-version-stamps.test.ts\n C. + BOTH mentions unquoted -> GREEN, exit 0. This is the load-bearing measurement: the real read is invisible to the coverage check.\nDirection observed 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.\nNo ablation in this card (comment-only change, no dist/ involved). No pnpm install was run and none was needed: all five gates are dependency-free by design, and the change is comment-only with no TS/build surface. Prettier flags this file both before and after my change (verified against HEAD via --stdin-filepath, and the 250-320 region is byte-identical to prettier's own output), and no workflow runs prettier — pre-existing, not introduced, not reformatted.\nPR #9765 is DRAFT, labelled skip-changeset via POST (not a set-replacing PUT); read back as ['size/s', 'skip-changeset'] — the labeler bot's size/s survived.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #9763 (sub-issue of #9747): check-cross-package-test-inputs' literal collector cannot see split-segment join/resolve paths, ascent-relative literals, or the 'skills/' prefix, so four declared radii (create-objectstack's sync-template-versions.mjs, spec's packages/lint/src/**, metadata-protocol's check-durability-degradation-log-level.mjs, formula's skills/objectstack-formula/**) are held by prose alone — a FALSE-GREEN, non-self-announcing instance of the #9747 meta-shape, measured live"
]
}
Activity
os-steve commentedon Aug 18, 2026
Claiming this card.
session_01XqDQYVU5smx29ts9pAErjaclaude/issue-9651-cross-package-rationale(pushed, empty, fromorigin/main@6f40ed736)/home/user/objectstack-9651Scope: prose fix to the
create-objectstackrationale inscripts/check-cross-package-test-inputs.mjs. Theglobsdeclaration is not changing. Also running the dispatched measurements: remove-the-glob severity check, a stale-rationale audit of every entry in the file, and a verdict on whether any narrow gate could hold a rationale to its coupling.Generated by Claude Code
Generated by Claude Code
claude commentedon Aug 18, 2026
{ "issue": 9651, "status": "done", "branch": "claude/issue-9651-cross-package-rationale", "pr": "https://github.com/objectstack-ai/objectstack/pull/9765", "premise_still_valid": true, "summary": "H1 (lead): the card's severity claim HOLDS — removing 'scripts/sync-template-versions.mjs' from the create-objectstack globs and running the gate goes RED immediately: \"create-objectstack names path(s) no declared glob covers ... scripts/sync-template-versions.mjs\". 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 what forces the glob today is still a prose mention. Writing only \"it is a real read now\" would have swapped one false belief for a more dangerous one, so the rewrite states three things: the read that exists now (#9648 imports stampedPaths()/TEXT_STAMPS and execFileSync's the script), 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. globs unchanged (ruling 1); the check-nul-bytes.mjs comparison on the @objectstack/cli entry untouched and re-measured as still true (ruling 2). H2: audited all 12 entries. Ten are accurate — every named test exists and still does what the prose says (spot-checked the drift-prone attributions: check-nul-bytes.mjs still mention-only at login-json-noninteractive.e2e.test.ts:143; connector-mcp still relative at serve-capability-identity.test.ts:201; spec's packages/lint/src/** still read at position-delegatable-enforcer.pin.test.ts:57). ONE more clause was stale and is folded in: @objectstack/dogfood's realtime-protocol.mdx block cross-referenced sync-template-versions.mjs as a fellow mention-only declaration. Its realtime-protocol.mdx half is still true (mentions at authz-conformance.test.ts:175 and :260); only the stale clause is removed. 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. H3: NO gate, and the reason is stronger than 'natural-language reading is a false-positive machine'. 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. The cheap structural variant is one level down and real: teach the literal collector the spellings it misses (split-segment join/resolve, ascent-relative literals, and the missing 'skills/' prefix). Filed as #9763, sub-issue of #9747, with the four declarations currently held by prose alone. H4: ADJACENT, not the same class, and the distinction is actionable. #9554 duplicated an ANSWER — both copies machine-readable (the walk, and cut-rc.yml's literal allowlist) — so it has a structural fix: export the declaration, delete the copy, drift becomes impossible. #9651 duplicated a REASON — one side is code, the other is English — and no export collapses those, because prose exists to carry exactly the part that is not machine-checkable. Shared parent: a declaration and reality drifting apart. Useful split: whether the drifting copy is machine-readable. When it is, single-source it and the class closes; when it is not, the only moves are to shrink what the prose must assert (my #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. Sharper framing for your 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. That is why the card's 'low' was right, and why the H2 hit is the instructive part — prose drift scales with how many places restate the reason, so the discipline that helps is 'state a reason once and cross-reference it'.", "tests": "Local gate union run on the FINAL commit bc26095b8 (all green):\n node scripts/check-cross-package-test-inputs.mjs --verify -> 'OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' exit 0\n node scripts/check-cross-package-test-inputs.mjs --self-test -> 'All 33 self-test cases passed.'\n node scripts/check-nul-bytes.mjs -> 'OK (scanned 6237 text file(s) ... no raw ASCII control bytes).'\n node scripts/check-ratchet-remedy-authority.mjs -> 'OK ... 95 scripts swept ... Control corpus: 18 hand-classified scripts, set-equality audited both ways.'\n node scripts/check-examples-live-imports.mjs -> 'examples/** live-import inventory OK -- 0 invisible (declared), 6 inputs-declared, 67 graph-visible.'\n grep -naP '[control-byte class]' on the changed file -> clean\ndispatch-gates.mjs named only check:cross-package-test-inputs (2 families). I re-derived from the actual diff and ADDED check-ratchet-remedy-authority and check-examples-live-imports, because both gates READ this file (the former classifies it via its own expectation registry keyed 'check-cross-package-test-inputs.mjs' -> expect 'excluded'; the latter mirrors its globs and its globToRegExp). Both green.\nH1 reverse-verification, run from the committed-clean tree at 6f40ed736 and restored with 'git checkout -- .' (never git stash):\n A. glob removed only -> RED, exit 1, names scripts/sync-template-versions.mjs (named in packages/create-objectstack/src/template-consistency.test.ts)\n B. + template-consistency mention unquoted -> RED, exit 1, now names template-version-stamps.test.ts\n C. + BOTH mentions unquoted -> GREEN, exit 0. This is the load-bearing measurement: the real read is invisible to the coverage check.\nDirection observed 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.\nNo ablation in this card (comment-only change, no dist/ involved). No pnpm install was run and none was needed: all five gates are dependency-free by design, and the change is comment-only with no TS/build surface. Prettier flags this file both before and after my change (verified against HEAD via --stdin-filepath, and the 250-320 region is byte-identical to prettier's own output), and no workflow runs prettier — pre-existing, not introduced, not reformatted.\nPR #9765 is DRAFT, labelled skip-changeset via POST (not a set-replacing PUT); read back as ['size/s', 'skip-changeset'] — the labeler bot's size/s survived.", "open_questions": [], "out_of_scope_findings": [ "filed as #9763 (sub-issue of #9747): check-cross-package-test-inputs' literal collector cannot see split-segment join/resolve paths, ascent-relative literals, or the 'skills/' prefix, so four declared radii (create-objectstack's sync-template-versions.mjs, spec's packages/lint/src/**, metadata-protocol's check-durability-degradation-log-level.mjs, formula's skills/objectstack-formula/**) are held by prose alone — a FALSE-GREEN, non-self-announcing instance of the #9747 meta-shape, measured live" ] }Generated by Claude Code