fix(pm): check-clause2-carriers names the board's SOURCE, answers --help, and refuses an argument it does not honour - #16699
Conversation
…elp, and refuses an argument it does not honour The board was already a parameter — `resolveSweepRepo` (imported from check-half-states.mjs) resolves PM_SWEEP_REPO, else GITHUB_REPOSITORY, else the default, and returns `source` alongside `repo`. The run simply never printed `source`, so a deliberate target and the fallback rendered as the same sentence, and a seat reading only this script concluded the board was hard-scoped. Three changes, one file: 1. Every run opens with a provenance line naming the board AND on what basis it is the board, on stderr in every mode (stdout stays contractually the answer), printed before the first request so a run that dies in transport has still said which board it was about. 2. `--help` / `-h` print usage and exit 0 before any network read. The usage text carries the env precedence, so the one action a seat takes to discover the mechanism no longer hides it behind a sweep. 3. An argument this file does not honour is refused before any read, naming it and printing the usage, exiting 1 — the register's usage code, deliberately not dispatch-gates' 2, which here is the UNJUDGED verdict. A KNOWN_FLAGS table is the single source for both the parse and the usage text: every parse site looks its flag up through `flagIndex`, which throws on a flag absent from the table, so a flag the tool honours and does not document is unreachable rather than merely unlikely. A repo-shaped positional is pointed at PM_SWEEP_REPO — the confusion this change exists to end. No verdict, row, reader or `--pair` semantics changed. 42 self-test cases added (251 pass, was 209). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX
…ause2-carriers-repo-refusal
|
ACCEPT — flight O, PR #16699 at head Own readings (review worktree detached at
Review checklist:
Implemented-by: os-dev subagent Landing: ready → Generated by Claude Code |
Fixes #16623
The card was corrected by its own filer (comments 5573481868 / 5573596217 / 5573604263) and re-graded by the seat (5576224596) before this branch made its first edit. The original shape — port
dispatch-gates.mjs's--repoassertion — is withdrawn, and no--repois added here. This PR implements the re-graded XS shape only.The rule, and what it actually asked for
The board this script reads was already a parameter.
main()resolves it throughresolveSweepRepo, imported by name fromcheck-half-states.mjs, whose precedence isPM_SWEEP_REPO→GITHUB_REPOSITORY→DEFAULT_SWEEP_REPO. Verified here, not relayed — the requested URL is printed inside the transport-failure line, so the two legs are decisive even in a container that cannot read GitHub at all:GET /repos/objectstack-ai/objectstack/pulls?state=open&per_page=100&page=1PM_SWEEP_REPO=objectstack-ai/objectuiGET /repos/objectstack-ai/objectui/pulls?state=open&per_page=100&page=1resolveSweepRepohas always returnedsourcebesiderepo. The run simply never printed it, so a deliberate target and the default fallback rendered as the same sentence — andgrep PM_SWEEP_REPO scripts/pm/check-clause2-carriers.mjsreturns nothing to this day (exit 1), because the capability arrives through the import. That is exactly how the filing seat concluded the capability was absent. The value was already computed; this PR renders it.What changed — three things, one file
1. Every run opens with a provenance line. Naming the board and on what basis it is the board:
It goes to stderr in every mode,
--jsonincluded, for the reason the read-path report next door already states: stdout is contractually the ANSWER, and a provenance line on stdout would travel into the round report that pastes it as though it were part of a finding. It prints before the first request, so a run that dies in transport has still said which board it was about — the state the filing seat was in.… in objectstack-ai/objectstack (source: …)). A separate first line was chosen instead because the headline is printed only by a sweep that reached the network:--pair— the invocation the skill tells seats to run before requesting review — prints no board name at all today, and neither does thePREREQUISITE NOT METpath. A banner covers all four modes and leaves the sweep headline byte-unchanged, so round reports that paste stdout are unaffected. The re-grade marked that spellinge.g.; if the seat wants the suffix instead, it is a one-line move.2.
--help/-hprint usage and exit 0, before any network read. The usage text carries the env precedence, so the one action a seat takes to discover the mechanism no longer hides it behind a sweep. Measured: exit 0, zeroGET /reposattempts.3. An argument this file does not honour is refused before any read, naming it and printing the usage.
The usage message and the exit chosen
Exit 1 (
EXIT_USAGE), and why not 2.dispatch-gates.mjsusesprocess.exit(2)for the same refusal. Here 2 isEXIT_INCOMPLETE— a pinned verdict meaning UNJUDGED. Reusing it would let a seat reading$?read a mistyped invocation as an incomplete sweep, which is the exact confusion the file's exit-code table exists to prevent. So the register's own documented usage code is used, which is also what every pre-existing usage refusal in this file already returns (malformed--pair, unreadable--pair-json, the pair-json repo conflict, a malformed sweep repo). A self-test case pinsEXIT_USAGE === 1 && EXIT_INCOMPLETE === 2so the collision cannot be reintroduced silently.--helpis not an error: it exits 0.A repo-shaped positional is pointed at the mechanism rather than at
--pair, because that is the confusion this change exists to end:The known-flag table
KNOWN_FLAGSis the single source for both the parse and the usage text. Every parse site now looks its flag up throughflagIndex, which throws on a flag absent from the table, so a flag the tool honours and does not document is unreachable rather than merely unlikely. The five converted sites wereargv.includes('--self-test')(twice),argv.indexOf('--pair'),argv.indexOf('--pair-json')andargv.includes('--json').--help-h--self-test--pairN--pair-jsonpath-= stdin)--jsonA value-taking flag consumes the next token only when one is really there: a missing or flag-shaped value is left for that flag's own site to refuse with its own message, so
--pair --jsonstill reaches--pair's existing refusal and--jsonis not swallowed.Self-test delta and the ablation
209 → 251 cases (both measured, not inferred: the base file was run from
git show 0f07b2c86:…and removed). One new battery,the argv contract and the board provenance (#16623), declared floor 42 = its measured case count;SELF_TEST_BATTERY_FLOOR13 → 14.Ablation — neuter
argvRefusalLines(returnnullfirst), from the committed state, with atrap … EXIT INT TERMrestore on absolute paths:a7dfc0e2252c46437d108ddb9134fe0805b9e2f1a7dfc0e2252c46437d108ddb9134fe0805b9e2f1(equal — tree was at HEAD)grep -c= 1; tree hash moved tobac0969c0c07fde275b8c79cf569258889cf01d2✗ check-clause2-carriers self-test: 11 of 251 case(s) failed.git checkout HEAD -- (absolute path); hash back toa7dfc0e2…; marker count 0;git diff HEADemptyDirection observed: turned RED, as predicted. No build/
distpreflight applies — this script is executed from source bynode scripts/pm/…, not resolved through a package'sexports.The five premises of the original dispatch, as measured
c3ead5524;--repo0 hits;process.argv3 hitsc3ead5524(#16604);--repogrep exit 1;process.argvat:2584 :2596 :2598(the card's:2341 :2353 :2355moved with #16604).--help,--this-flag-does-not-existand--issue 7760 --repo objectstack-ai/objectuiall hashed to326175b3a7b77d87.--repomirroringdispatch-gates.mjsPremise 5, measured.
list_pull_requests(open) returned 18 PRs; none names this path. Verified by file surface rather than by title: every open PR's head ref was fetched and diffed against its own merge base — zero of the 18 touchscripts/pm/at all. (The first attempt returnedmerge-baseexit 1 on all 18 because this is a shallow checkout;git fetch --deepen=300was run and the control leg then answered exit 0, so the negative is real and not a shallow artifact.)Merge-tree reading, before opening this PR:
origin/mainhad moved 16 commits ahead anddispatch-gatesprinted a STALE TREE warning naming 14 changed derivation files, soorigin/mainwas merged in (clean) and every reading below was retaken at the merged HEADca3b108b76, where the warning is gone.Sibling readings — readings, never a count
The card listed four
scripts/pm/**entries as same-shape residue and explicitly forbade quoting them as a count. Measured at final HEAD, ⛔ none edited:--repo objectstack-ai/objectui--this-flag-does-not-existcheck-half-states.mjscheck-governed-queue-guard.mjsbare-root-worklist.mjsci-failure.mjscheck-half-statesandci-failureexit 3 because the network is refused here, andcheck-governed-queue-guardexits 1 on a missingGITHUB_EVENT_PATH— in all three the environment answers before argv is ever judged, so these runs say nothing about whether those scripts would refuse a bad flag on a healthy container. Onlybare-root-worklist.mjs— which reads the tree, not the network — is a clean reading of the same swallow: a full report, exit 0, identical for both probes. ⛔ No number of affected scripts is claimed, and no sibling was edited.Gates — derived, never hand-listed
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat final HEADca3b108b76;--repoverdict line:--repo 'objectstack-ai/objectstack' checked against this checkout's 'origin' remote — it holds.Change set: 1 path, three-dot vs merge base779710213. Every command run with its exit captured to a file before any pipe.34 derived, 34 run, 0 unrun — reconciled, not asserted:
All 34 exited 0. The families most directly implicated:
pnpm check:pm-clause2-carriers✓ check-clause2-carriers self-test: 251 cases passpnpm check:pm-dispatch-gates✓ dispatch-gates self-test: 1534 cases pass.(0 failing-case lines)node scripts/check-self-test-wired.mjsnode scripts/check-self-test-wired.mjs --self-testpnpm check:nul-bytesnode scripts/check-scripts-symbol-anchors.mjs(+--self-test)node scripts/check-changeset-no-major.mjs --base origin/mainpnpm check:pm-dispatch-gatescannot finish inside the 600 s foreground cap (~10 min, 1534 cases), so it was started detached with its output redirected and blocked on in the foreground by its own PID (tail --pid=… -f /dev/null); its verdict is quoted from the gate's own printed line, never from$?. Run additionally, outside the derivation, because this file imports it:pnpm check:pm-widening-tells→ exit 0,✓ check-widening-tells self-test: 131 cases pass.Governed-merges reading, quoted:
Lint — a declared narrowing with its three evidence pieces.
pnpm lintiseslint . --no-inline-config, a repo-wide scan CI owns. Narrowed to the one changed file:eslint --print-config scripts/pm/check-clause2-carriers.mjsexits 0 and returns a real rule set (2 rules,sourceType: module), so the file is inside the configured population and is not ignored.--format json—eslint scripts/pm/check-clause2-carriers.mjs --no-inline-config --format json→ exit 0,files linted: 1, errors 0, warnings 0.eslint.config.mjsnever enables type-aware linting: noparserOptions.projectand no typed@typescript-eslintrules anywhere (grep returns onlyecmaVersion/sourceTypeentries; the config states this at L326–329 with its own measured positive control). With no cross-file type program, a one-file diff cannot move the verdict on any file it does not contain.⇒ The narrowing is a measurement, not a skip. The full repo scan remains CI's.
验收备注
--helpon the four sibling scripts.bare-root-worklist.mjsis the clean reading above: exit 0 and a full report for any flag, on a script that reads the tree rather than the network. This PR ⛔ does not touch it — the re-grade scoped the work to one file. 承接者:seat, if it judges the same self-description is owed one script over. ⛔ Not filed as a card: it is one instance of a class the filer has already recorded on ⛔ CORRECTED BY FILER (see comment 5573481868) — the cross-repo capability EXISTS viaPM_SWEEP_REPO; what remains is thatcheck-clause2-carriers.mjsnever names it and--helpruns a sweep instead of saying so #16623 and asked not to be generalised into a count.e.g.spelling, with the reason. 承接者:seat at ACCEPT.dispatch-gatesreporting the checkout ≥20 commits behind) reproduced here at 16 commits and was resolved by mergingorigin/main. It is checkout hygiene, ⛔ not a defect. 承接者:无.--jsonstdout shape is unchanged. The provenance line is stderr, so machine consumers of--jsonsee identical bytes. 承接者:无.Landing
skip-changeset— nothing publishes fromscripts/pm/**. Ungoverned: the seat reviews, flips ready and arms auto-merge. ⛔ This branch does not ready, enqueue, arm, request reviewers or submit a review.Clause-②: noper the claim, so noneeds:contract-review.Generated by Claude Code