Uh oh!
There was an error while loading. Please reload this page.
feat(pm): reconcile the gate families a dev DERIVED against the ones it RAN - #14306
Merged
Merged
Conversation
…it RAN Add a --ran <file> verdict mode to scripts/pm/dispatch-gates.mjs. It takes a run record whose lines are the exact strings --commands emits and reports an exact set difference against this tree's derivation, recomputed in the same process. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
baozhoutao
marked this pull request as ready for review
September 2, 2026 02:08
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Sep 2, 2026
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 freeto 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#13774
Adds a
--ran FILEverdict mode toscripts/pm/dispatch-gates.mjs: it takes a record of what was run and names the exact set difference against this tree's derivation. One file changed,scripts/pm/dispatch-gates.mjs, plus its self-test.Follows the triage ruling on that card: 「⭐ 分诊裁定:走生产侧(选项 2),⛔ 不以消费侧纪律收场」 — the normalization is owned once, in a place that can be tested.
Premise re-derivation — what already existed, and what was still missing
origin/main(a59f78df3) before anything was designed.familyReconciliation+familyReconciliationLines— a TOTAL, printed on every human run, with the arithmetic tying it to both sectionsciOnlyMeasurement/ciOnlyCommandSet— CI-measured-only families classified and subtracted from--commands, with the omission stated where it happens⇒ The card's gap is open. Everything that landed is print-side: it makes a SHORT HARVEST detectable by stating a number the reader can assert against. Nothing in the file accepts a record of what was executed, and no code path compares two lists. Verified on today's tree: no
--ran, no run-record reader, no set difference anywhere;familyReconciliation's inputs arematchedRowsandkindGroupsonly.That is the card's own discriminating test, unchanged by the drift: «a better list does not make anyone run it». The count line answers did you harvest the whole list; this answers did you run it, and one does not imply the other.
premise_still_valid: true.The run-record format, and why it makes the escape clause moot
The ruling attached a condition: 「若实现时发现日志命名无法被工具端可靠归一(例如各 dev 的日志名形状不受控),回报而不是自行退回消费侧」.
The answer is not a better normalizer — it is that there is nothing to normalize. The record's lines are the exact strings
--commandsemits, copied by the runner as it goes:Both sides of the comparison are therefore produced by one expression in one file and are identical in shape by construction. ⛔ The record is never built from log FILE NAMES — that is the step that needs a slug, and the slug is where the first hand-built reconciliation went wrong.
Everything the parser does to a line is line-ending decoding, and it is stated as such: one trailing
\ris dropped because a CRLF file terminates its lines with two bytes; blank, whitespace-only and#lines carry no command; nothing else is touched. Leading and trailing spaces inside a line are content, and trimming them would be a normalization applied to one side of the comparison only — the fuzzy shape wearing a smaller hat.The three measured failure mechanisms, and what defeats each
--ranexits 1 while any derived family is unaccounted for. A report can paraphrase prose; it cannot paraphrase a nonzero exitunreconciled 0where the rawcommreported 36 (bashtr -cleft a trailing underscore the Python slug did not reproduce)Set.hason the untouched string. Prefix, substring, truncation and whitespace relations all pair with nothing — pinned by cases. A whitespace-only difference is reported and the family stays UNRUN: the diagnostic can never move a verdict, which is the whole lesson of that incident57 + 1 = 58balanced perfectly because the missing family had left the numerator and the denominator in the same operationcommandsFor's union, recomputed in THIS process from THIS tree — the same expression--commandsprints — and the record can only ever subtract from a total it did not produce. Pinned at the limit: an empty record over a non-empty derivation reports every family unrun, never a balanced nothingThe classes, and why the remainder is the tool's job
#13711's row on this card is the warning: a remainder of 18 was accepted because the dev named every entry in prose, and a reconciliation whose output is routinely non-empty trains readers to wave it through. Its 18 were 16 CI-owned job steps and 2 spelling duplicates. Neither can appear here, and neither is classified away by a rule:
derivediscommandsFor's union, which never contained the always-runs tail (that is a listing about the REPO, printed under its own heading) and which deduplicates the two spellings of one family into one command before the reconciliation sees them.What the tool still meets in a record it classifies itself, byte-exactly, against sets the same derivation produced: CI-measured-only families (
ciOnlyCommandSet, subtracted from--commandsby design) and pending-changeset families. Neither owes the runner an explanation.What is left is only what the tool genuinely cannot know — a gate that refused with its own prerequisite:
NOT-MEASUREDis its own class, reported apart fromranbecause this tool did not measure it and must not imply it did. It costs a stated reason (NOT-MEASURED <command> :: <reason>); a marked line without one leaves its family UNRUN. That is comment5477620344's lesson made mechanical: the category exists for a gate that refuses with its own prerequisite, and it is exactly where a family you merely did not FINISH running goes to hide — a cap kill (exit 143) leaves no verdict at all, and the rendering says so beside the class.Live demonstration — derived on this PR's own diff, run, fed back
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandson this branch derived 15 families; all 15 were run under the shared verify lock, pluspnpm check:ratchet-remedy-authorityfrom the dispatch order. The record was captured by construction, oneprintf '%s\n' "$cmd"per completed command.1 — the record as captured (every command recorded as run):
2 — the honest record.
node scripts/check-test-completeness.mjsexited 3 on this tree withPREREQUISITE NOT MET(it grades a savedturbo run testlog and none was named; its own message says to record it as NOT MEASURED). Recorded as the refusal it is:3 — one line removed from that same record:
All three classes on one real card, from one real run.
Self-tests
53 new cases (51 assertions, one of them a three-input loop), inside the tool's own
--self-test. Whole suite:✓ dispatch-gates self-test: 1232 cases pass, 0 failures.They cover each of the three mechanisms above, both malformed-record shapes, the class partition asserted over 0/1/5-family derivations, the closure check that refuses rather than printing an untrustworthy verdict (#4690), the argv split (a two-token flag's value must not fall through into the path list — the hazard
--repo's own docblock predicted, which came true the moment a second value-taking flag existed), and the message that would otherwise LIE about which argument is wrong.⭐ The end-to-end half is the one that matters: it runs the real CLI, takes
--commandsoutput on a real card, writes it to a file unmodified, and reconciles against it — green, exit 0 — then removes one line and asserts exit 1 naming exactly that command. That measures the by-construction property the whole design rests on; every unit case above it stays green if--ranis never wired into the CLI.check:pm-dispatch-gatesmeasured 759s on this branch under the shared verify lock (shared-box seconds — the lock excludes other locked runs, not unlocked sibling work). The three CLI derivations in the new end-to-end block are ~90s of that. #14281 already owns that gate's runtime and its cap-kill behaviour; this PR does not make that problem, it adds to it, and the end-to-end cases are the ones that cannot be replaced by fixtures.Gates run — 16 of 16, at
51801a456Derived families re-derived from the merge base after the final commit (
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), then reconciled with the mode this PR adds — see the demonstration above,unrun 0.node scripts/check-ci-filter-parity.mjsnode scripts/check-cross-package-test-inputs.mjsnode scripts/check-self-test-wired.mjsnode scripts/check-shard-attestation.mjsnode scripts/check-test-completeness.mjs— NOT MEASURED by the gate's own declaration, not a redpnpm check:agent-test-spellingpnpm check:bash32-floorpnpm check:cli-command-idspnpm check:cross-package-test-inputspnpm check:declared-population-livepnpm check:entry-guardpnpm check:parse-guardpnpm check:pm-dispatch-gates(759s)pnpm check:pnpm-filter-targetspnpm check:watch-hint-literalpnpm check:ratchet-remedy-authority(named by the dispatch order; outside the derived union)Every exit code captured with
cmd > log 2>&1; status=$?— redirected before capture, never read after a pipe.Changeset
skip-changeset: the diff is one file underscripts/pm/**and publishes nothing from any package.⛔ Not done here
os-devcontract. Wiring--raninto the dispatch order is the PM's call and is not in this PR.--jsonshape for the verdict.--ranrenders a verdict and--commands/--jsonrender the derivation; combining them is refused rather than blended, for the reason the file already refuses--commands --json. A machine-readable reconciliation would be a new stdout shape and is not what the card asked for.--tier --commandssilently prefers--tier— the one two-stdout-shape blend the CLI does not refuse #14294 —--tier --commandssilently prefers--tier, the one two-stdout-shape blend the CLI does not refuse. dispatch-gates:--tier --commandssilently prefers--tier— the one two-stdout-shape blend the CLI does not refuse #14294 remains open.Dispatched session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Generated by Claude Code
Generated by Claude Code