Skip to content

docs(pm): describe check-dispatch-gates' watch hints by shape, not by count - #13286

Draft
os-elon wants to merge 1 commit into
mainfrom
claude/issue-13230-check-dispatch-gates-stale-claim
Draft

docs(pm): describe check-dispatch-gates' watch hints by shape, not by count#13286
os-elon wants to merge 1 commit into
mainfrom
claude/issue-13230-check-dispatch-gates-stale-claim

Conversation

@os-elon

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

Copy link
Copy Markdown
Collaborator

Part of #13230 — this PR repairs the sites that live in scripts/pm/check-dispatch-gates.mjs. The sites in scripts/pm/dispatch-gates.mjs are deliberately untouched (that file is in flight as PR #13247, and #13232 / #13251 are held out of this round for it), so #13230 stays open for them.

What was false

The gate's header said its watch hints were "the one constant below: this gate is matched for a card that edits the tool, and for nothing else". The file carries three module-body path constants — TOOL, plus SURFACE_MODULE and FRAME_MODULE, each added by #9116 with its own docblock. A reader deciding whether a card editing scripts/i18n-bundle-surface.mjs needs this gate was told by the authoritative header that it does not, while the tool's own self-test pins that it does.

What I measured (parent commit 74049254d4)

Live, through the tool's own extractor — not recalled:

extractWatchHints(check-dispatch-gates.mjs) ->
  ["scripts/pm/dispatch-gates.mjs",
   "scripts/i18n-bundle-surface.mjs",
   "scripts/check-skill-frame-sync.mjs"]        # 3, not 1

Provenance, so the corrected "and for nothing else" is a measurement rather than a restatement:

coveringKey(entry, 'scripts/pm/check-dispatch-gates.mjs')  -> via 'gate script'  (identity)
coveringKey(entry, 'scripts/pm/dispatch-gates.mjs')        -> via 'gate source'  (hint)
coveringKey(entry, 'scripts/i18n-bundle-surface.mjs')      -> via 'gate source'  (hint)
coveringKey(entry, 'scripts/check-skill-frame-sync.mjs')   -> via 'gate source'  (hint)
coveringKey(entry, 'packages/spec/src/filter.ts')          -> null
extractTriggerPaths(lint.yml)                              -> []
extractCheckInvocations(lint.yml) for check:pm-dispatch-gates -> filter: null

So hints plus gate-file identity really are the whole of what reaches this family: there is no trigger-paths or job-filter provenance to inherit.

The backticking reading in the "Why the paths above are unquoted" section was re-derived too, on the parent commit and again on this commit: rewriting every repo path the header names into backticks yields exactly the hints the file ships with (the module-body constants, nothing out of the header). The old sentence's figure — "1 hint ... the same single hint the file ships with" — was stale in both halves; the invariant it was making survives, so the invariant is what the paragraph now states.

The count found four sites in this file, not the two the card named

line (before) what it said note
85-87 "Its watch hints are the one constant below ... and for nothing else" named by the card
103-105 "1 hint under today's extractor ... the same single hint the file ships with" not named by the card
113-115 "the one quoted path below is still the one input this gate genuinely has" named by the card
127 "and one of this gate's two watch hints" not named by the card — and note it says two: it was corrected once when SURFACE_MODULE landed and went stale again when FRAME_MODULE did

That last row is the argument for the shape of this repair: a hand-maintained count in this header has now gone stale twice.

The repair

Per the triage ruling on the card ("⛔ Do not 'fix' it by changing 'one' to 'three'"), all four sites are now count-free. They name the constants' shape and point at the declarations, where each constant's reason already lives — the enumeration has one home, and a fourth declaration would be read there rather than needing four sentences updated in step. The header also gains one sentence saying that the number is deliberately absent, and why, matching the ⛔ note that already governs the paragraph above it.

Local checks — run on 4ba6b55070, this PR's head

All green unless noted; each verdict is the gate's own printed line, exit codes captured before any pipe.

  • check:pm-dispatch-gates✓ dispatch-gates self-test: 879 cases pass. (exit 0). This is the gate whose file changed, and its self-test is what pins the hint SET live.
  • check:nul-bytescheck-nul-bytes: OK (scanned 7359 text file(s) ... no raw ASCII control bytes)
  • check:watch-hint-literal✓ 21 ROOT_DIR_WATCH_HINTS declaration(s), every one an array of quoted literals inside its own statement.
  • 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: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, check-ci-filter-parity.mjs, check-shard-attestation.mjs — exit 0
  • scripts/pm/bare-root-worklist.mjs --self-test — exit 0 (a convention-triggered obligation for editing a gate script; no new bare-root population is declared here)
  • pnpm lint (eslint . --no-inline-config, whole repo, not narrowed) — exit 0, no output
  • check-test-completeness.mjsNOT MEASURED, by the gate's own text: run with no argument it has no turbo run test log to parse and exits 3, which it documents as "not a red, and there is nothing here to fix".

The family list was re-derived from the real change set with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack rather than taken from the dispatch prompt.

Scope

Comment-only. No contract accept/reject behaviour changes and no published surface changes — the module body, the spawn, and every exit path are byte-identical; the diff is the header docblock and the TOOL docblock. No changeset: this PR releases nothing, hence skip-changeset.

Generated by Claude Code


Generated by Claude Code

… count

The gate's header said its watch hints were "the one constant below: this
gate is matched for a card that edits the tool, and for nothing else". It
carries three module-body constants — TOOL, plus SURFACE_MODULE and
FRAME_MODULE, declared with their own docblocks — so the sentence was a
false negative claim about the derivation, in the file whose subject is
derivation correctness.

Four sites in this file said it. Two of them are the ones the finding
names; the other two are the same claim in different spellings — the
backticking measurement ("the same single hint the file ships with") and
the TOOL docblock, which said "two watch hints" and had itself gone stale
a second time when the third constant landed.

All four are now count-free: they name the constants' shape and point at
the declarations, where each constant's reason already lives. A re-count
would re-rot at the fourth declaration, which is exactly how this survived
the second and third.

Measured on the parent commit: extractWatchHints over this file returns
scripts/pm/dispatch-gates.mjs, scripts/i18n-bundle-surface.mjs and
scripts/check-skill-frame-sync.mjs; the gate's step declares no job filter
and lint.yml no trigger paths, so hints plus gate-file identity are the
whole of what reaches this family.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants