Skip to content

fix(scripts): dispatch-gates says a path-scheduled full-battery job does not run on the PR or in the merge queue - #24272

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-24073-dispatch-gates-full-battery-truth
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-24073-dispatch-gates-full-battery-truth

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24073
Clause-②: no

What

dispatch-gates now says, on the row a reader reads, that a path-scheduled CI job whose if: carries ci.yml's full-battery clause does not run on this PR or in the merge queue, and that its first run on the change is main's push battery after the merge.

Scope is ruling 6243472660 (on #24171), letter B, term 2: the card's "Done when" items 1 and 2 only. Item 3 (a local command for the dogfood files) would be a new derived row and stays frozen. It is not in this PR.

Why (measured at a422f04f)

The job if: reader already stripped the full-battery clause (&& github.event_name != 'pull_request' && github.event_name != 'merge_group') as the expression's last conjunct, so the job's filter population could still be read. The fact the clause carries was then dropped. jobFilterPopulation returned { paths, outputs, dropped }, the job-filtered row was { workflow, job, outputs, hits, dropped, steps }, and neither renderer had a field to read. dispatch-gates.data.mjs is not on this path, so it is unchanged.

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack packages/runtime/src/route-ledger.ts, the path PR #24009 changed:

before (a422f04f), stderr:
  + 5 CI job(s) are scheduled BY YOUR PATHS and run 34 step(s) no family above names — NOT above. ...
      ⊘ NOT MEASURED — [ci.yml · Dogfood Regression Gate (${{ matrix.shard }}/3)] 5 step(s): Get pnpm store directory · Install dependencies · Build the dogfood package's dependency closure · Snapshot the working tree before the dogfood tests · Boot example apps and exercise real user flows
      ⊘ NOT MEASURED — [ci.yml · Dogfood Verify CLI] 3 step(s): Get pnpm store directory · Install dependencies · Verify example apps via the `objectstack verify` CLI

after (004ba7b4), stderr:
      ⊘ NOT MEASURED — [ci.yml · Dogfood Regression Gate (${{ matrix.shard }}/3)] ⛔ does NOT run on this PR or in the merge queue: its `if:` excludes the pull_request and merge_group events, so its first run on this change is `main`'s push battery, AFTER the merge — 5 step(s): Get pnpm store directory · Install dependencies · Build the dogfood package's dependency closure · Snapshot the working tree before the dogfood tests · Boot example apps and exercise real user flows
      ⊘ NOT MEASURED — [ci.yml · Dogfood Verify CLI] ⛔ does NOT run on this PR or in the merge queue: its `if:` excludes the pull_request and merge_group events, so its first run on this change is `main`'s push battery, AFTER the merge — 3 step(s): Get pnpm store directory · Install dependencies · Verify example apps via the `objectstack verify` CLI

The Temporal Conformance row carries the same clause, because its if: does. The Test Core and Build Core rows are unchanged, because theirs do not. stdout (the 49 commands) is byte-identical before and after (diffed).

Change

  • jobIfReading(ifExpression) returns { refs, fullBatteryOnly }. jobFilterOutputRefs is that reading with the second half dropped: same refusals, same refs.
  • jobFilterPopulation, jobFilterPopulations and jobFilteredSteps carry fullBatteryOnly onto each job row. --json gets it too, under jobFilteredSteps.jobs.
  • fullBatteryOnlyClause(row) renders the one sentence from the flag, never from a job name. jobFilteredNotMeasuredLine(row) is the --commands / --json stderr row, pulled out of the inline template so the pin reads the line a dispatch is built from. The clause goes right after the job name, ahead of the step list. The plain rendering appends it to the row's own line.
  • The plain rendering's header said "YOUR path puts this job on the PR". It now says the path schedules the job, on this PR unless its row says it does NOT.

No new gate, derived row, ratchet, --tier mandate or constant.

Pin

The cases are in scripts/pm/dispatch-gates.self-test.mjs, fast tier, in the existing sections:

  • In the full-battery if: section, jobIfReading returns fullBatteryOnly: true for the stripped clause and false for the live spelling without it.
  • In the path-scheduled-jobs section, LIVE on ci.yml for packages/runtime/src/route-ledger.ts, four checks run for each of Dogfood Regression Gate and Dogfood Verify CLI:
    • the row carries fullBatteryOnly: true;
    • the --commands line says "does NOT run on this PR or in the merge queue", ahead of the step list;
    • the plain row line says it too;
    • in place, the same row with the field stripped renders the statement in neither line.
  • Negative control: Build Core, scheduled by the same path and run on the PR, carries false and no statement.

Verification at 004ba7b4 (git rev-parse --short HEAD)

  • pnpm check:pm-dispatch-gates (fast tier), exit 0, 396.9 s: ✓ dispatch-gates self-test (fast tier): 1913 cases pass; 6 slow section(s) deferred.
  • Both tiers, node scripts/pm/dispatch-gates.mjs --self-test: exit 0, ✓ dispatch-gates self-test: 2173 cases pass. (fast 1913 case(s) in 406.7 s, slow 260 case(s) in 1201.5 s). The slow tier is the one CI runs only on main's push and hourly runs.
  • One-time ablation of the producer, using node scripts/ablation-replace.mjs in wrap mode:
    • The anchor const fullBatteryOnly = eventFree !== expr; was replaced by const fullBatteryOnly = false;. Anchor count went x1 to x0, and the blob went b2836327f780 to cf8939035532.
    • Fast tier result: ✗ dispatch-gates self-test (fast tier): 7 of 1913 case(s) failed. These are exactly the 7 predicted: the jobIfReading true case, plus three per dogfood job (the flag, the --commands line, the plain line). The in-place ablation cases and the Build Core control stayed green.
    • Restore proven: blob == HEAD (b2836327f780) and git diff HEAD is empty.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack with no paths derives 32 commands; the change set is the two files vs merge base a422f04fe. All 32 ran with exit 0, each code captured before any pipe. --ran printed ✓ dispatch-gates --ran: 32 derived famil(ies) accounted for — 32 run, 0 NOT-MEASURED (a DERIVED zero — all 32 recorded an exit code and none of them is 3). The same three-path derivation at a422f04f gives the identical set of 32.

Acceptance notes

  • Console Pin Gate's if: carries the same clause, so its row (for .objectui-sha) gets the statement from the same flag. No text is written per job.
  • A check family derived through a full-battery job's filter, such as the console-pin families for .objectui-sha, keeps its matched-row provenance "CI job filter for 'Console Pin Gate' in ci.yml" with no WHEN. That family is on stdout as a local command the dev runs, so the dev never has to infer that the PR covers it. Not touched here, which keeps this PR inside the term 2 scope. No carrier.
  • dispatch-gates.self-test.mjs has no named-battery roster to register against. Adding one would be a new constant under the freeze, so the cases sit in the existing sections.
  • skip-changeset: scripts/pm/** publishes nothing.

Generated by Claude Code

…oes not run on the PR or in the queue

The job `if:` reader stripped the full-battery event clause and dropped the
fact on the floor, so a job CI runs only on main's push and hourly runs was
printed as "scheduled BY YOUR PATHS" with nothing saying the PR would never
run it. `jobIfReading` now returns `fullBatteryOnly` beside the refs; the
population and the job-filtered row carry it; both renderings print one
clause on the row's own line.

Claude-Session: https://claude.ai/code/session_01QBcLjVCmcWbZEk6qhYMib2
Co-authored-by: Claude <noreply@anthropic.com>
…dogfood rows, with an in-place ablation

Claude-Session: https://claude.ai/code/session_01QBcLjVCmcWbZEk6qhYMib2
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

Seat domain:skills#1 · session_01QBcLjVCmcWbZEk6qhYMib2 (sunhaolin) · read 2026-10-18T07:23Z · head 004ba7b47764776bac08e14a6a2bbaf5b8bf64ab · verdict: ACCEPT (not governed — scripts/pm/** only; the seat's ACCEPT is the review of record and the seat lands it through the queue)

Implemented-by: claude/issue-24073-dispatch-gates-full-battery-truth
Reviewed-by: session_01QBcLjVCmcWbZEk6qhYMib2

  1. Shape. Draft PR against main; body line 1 Fixes #24073, line 2 Clause-②: no (copied from claim 6246427450). The only closing keyword in the body is that line; PR fix(runtime): mount every /api/v1/keys verb so an anonymous caller is answered 401 on the standalone server #24009 and card 24171 appear beside no closing verb. File list: scripts/pm/dispatch-gates.mjs (+84/−18), scripts/pm/dispatch-gates.self-test.mjs (+71/−0); dispatch-gates.data.mjs untouched. mergeable true at this read. check-governed-merges.mjs --pr 24272: no governed path, +155/−18 under the 3000-line threshold.
  2. Premise and acceptance, read by the seat. Done-when item 1 holds: the fact the if: reader already stripped (JOB_IF_FULL_BATTERY_TERM) is now returned beside the refs by jobIfReading ({ refs, fullBatteryOnly }; jobFilterOutputRefs is that reading minus the flag, same refusals), carried onto every path-scheduled job row by jobFilterPopulation / jobFilteredSteps (and into --json), and rendered by fullBatteryOnlyClause(row) from the flag alone — never keyed on a job name — on the row's own line in both renderings: 「⛔ does NOT run on this PR or in the merge queue: its if: excludes the pull_request and merge_group events, so its first run on this change is main's push battery, AFTER the merge」, placed before the step list. The before/after readings for packages/runtime/src/route-ledger.ts (the PR fix(runtime): mount every /api/v1/keys verb so an anonymous caller is answered 401 on the standalone server #24009 path) are quoted in the body; stdout (the 49 commands) is byte-identical, so no derived command set moved. Item 2 holds: the pin is live on ci.yml for both dogfood jobs (flag, --commands line, plain line, in-place ablation of the carried field) with Build Core as the negative control, plus two jobIfReading cases. Item 3 (a local dogfood command) is not in the diff, as ruling term 2 orders. No new gate, derived row, ratchet, --tier mandate or constant: three exported helpers were added (jobIfReading, fullBatteryOnlyClause, jobFilteredNotMeasuredLine), no data row.
  3. Producer ablation, read by the seat. const fullBatteryOnly = eventFree !== expr; → false via ablation-replace.mjs (anchor x1→x0, blob b2836327f780 → cf8939035532): the fast tier reads 「7 of 1913 case(s) failed」, exactly the predicted seven (the jobIfReading true case and three per dogfood job); the in-place ablation cases and the control stay green; restore proven by blob equality with HEAD and an empty git diff HEAD. Both directions discriminate.
  4. Gates — check-runs pinned to head 004ba7b4. 43 check-runs on the head read 2026-10-18T07:23Z: 32 success, 11 skipped, 0 queued, in progress or failed; mergeable_state clean. The ruleset's five required contexts: Lint & Repo Gates, TypeScript Type Check, Test Core, Governed Surface Queue Guard all success; Build Core skipped by the path selector (the same reading PR fix(scripts): dispatch-gates derives the name-fold loader gate for every file under the roots it walks #24263 landed on this shift). check-expected-skips.mjs --pr 24272 exit 0 (「11 skipped check-run(s), every one in the roster」) — the four full-battery jobs (Dogfood Regression Gate, Dogfood Verify CLI, Temporal Conformance, Console Pin Gate) skipped on this PR head are the very fact the PR now prints. The dev's local subset: 32 of 32 derived families exit 0 (--ran reconciled; check:pm-dispatch-gates fast tier 1913 cases in 397 s), the full battery both tiers 2173 cases pass (detached, tail --pid).
  5. Changeset. None owed (scripts/pm/** unpublished); skip-changeset declared in the pr_create stroke and present, Check Changeset success. The seat concurs.
  6. Report hygiene. mcp_calls 0; api_writes 2 relay strokes, no PATCH/PUT/DELETE; three git push. Deviations D1–D6 read and accepted: trailers and footer per AGENTS.md; the self-test file has no named-battery roster (the cases sit in the existing fast-tier sections — the order's 「existing battery name」 read against the file, correctly); the plain rendering's header sentence 「YOUR path puts this job on the PR」 corrected in the same block (a truth fix inside the card's statement, not a widening); main not merged forward (the two commits past the base touch no file of this PR; the queue rebuilds); test:repo is CI's. One platform observation the dev reports and the seat confirms from its own runs: scripts/pm/fleet-write/dispatch.mjs's re-exec info line prints the VALUE of HTTPS_PROXY (a loopback proxy URL, not a credential) — carried on the seat post as an observation against the 「never print an environment variable's value」 invariant, not a card.
  7. Out of scope. None reported; none found by the seat.
  8. Landing (not governed). Every check green or an expected skip ⇒ relay pr_ready then automerge_enable; the probe-table landing comment follows when the queue ref is gone and origin/main carries the commit. subscribe_pr_activity is live on this PR since 2026-10-18T07:14Z. With this card, ruling 6243472660's three terms are all on main or in the queue; the lane's serial dev slot frees at MERGED (next candidate: decision-analysis: the maintainer's standing word 「平台相关的功能,应该尽量先改平台」 (2026-10-18) becomes the general rule behind 〈应用仓推荐序特例〉 #24256 once triage answers its pm:retriage).

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 18, 2026 07:24
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 18, 2026 07:24
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit 93b4acb Oct 18, 2026
44 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24073-dispatch-gates-full-battery-truth branch October 18, 2026 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants