Skip to content

docs(pm): stop transcribing the test-file ratchets' hint sets in CHANGE_KIND_GATES, derive them - #13311

Merged
os-elon merged 1 commit into
mainfrom
claude/issue-13232-convention-gates-stale-hints
Aug 30, 2026
Merged

docs(pm): stop transcribing the test-file ratchets' hint sets in CHANGE_KIND_GATES, derive them#13311
os-elon merged 1 commit into
mainfrom
claude/issue-13232-convention-gates-stale-hints

Conversation

@os-elon

@os-elon os-elon commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Fixes #13232

The CHANGE_KIND_GATES docblock in scripts/pm/dispatch-gates.mjs transcribed
the three test-file ratchets' hint sets and drew a conclusion from the copy
("Not one of those can cover a card's path, so all three score silent"). The
copy was stale and the conclusion built on it was false.

Re-derived, not taken from the card

The card names two stale sites. Driving the tool's own extractor over the
live tree, four of the block's five claims are false and one is still true:

claim in the deleted block live derivation verdict
lead-in: sources name "that baseline and, for two of them, the git ref they diff against — never the population" neither source yields a git ref as a hint; one of them does name its population FALSE
check:query-options-erasure BASELINE, origin/main 5 hints: its baseline plus three self-test fixture paths and scripts/eslint-fatal-guard.mjs FALSE
check:where-matcher BASELINE, origin/main 2 hints: its baseline plus its declared *.test.ts population glob FALSE
check:engine-double-contract BASELINE, @objectstack/{core,objectql,metadata-core} set-equal (order differs) TRUE
"Not one of those can cover a card's path, so all three score silent" check:where-matcher is MATCHED for any test file under its scan root FALSE

Live residue on a specimen test file, --residue packages/core/src/__tests__/*.test.ts:
21 matched · 36 undetermined · 126 silent, 25 of the silent ones roster-only —
exactly the 20 -> 21 / 127 -> 126 / 26 -> 25 shift the card predicted.
check:where-matcher prints in the matched list as
matched via ... gate source 'packages/**' + '/*.test.ts'.

What else moved it

The card attributes the staleness to one PR. It is two independent drifts, and
the second one touched neither this file nor that PR's:

  1. check:where-matcher grew a real population literal in its own source (PR fix(tooling): declare check:where-matcher's test-file population where dispatch-gates can read it #13231, the one the card names).
  2. origin/main stopped being admitted as a hint at all. isNonPathNamespace
    refuses the remote-ref class, and this same file's self-test already pinned
    the refusal (nor is the remote-tracking shorthand for one, the extractor drops a scraped git ref) — three thousand lines above a docblock still
    naming it as a measured hint. Both ratchet sources still spell the ref;
    the extractor simply no longer yields it.

The check:query-options-erasure row moved too, in a third direction: its
source grew four more path literals, three of them fixture names that exist
only as strings in its own self-test.

⚠ The checkout available to this seat is shallow (120 commits), so I did not
attribute either drift to a specific merge — git log bottoms out at the graft
boundary and would have produced a confident wrong answer. What is asserted
above is the live state, measured; the archaeology is not.

The claim about this file made by the PR that freed it — verified, held

PR #13247, which released this file, states it does not touch CHANGE_KIND_GATES.
Confirmed against its diff: its two hunks are at coveringJobFilter (~2703) and
inside selfTest() (~6760), neither within the docblock. The sites were still
present and still stale on today's tree.

The change

Remove the transcription instead of re-measuring it. A hand-maintained count in
this file's gate prose has now gone stale twice, so a fresh copy would only
reset the clock.

  • The paragraph now states what is still true of the two ratchets that name only
    an artifact roster, and points at --residue, which prints every family's
    live names: set and re-derives it on every run.
  • check:where-matcher is recorded as the exception, with the reason its line
    stays in the table anyway — the same two-direction argument the neighbouring
    check:cross-package-test-inputs measurement already makes: the declaration
    is set-equal to that gate's own walk under its scan root, so it reaches no
    test file outside it while the KIND reaches every one, and the KIND reaches a
    card dispatched before its code exists, which no path derivation can.
  • ⚠ The convention-table entry is untouched, as the card asks.

The part that cannot go stale the same way

Nine self-test cases, following the precedent set by the PR that freed this
file (PR #13247) — they re-derive against the live tree on every run rather than restating
a number:

✓ all three ratchets are still discovered with hints, so nothing below is vacuous
✓ check:query-options-erasure still names nothing that can cover a card's test file — the surviving half of the old paragraph
✓ …and so does check:engine-double-contract
✓ check:where-matcher IS reached by the ordinary path derivation for a packages test file — the exception the prose states
✓ …and that covering hint is the gate's OWN declared population, not one inherited from a module it imports
✓ but no hint of check:where-matcher reaches a test file outside its scan root, while the KIND does
✓ the tree still holds test files no hint of check:where-matcher reaches (43), so its line is not redundant either
✓ both ratchet sources still spell origin/main (2), so the next case is about the extractor and not a missing literal
✓ …and not one of them yields it as a hint, which is why no row here may name it

The last pair is the one the deleted transcription could not have reported: it
asserts the literal is still spelled and still not yielded, so it reddens if
the refusal is ever relaxed and a row naming the ref becomes writable again.

Ablation — the new cases can actually go red

Committed first, then mutated, with the mutation proven on disk before anything
was read:

HEAD blob: 23550c73749bd4d9ef608090416e6b11800113a6
declared-literal present: 1 -> 0   injected marker present: 0 -> 1
mutated blob: 00f63c20340fcd6af1d5fc3d56e233071eca69d4   (differs from HEAD blob)
extractor now sees: ["scripts/where-matcher-conformance.baseline.json",
                     "scripts/__ABLATED_NO_POPULATION__.json"]

✗ dispatch-gates self-test: 1 of 893 case(s) failed.
555:  ✗ check:where-matcher IS reached by the ordinary path derivation for a packages test file

Restore leg proven the same way rather than by its exit code — git checkout HEAD --
against an absolute path from a trap, then git hash-object back to
23550c73749bd4d9ef608090416e6b11800113a6, marker count back to 0, declared
literal back to 1, git diff HEAD empty.

⚠ One honest limit: under this mutation the following case
("…and that covering hint is the gate's OWN declared population") stays green
vacuously — with no covering hint, the lookup it negates is undefined. It is
a supporting assertion guarded by the case above it, the same chaining the
neighbouring block uses, not an independent claim.

Local verification

All at 95e75cb9f2, the final commit. Gate family derived by
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (14 matched
families, derived from the merge base, not hand-listed):

gate result
check:pm-dispatch-gates ✓ dispatch-gates self-test: 893 cases pass.
check:self-test-wired ✓ every one of the 148 script(s) CI runs that ship a --self-test has that self-test run by CI.
check:watch-hint-literal (+ its own --self-test) ✓ 21 ROOT_DIR_WATCH_HINTS declaration(s), every one an array of quoted literals inside its own statement.
check:nul-bytes OK (scanned 7361 text file(s) ... no raw ASCII control bytes).
check:agent-test-spelling, check:bash32-floor, check:cli-command-ids, check:cross-package-test-inputs, check:entry-guard, check:parse-guard, check:pnpm-filter-targets exit 0
check-ci-filter-parity.mjs OK: all 123 declared cross-package glob(s) (88 unique) are covered ...
check-shard-attestation.mjs ✓ 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
check-test-completeness.mjs NOT MEASURED — grades a saved turbo run test log; the family names it with no argument, and the gate says so itself. Not a red.

This file's own watch-hint set is byte-identical before and after the edit
(9 hints, unchanged) — the new prose fabricates none, which is the failure this
script is written against. Comment ranges are masked by the extractor, verified
directly rather than assumed.

Repo-scoped REST is 403 for this seat, so issue reads went through the public
single-card payload channel; MCP was used for the comment thread, this PR, and
the label.

Declarations

  • Contract accept/reject behaviour: UNCHANGED. No parser, schema or
    validator is touched. The only executable change is nine additional assertions
    inside selfTest(); every derivation function, predicate and table entry is
    byte-identical.
  • Published surface: UNCHANGED. scripts/pm/dispatch-gates.mjs ships in no
    package's files; nothing under skills/ or packages/ is touched.
  • No changeset, and skip-changeset applied: this PR releases nothing.
  • CHANGE_KIND_GATES is the real identifier; there is no CONVENTION_GATES
    symbol anywhere in the tree. The card's name for it is descriptive (the tool
    prints the section as "Convention-triggered gates"). Recorded, not "fixed".

Generated by Claude Code


Generated by Claude Code

… them

The CHANGE_KIND_GATES docblock copied all three test-file ratchets' hint
sets and drew "all three score silent" from the copy. Two independent
drifts falsified it without editing this file: a git ref stopped being
admitted as a hint, and check:where-matcher grew a real population
literal, which moves it into the MATCHED column.

Remove the transcription rather than re-measure it, restate the argument
for the two ratchets it is still true of, and record why the where-matcher
line stays in the table anyway. Every membership claim is re-derived in
--self-test against the live tree, so a tree that moves one reddens a case
instead of leaving the prose quietly false.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
@os-elon os-elon added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/m labels Aug 30, 2026 — with Claude
@os-elon
os-elon marked this pull request as ready for review August 30, 2026 07:56
@os-elon
os-elon enabled auto-merge August 30, 2026 07:57
@os-elon
os-elon added this pull request to the merge queue Aug 30, 2026
Merged via the queue into main with commit d029c31 Aug 30, 2026
39 of 40 checks passed
@os-elon
os-elon deleted the claude/issue-13232-convention-gates-stale-hints branch August 30, 2026 08:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants