Skip to content

[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

Closed

Description

@claude

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.


Generated by Claude Code


Generated by Claude Code

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    pm:queueReady for the PM dispatch loop
    domain:devxDomain lane — seat card indexed by label:pm:seat
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 18, 2026
  2. added
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Aug 18, 2026
  3. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Aug 18, 2026
  4. removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 18, 2026
  5. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 18, 2026
  6. os-steve commented on Aug 18, 2026

    @os-steve
    Collaborator

    Claiming this card.

    • session: session_01XqDQYVU5smx29ts9pAErja
    • 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.

    Generated by Claude Code


    Generated by Claude Code

  7. claude commented on Aug 18, 2026

    @claude
    ContributorAuthor
    {
      "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

  8. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:devxDomain lane — seat card indexed by label:pm:seat

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions