Give dispatch-gates a machine-readable harvest, and a spelling footer - #13534
Merged
os-project-manager merged 1 commit intoAug 30, 2026
Merged
Conversation
…lling footer (#13462) The matched block renders in two spellings, because lint.yml invokes many gates directly rather than through a `pnpm check:*` alias. That idiom is deliberate and correct, and discovery is not lossy across it: measured at 57827b6, all 39 direct-form gate scripts in lint.yml are discovered. The loss is one step downstream, in what the consumer does with the printed block -- a harvest that greps one spelling takes 8 of 12 and says nothing, and every command in the short list passes. - `--commands` / `--json`: stdout carries runnable commands and nothing else, so there is no prose to pattern-match and no spelling to prefer. Matched families union the convention-triggered ones, deduped and sorted. - The human footer prints the spelling split (`12 families - 8 pnpm, 4 direct node`), which is the control while consumers migrate. The split, not the count: a whole-output grep returns a plausible 12 that is the wrong 12, and only the distribution discriminates. - The correct harvest -- until now one agent's private discipline, written down nowhere -- is published in the tool's own output. A blank line above the footer keeps it working; the self-test drives the real awk and sed over a real rendering and pins that. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
os-project-manager
marked this pull request as ready for review
August 30, 2026 17:28
os-project-manager
enabled auto-merge
August 30, 2026 17:28
os-project-manager
deleted the
claude/issue-13462-gate-harvest-machine-readable
branch
August 30, 2026 17:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #13462
Triage ruled the shape at R+50: 形 1 (machine-readable output) as the body, 形 2 (spelling footer) as a mandatory adjunct, 形 3 published only as a transitional measure. All three are here.
.github/workflows/lint.ymlis untouched — its GATE INVOCATION IDIOM comment is a legitimate idiom and not this card's business.The defect, re-derived on current
origin/main(H2)The reproduction holds verbatim at
57827b617, and the four dropped names are unchanged:⭐ Sharper than the card measured: the count is not a safe check either
A consumer greping
pnpm check:over the whole output rather than over the block gets 12 rows — the right count and the wrong twelve. It drops all four direct rows and backfills with three families from the pending-changeset section (which do not apply yet) and one from the unreachable section (which is dead). So a footer printing only12 familieswould have signed that harvest off. The distribution is what discriminates: that harvest is 12 pnpm and 0 direct, and the footer says 8 and 4. This is why 形 2 had to be the split rather than a count.H1 — the half the card did not accuse still holds
lint.yml:72-73claims the direct form loses no discovery. Re-measured at57827b617: all 39 direct-form gate scripts inlint.ymlare discovered by the derivation, 0 missing. The card's attribution boundary stands — the tool is right, the idiom is right, and the loss is at the seam.What landed
--commands— stdout carries one runnable command per line and nothing else.--json— the whole derivation as one document. The banner, the change-set provenance and the new accounting all stay on stderr, so a consumer redirecting stdout gets a file it can execute or parse with no filter in front of it. That is the mechanism that makes the hazard structurally unreachable: there is no prose in the stream to pattern-match, so there is no spelling for a pattern to prefer.What
--commandsdeliberately leaves out, out loudThe pending-changeset families are disclosed (their own key in
--json, a named stderr line in--commands) and kept out of the runnable list, because they are derived against a path that does not exist yet. The always-runs tail is likewise named on stderr. A quiet omission is the defect this mode was added to fix, and adding a new one inside the fix is how that defect reproduces itself one layer up.⛔ The three non-empty controls
1 —
--commandsemits 12, not 8.2 — the OLD harvest is still short on the same input. Kept deliberately: a new mode returning 12 proves nothing on its own if the old one would too.
The defect is real, and it is untouched for a consumer that has not migrated.
--commandsis what bypasses it.3 — the footer does not hardcode "there is always a direct form." On
CLAUDE.md, a genuinely pure-pnpm surface:The
⛔shortfall line correctly does not fire there: with no direct row a one-spelling grep really loses nothing, and a warning that fired anyway would train the reader on a claim the run just measured as false. Both terms still print, at zero. Pinned as a unit case as well as observed live.⭐ The footer could have broken the snippet it publishes — pinned so it cannot
The published harvest ends the block at the first empty line. A footer butted against the rows would be swallowed into it and read as more commands — the remedy breaking the transition it exists to cover.
deriveprints the separator first, and the self-test drives the realawkandsedover a real rendering and asserts every harvested row is still a runnable command. Measured after the change: the snippet still yields 12, byte-identical to--commands.--commandsis strictly better than the snippet, not merely equalThe snippet reads the matched block alone, so it silently drops the convention-triggered block printed beneath it. On
packages/spec/scripts/authorable-defaults.test.ts: snippet 28 rows,--commands32 — the four it drops arecheck:engine-double-contract,check:query-options-erasure,check:type-check-coverage,check:type-check-debt. Pinned end to end.H3 — this settles #13511, and #13511 stands
PR #13500 added one file,
.github/workflows/merged-branch-reaper.yml, and went red oncheck:pm-dispatch-gates. Replicating that exact surface at57827b617:check:pm-dispatch-gatesis not among them;lint.yml:649spells itpnpm check:pm-dispatch-gates— the pnpm form.⇒ A
pnpm check:harvest would have kept that gate had it been derived. It was never in the list to be dropped. #13500's miss was a derivation gap, not a harvest drop — #13511 is correctly attributed and is not this card. The two are not folded.Verification
All at
a1ac525ab, exit codes captured before any pipe.Both convention legs for editing a gate script, quoted from their own verdict lines:
The self-test moved 976 → 1014 (38 new cases).
bare-root-worklist.mjsis the tool's only real importer, which is why that leg is owed.The derived family union for this diff, re-derived on the final commit and run against it — 14 families, all green, plus
check:nul-bytes:check-test-completeness.mjsexit 3 = NOT MEASURED — its own output says so, and it is neither red nor green.Repo-wide ESLint ran in full rather than narrowed:
os-verify-lock: VERDICT command-exit 0 · held the lock 68s.One red, found and fixed, worth recording
check:pnpm-filter-targetswent red on my first union run — from my own self-test fixture. That gate sweeps the whole tree forpnpm --filter TARGETand requires a live workspace package; unlike the watch-hint scanner it does not mask self-tests, so an invented@xin a fixture reddens it from inside this file. The fixture now names a real package and the reason is comments beside it. A fixture is still a literal in the tree.Scope
One file,
scripts/pm/dispatch-gates.mjs— exactly the declared surface, nothing spread. No changeset: the diff isscripts/pm/**only and publishes nothing from any package, soskip-changesetapplies and is applied.Generated by Claude Code
Generated by Claude Code