Skip to content

Commit cd1f8ee

Browse files
baozhoutaoclaude
andauthored
fix(pm): dispatch-gates' --changed --commands control accepts the no-diff refusal (#15320)
The case asserts that `--changed` combines with a stdout-shape flag — that the combination PARSES and REACHES the derivation. It demanded `status === 0`, which is a property of the tree the run stands in and not of the combination: where HEAD equals origin/main the derivation refuses by design with exit 2 and the "changes nothing against ... nothing to derive" sentence, so the case went red with nothing wrong with the tool. That shape is `main`'s own push run of lint.yml, which has been red at the gate step on every push since the case landed, while the merge_group run of the same head stayed green. The predicate now accepts either outcome, matching the refusal on the constant the print site interpolates rather than on a retyped copy of it, and a positive control puts an ILLEGAL combination (`--changed` with a path — refused by argv before any derivation, at the same exit 2 with a different sentence) through the SAME predicate and requires it to fail, so widening the case cannot empty it out. Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6e67b86 commit cd1f8ee

1 file changed

Lines changed: 55 additions & 3 deletions

File tree

scripts/pm/dispatch-gates.mjs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19066,10 +19066,50 @@ function selfTest() {
1906619066
// CONTROL: --changed really does combine with a stdout-shape flag — the
1906719067
// usage-line fix above would otherwise be cosmetic on a refusal that does
1906819068
// not exist.
19069+
//
19070+
// ⛔ The claim is about the COMBINATION — that it PARSES and REACHES the
19071+
// derivation — so it must not be hostage to whether the tree this run
19072+
// happens to stand in has a diff. The first spelling demanded
19073+
// `status === 0`, which made the verdict a property of the caller's tree:
19074+
// where HEAD equals origin/main the derivation refuses BY DESIGN (exit 2,
19075+
// the NO_DIFF_REFUSAL sentence) and the case went red with nothing wrong
19076+
// with the tool. That is not only the fresh-worktree baseline it was found
19077+
// on. `main`'s own `push` run of lint.yml has exactly that shape — the
19078+
// pushed head IS origin/main — so every push to main was red at this
19079+
// file's gate step from the commit that added this case until this one,
19080+
// while the `merge_group` run of the same head stayed green because a
19081+
// queue branch HAS a diff (#15278).
19082+
//
19083+
// Both outcomes prove the claim, and nothing else does: exit 0 with a
19084+
// command list, or the exit-2 refusal that ONLY the derivation reaches. An
19085+
// illegal combination never gets that far — argv refuses it first, with
19086+
// its own sentence at the same exit code — which is why the predicate
19087+
// matches on the sentence and never on the code alone, and why the
19088+
// POSITIVE CONTROL below puts an illegal run through the SAME predicate
19089+
// and requires it to FAIL. A case widened to accept a second outcome is
19090+
// one reading away from accepting every outcome; that reading is made
19091+
// here, mechanically, instead of being left to the next author's eye.
19092+
const reachedDerivation = (run) =>
19093+
(run.status === 0 && (run.stdout ?? '').length > 0)
19094+
|| (run.status === 2 && (run.stderr ?? '').includes(NO_DIFF_REFUSAL));
1906919095
const changedCommandsRun = runCli(['--changed', '--commands']);
1907019096
t(
19071-
'CONTROL: --changed --commands is legal and answers, so the moved usage line describes a real combination',
19072-
changedCommandsRun.status === 0 && (changedCommandsRun.stdout ?? '').length > 0,
19097+
'CONTROL: --changed --commands parses and REACHES the derivation, so the moved usage line describes a real combination'
19098+
+ ' — exit 0 with commands, or the exit-2 no-diff refusal on a tree that has nothing to derive (#15278)',
19099+
reachedDerivation(changedCommandsRun),
19100+
);
19101+
// POSITIVE CONTROL: the predicate above still REJECTS a combination the
19102+
// argv chain refuses before any derivation runs. `--changed` with a path is
19103+
// that combination — the two input modes answer different questions — and
19104+
// its refusal carries a different sentence at the same exit 2, which is
19105+
// exactly the confusion the predicate has to survive.
19106+
const changedPathRun = runCli(['--changed', '--commands', seamCard]);
19107+
t(
19108+
'…and that same predicate REJECTS the illegal --changed-with-a-path combination, so no parse failure can satisfy the control above',
19109+
!reachedDerivation(changedPathRun)
19110+
&& changedPathRun.status === 2
19111+
&& (changedPathRun.stderr ?? '').includes('--changed derives the paths itself')
19112+
&& !(changedPathRun.stderr ?? '').includes(NO_DIFF_REFUSAL),
1907319113
);
1907419114
}
1907519115

@@ -19563,6 +19603,18 @@ const invokedDirectly = isEntrypoint(import.meta.url);
1956319603
* where `changedPathsFromGit()` refuses, and a pin that cannot be run in the
1956419604
* self-test is not a pin.
1956519605
*/
19606+
/**
19607+
* The invariant half of the refusal `--changed` prints on a tree with NO diff.
19608+
*
19609+
* A CONSTANT for USAGE_LINE's reason and for one more that is this file's own
19610+
* subject. The self-test's `--changed --commands` CONTROL has to tell that
19611+
* refusal — which only the derivation can print — apart from an argv-parse
19612+
* refusal, and BOTH exit 2. Matching a retyped copy of the sentence would pin
19613+
* the case to a memory of the message rather than to the message, so the case
19614+
* and the print site read the same constant (#15278).
19615+
*/
19616+
const NO_DIFF_REFUSAL = 'changes nothing against';
19617+
1956619618
const USAGE_LINE =
1956719619
'usage: node scripts/pm/dispatch-gates.mjs'
1956819620
+ ' [--tier | [--residue] [--commands | --json | --ran <file>]]'
@@ -19763,7 +19815,7 @@ if (invokedDirectly) {
1976319815
// and "no gates" is the most expensive thing this tool could say wrongly
1976419816
// (#4690: an unreadable input must never look like an empty answer).
1976519817
console.error(
19766-
`dispatch-gates: this branch changes nothing against '${derived.base}' (merge base ${derived.mergeBase.slice(0, 9)}) — ` +
19818+
`dispatch-gates: this branch ${NO_DIFF_REFUSAL} '${derived.base}' (merge base ${derived.mergeBase.slice(0, 9)}) — ` +
1976719819
'nothing to derive. On the base branch already, or in the wrong checkout? Pass explicit paths to ask about a hypothetical surface.',
1976819820
);
1976919821
process.exit(2);

0 commit comments

Comments
 (0)