ci: give main a baseline lane that can actually finish (#274) - #397
Merged
Conversation
Spec before code, committed alone. Scope is #274's SECOND finding only: give the project a SHA where `main` is known green. The six sanitizer failures are finding #1 / #301 and are deliberately untouched. The mechanism is NOT what #274's third comment concluded. That comment checked the WORKFLOW-level concurrency block (ci.yml:26-28), found it already correct for `push`, and inferred an external canceller -- "a repository/org-level runner concurrency or spending cap ... an automation cancelling older runs" -- and told the next person not to touch the concurrency config. The workflow-level block is indeed correct. The NINE JOB-level groups are the cause: each is keyed on `${{ github.ref }}` with `cancel-in-progress: true`, and every push to main shares the constant `refs/heads/main`, so consecutive pushes cancel each other's expensive jobs. Measured, not inferred. The cancel instant equals the next push's start instant, 3 for 3: run 31485402200's grouped jobs all died at 11:46:33 and run 31488132224 was created at 11:46:32; 31482845117 died at 11:05:12 and 31485054749 was created at 11:05:11. It also explains the PARTIAL cancellation the comment found puzzling: a job-level group kills individual jobs, so finished ones keep their result and the two DIFF-scoped jobs that carry no group at all always survive. Second finding, which changes the design: `sanitize-cpu` is `continue-on-error: true`, so a run's conclusion can be `success` while the sanitizers are red. The ONE completed main run in the last 40 -- 31448896841 at 5812b8b -- reports exactly that. Any baseline reading `run.conclusion` would publish that SHA as green. It is not green. 40 consecutive main runs: 26 cancelled, 12 failure, 1 success, 1 in flight, all `event=push`, spanning 17.43 hours = 55 pushes/day. A full suite is 99 min wall-clock (5944000 ms), 0 billable minutes (public repo). Chosen: schedule (`17 */4 * * *`) + `workflow_dispatch` inside ci.yml, with an event discriminator on the job groups so a baseline cannot be cancelled by a push, and a `baseline-summary` job that reads PER-JOB conclusions from the Actions API. Verdict is DERIVED by `scripts/main-baseline.py`, never committed: a `LAST_GREEN.md` would be the worst instance of the surface AGENTS.md forbids. Rejected: a non-cancellable run per merge. At 55 pushes/day that is ~55 overlapping 99-minute runs, ~190 h/day of job time on a pool where one run already sat queued 37 minutes without starting a single job -- and at that rate the newest completed baseline would be several commits stale anyway, which is the property it was supposed to buy. The first baseline run WILL be red on the six. That is the correct first verdict; a baseline that hides known failures is worthless. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]
…finish (#274) #274's second finding: `main` is not verified by its own CI, so nobody can say which SHA was last green and a red check on a PR reads as someone else's problem. This adds the lane that can answer it. It does NOT touch the six sanitizer failures (finding #1 / #301) -- the first baseline run will be RED on them, and that is the correct first verdict. WHAT ACTUALLY CANCELS THE RUNS. #274's third comment checked the WORKFLOW-level concurrency block, found it already correct for `push`, and concluded an external canceller was at fault -- "a repository/org-level runner concurrency or spending cap ... an automation cancelling older runs" -- and advised the next person not to touch the concurrency config. The workflow-level block IS correct. The NINE JOB-level groups are the cause: each keyed on `${{ github.ref }}` with `cancel-in-progress: true`, and every push to main shares the constant `refs/heads/main`. Measured 3 for 3: the cancel instant equals the next push's start instant. Run 31485402200's grouped jobs all died 11:46:33, run 31488132224 started 11:46:32. Run 31482845117 died 11:05:12, run 31485054749 started 11:05:11. It also explains the PARTIAL cancellation that comment found puzzling: a job-level group kills individual JOBS, so finished ones keep their result and the two DIFF-scoped jobs that carry no group at all always survive. Short-vs-long is the right observation; runtime crossing the next-push interval is the cause. 40 consecutive main runs at 0eb049f: 26 cancelled, 12 failure, 1 success, all `event=push`, spanning 17.43 h = 55 pushes/day against a 99-minute suite. THE OTHER TRAP, which shapes the design. `sanitize-cpu` is `continue-on-error: true`, so the run conclusion can be `success` while it is red. The single completed run in that window -- 31448896841 at 5812b8b -- reports exactly that. Anything reading `run.conclusion` publishes that SHA as known-good. So nothing here reads it; the verdict is per-job, always. .github/workflows/ci.yml `schedule: 17 */4 * * *` + `workflow_dispatch`. Nine job groups and the workflow group gain `${{ github.event_name }}`; without it a push cancels the scheduled baseline exactly as it cancels the previous push. For `push` and `pull_request` the token is a CONSTANT, so their grouping is unchanged -- asserted by test_push_and_pull_request_grouping_is_unchanged. `cancel-in-progress` is now false on the baseline lane, so a second baseline queues rather than killing the first. The two DIFF-scoped jobs are skipped there (a schedule payload has no `github.event.before`, so their range would be empty) and keep carrying no group, per ci.yml's standing instruction. New `baseline-summary` publishes the verdict and FAILS when any covered job is red, sanitizers included. It cannot run on a push or a PR, so it blocks no contributor and `continue-on-error` is untouched for those lanes; it just refuses to call a baseline green while a job it covered is red. scripts/main-baseline.py Derived at read time -- no file written, no file to conflict on. A committed LAST_GREEN.md would be the worst instance of the surface AGENTS.md forbids: one line every merge wants to rewrite. Mirrors scripts/now.py, including REMOTE_UNVERIFIED on an unreachable remote rather than rendering an absence as "nothing failed". Verified against the live API: `--run-id 31448896841` prints RED naming both sanitizer lanes, where the run says success. tests/scripts/test_main_baseline.py 24 tests, all offline. RED-first: a stub reading `run.conclusion` fails test_run_level_success_with_red_sanitizers_is_not_green with "True is not false" and passes nothing else of substance; before the workflow edits, 24 of 24 failed. Also pins the properties a reviewer needs held constant -- push/PR grouping unchanged, baseline job unreachable from a PR, diff-scoped jobs still group-free, sanitize-cpu still continue-on-error. REJECTED: option (a), a non-cancellable run per merge. At 55 pushes/day that is ~55 overlapping 99-minute runs, ~190 h/day of job time, on a pool where run 31485402200 sat queued 37 minutes and was cancelled without one job starting -- it would slow PR feedback for everyone. And at that push rate the newest complete baseline would still be several commits stale, which is the property it was meant to buy. `workflow_dispatch` keeps the useful half. WHAT THIS EVIDENCE DOES NOT ESTABLISH. A CI-config change cannot be proven by the PR's own run: that run is a `pull_request` event, so it exercises neither the `schedule` trigger (GitHub fires cron only for the workflow committed on the default branch) nor main-branch grouping. Everything green here is static. Post-merge observation is owed on four claims: the cron fires; the run survives concurrent pushes; `baseline-summary` reads the Actions API with the default token and `actions: read`; and `scripts/main-baseline.py` parses real baseline runs. `## Outcome` in the spec stays unwritten until then. Gates, all green at this tree: tests/scripts/test_main_baseline.py 24/24; check-agent-record; test_agent_record; test_doc_checkpoint; test_agent_gates; check-test-registration; check-release-workflow; check-container-workflow; check-container-matrix; check-role-discipline; yaml.safe_load of ci.yml; agent-preflight.sh --staged exit 0. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]
…reen (#274) Repair of the fresh review's FAIL on 36fa56d. The concurrency change itself was proved SAFE by that review -- all ten group keys extracted from both revisions, resolved per event, zero partition mismatches -- and is untouched here. THE BLOCKING DEFECT. `agent-record` is in `baseline-summary`'s `needs:`, carried no event guard, and embedded the same diff-scoped `github.event.before` logic that 36fa56d correctly guarded OUT of `documentation-checkpoint` and `commit-protocol-tag`. On a `schedule` payload `PUSH_BASE` renders empty, and under `set -eu`: range=[..36fa56d...] check-commit-trailers.py: error: argument --range: range must be exactly BASE..HEAD STEP_EXIT=2 So the lane would have published RED forever, for a reason with nothing to do with the tree, and the whole row would have certified nothing. Reproduced by replaying the step body before touching it. The two range-scoped calls are now guarded in place rather than the job being skipped: the REST of `agent-record` is tree-scoped and the baseline needs it. `check-role-discipline.py` gets the same guard for a different reason -- it would NOT have aborted, because `--base ""` makes `commits_in_range` fall back to `[head]` and print `OK: every change on main arrived on a task branch` (scripts/check-role-discipline.py:328-333). A vacuous PASS covering one commit while looking like it covered a range is worse than a loud skip. That checker's own behaviour is untouched -- changing it needs its own spec and red-before evidence -- and is recorded as risk 10 in the spec. THE SUITE RAN ON NO MACHINE. `grep -rn test_main_baseline .` returned exactly one hit: the file itself. It was in neither `scripts/agent-preflight.sh`'s `SUITES` array nor the `agent-record` job's explicit list, which is how the 35 sibling suites run, so every claim in the row was guarded by tests nothing executed. Registered in both, and asserted. `check-test-registration.py` cannot see this class -- its `REQUIRED_TESTS` is a fixed map naming one C++ target and `wiring_errors()` only proves the checker guards ITSELF -- so #408 is filed for the class -- 12 of 54 suites, 11 still unwired after this -- rather than changing that checker's semantics without a spec and mutation evidence. FOUR SURVIVING MUTATIONS, AND THE CORRECTION THIS COMMIT OWES. 36fa56d's message says push/PR grouping being unchanged is "asserted by test_push_and_pull_request_grouping_is_unchanged". IT IS NOT. That test compared the key against no baseline at all and blocked only `github.run_id`; adding `${{ github.sha }}` -- which gives every run its own group and disables push and PR dedupe entirely -- walked straight through it. The claim was true; the test did not establish it. It does now, as an EQUALITY against the base revision's key with the constant `-<event>` inserted, for all four events. The other three survivors are the same shape -- substring assertions where a resolved value was needed: * inverting `cancel-in-progress` to `== 'schedule' || == 'workflow_dispatch'` kept every substring the old test looked for while turning PR/push cancellation OFF and baseline self-cancellation ON, on all nine groups. Now resolved to a boolean per event by a small GitHub-expression evaluator that RAISES on any token outside the recognised subset. * `|| true` on the verdict step, and `continue-on-error: true` on the verdict job. Either rebuilds the exact `sanitize-cpu` defect this row is about, one level up: RED computed, RED printed, lane green. VERDICT() HAD NO EXPECTED-JOB SET. It was purely subtractive, so called with eight of nine covered jobs simply ABSENT it printed GREEN with `jobs covered: 1`. A job renamed or deleted by an unrelated PR silently narrows the baseline while it keeps publishing green. `EXPECTED_JOBS` is pinned and cross-checked against `baseline-summary`'s `needs:` list, matrix lanes match by id prefix (`sanitize-cpu (thread)`), a skipped expected job counts as never-ran, and any absence is RED. Two smaller ones: unfinished jobs (`conclusion is None`) were bucketed under `failed:` -- fail-closed is right, the label was a false statement, so they get a `pending` bucket -- and "No completed baseline run found on main" exited 0 while REMOTE_UNVERIFIED exited 1, so `main-baseline.py && echo ok` read absence as success. That is the precise confusion the module docstring says it exists to prevent. Absence is now non-zero. tests/scripts/test_main_baseline.py 24 -> 44 tests, still fully offline. `AgentRecordDiffRangeTests` EXECUTES both step bodies under a `python3` argv shim on all four events and asserts BOTH directions: no range-scoped call where there is no range, and the range-scoped calls STILL happen on `push` and `pull_request`. Deleting the checkers instead of guarding them fails the second half. 13 mutations re-run against a scratch tree, 13 caught, 0 survived -- the reviewer's four, both guard removals, the delete-instead-of- guard overcorrection, both registration removals, both narrowing mutations, the pending relabel and the absence exit code. .agents/specs/main-verifiability.md "the workflow text says what it should" is struck: this row disproves it, and reading a workflow is not running it. The owed-observation list gains the two things only the first scheduled run can settle -- that every job in `needs:` can reach green on a schedule payload, and that the API's job names still match `EXPECTED_JOBS`. Risks 9-11 added. Gates: `pytest tests/scripts/test_main_baseline.py -q` 44 passed / 171 subtests; `yaml.safe_load(ci.yml)` OK; test_agent_gates, check-release-workflow, check-container-workflow, check-container-matrix, check-agent-record, test_agent_record (29), check-test-registration + its 52 mutations all green; `scripts/agent-preflight.sh --staged` "All gates green" with test_main_baseline now among the suites it runs. Issue: #274 Filed: #408 FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]
Brings in #407 (Marlin gencode), #399 (sanitizer defects), #391 (CPU decode barrier) and the SPEC-DSPARK correction. No overlap with this branch: none of them touch .github/workflows/, scripts/main-baseline.py or scripts/agent-preflight.sh. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]
joral
pushed a commit
to joral/vllm.cpp
that referenced
this pull request
Aug 11, 2026
Main advanced again (mudler#274/mudler#397, a main baseline CI lane that can finish) between the gate and the push. Merged forward, never rebased. One keyed record auto-merged and the auto-merge was not kept (AGENTS.md "Records"): `.agents/roadmap_v1.md` was reset to `origin/main` wholesale and the mudler#352/mudler#353 intake rows reapplied ahead of `KV-EXTERNAL-CACHE`, against an anchor asserted to match exactly once. Every other file in the incoming range is new to this branch, so nothing else needed reapplying. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]
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.
Closes the second half of #274:
mainis not verified by its own CI, so nobody can name the last SHA where it was green, and a red check on a PR reads as someone else's problem. Spec:.agents/specs/main-verifiability.md. Issue table row added in.agents/roadmap_v1.md.The six sanitizer failures are NOT touched (that is #274 finding #1 / #301, in flight in parallel). The first baseline run will therefore be RED, on exactly those six. That is the correct first verdict and the reason not to mute them: a baseline that hides known failures certifies nothing.
The stated mechanism was wrong, and that changes the fix
#274's third comment checked the workflow-level concurrency block, found it already correct for
push, concluded the cancellation must be external — "a repository/org-level runner concurrency or spending cap … an automation cancelling older runs" — and advised the next person not to touch the concurrency config.The workflow-level block is indeed correct. The nine job-level groups are the cause. Each is keyed on
${{ github.ref }}withcancel-in-progress: true, and every push tomainshares the constantrefs/heads/main.Measured, 3 for 3 — the cancel instant equals the next push's start instant:
3148540220011:46:333148813222411:46:323148284511711:05:123148505474911:05:113147857596509:39:103147865916209:39:09It also explains the partial cancellation that comment found puzzling: a job-level group kills individual jobs, so finished ones keep their result and the two DIFF-scoped jobs that carry no group at all always survive. Short-vs-long was the right observation; the cause is runtime crossing the next-push interval, not an external canceller.
40 consecutive
mainruns at0eb049f7: 26 cancelled, 12 failure, 1 success, allevent=push, spanning 17.43 h — 55 pushes/day against a 99-minute suite.The trap that shaped the design
sanitize-cpuiscontinue-on-error: true, so a run's conclusion can besuccesswhile it is red. The single completed run in that window —31448896841at5812b8b6— is exactly that: runsuccess, both sanitizer lanesfailure. Anything readingrun.conclusionwould publish that SHA as the known-good baseline. So nothing here reads it; the verdict is per-job, always.What lands
.github/workflows/ci.yml—schedule: 17 */4 * * *+workflow_dispatch. The nine job groups and the workflow group gain${{ github.event_name }}; without it a push cancels the scheduled baseline exactly as it cancels the previous push. Forpushandpull_requestthat token is a constant, so their grouping is unchanged.cancel-in-progressis false on the baseline lane, so a second baseline queues instead of killing the first. The two DIFF-scoped jobs are skipped there (aschedulepayload has nogithub.event.before, so their range would be empty) and still carry no group. Newbaseline-summarypublishes the verdict.scripts/main-baseline.py— answers the question. Derived at read time; writes nothing. A committedLAST_GREEN.mdwould be the worst instance of the surface AGENTS.md forbids — one line every merge wants to rewrite — so this mirrorsscripts/now.py, includingREMOTE_UNVERIFIEDrather than rendering an absence as "nothing failed".tests/scripts/test_main_baseline.py— 44 offline tests. The workflow half evaluates the workflow rather than grepping it: see Review repair below..agents/verification.md— how to read it, next to the other false-green traps.baseline-summaryfails when any covered job is red, sanitizers included. It can never run on apushor apull_request, so it blocks no contributor andcontinue-on-erroris untouched for those lanes. It only refuses to call a baseline green while a job it covered is red.What a reader can answer after this
That is live output against the real API, on the run whose own conclusion says
success.Rejected: option (a), a non-cancellable run per merge
At 55 pushes/day that is ~55 overlapping 99-minute runs, ~190 h/day of job time, on a pool where run
31485402200already sat queued 37 minutes and was cancelled without a single job starting — it would make PR feedback slower for everyone, to answer a question asked a few times a day. It also reverses a deliberate, documented decision (ci.yml:9-14) with no new evidence against it. And at that push rate the newest complete baseline would still be several commits stale, which is the property (a) was supposed to buy.workflow_dispatchkeeps the useful half: pin a baseline on a SHA you care about, right after merging it.Also rejected: a separate workflow calling
ci.ymlviaworkflow_call(github.refis still the caller's, so it needs the identical discriminator, plusworkflow_callsemantics for no gain); duplicating the job definitions (two suites that drift); daily (~55-commit range) and hourly (24 × 99 min, overlapping runs) cadences.Review repair (fresh review returned FAIL on
36fa56d0)The concurrency change itself was proved SAFE by that review — all ten group keys extracted from both revisions, resolved per event, zero partition mismatches — and is untouched. Seven findings are repaired in
d496f006.F1 (HIGH, blocking) — the lane could never have reported GREEN
agent-recordis inbaseline-summary'sneeds:, carried no event guard, and embedded the same diff-scopedgithub.event.beforelogic that36fa56d0correctly guarded out ofdocumentation-checkpointandcommit-protocol-tag. On aschedulepayloadPUSH_BASErenders empty, and underset -eu:Reproduced before touching anything. So the lane would have published RED forever, for a reason with nothing to do with the tree — the whole row would have certified nothing.
The two range-scoped calls are guarded in place rather than the job being skipped: the rest of
agent-recordis tree-scoped and the baseline needs it.check-role-discipline.pygets the same guard for a different reason — it would not have aborted, because--base ""makescommits_in_rangefall back to[head]and printOK: every change on main arrived on a task branch(scripts/check-role-discipline.py:328-333). A vacuous PASS covering one commit while looking like it covered a range is worse than a loud skip. That checker's own behaviour is untouched (a checker-semantics change needs its own spec and red-before evidence) and is recorded as risk 10 in the spec — finding F7, record-only.F2 (HIGH, blocking) — the suite guarding every claim here ran on no machine
grep -rn test_main_baseline .returned exactly one hit: the file itself. It was in neitherscripts/agent-preflight.sh'sSUITESarray nor theagent-recordjob's explicit list, which is how the 35 sibling suites run. Registered in both, and asserted bySuiteRegistrationTests.check-test-registration.pycannot see this class: itsREQUIRED_TESTSis a fixed map naming one C++ target, andwiring_errors()only proves that checker guards itself. Changing its semantics needs a spec, new mutations intest_check_test_registration.pyand a re-pinnedMUTATION_MANIFEST_SHA256, so the class is filed as #408 — 12 of 54 suites, 11 still unwired after this — rather than folded in.F3 (MEDIUM) — four mutations SURVIVED the original suite
All four were substring assertions where a resolved value was needed.
(b) is also a correction this PR owes.
36fa56d0's commit message creditstest_push_and_pull_request_grouping_is_unchangedwith establishing that push/PR grouping is unchanged. It does not. It compared the key against no baseline at all and blocked onlygithub.run_id; adding${{ github.sha }}— which gives every run its own group and disables push/PR dedupe entirely — walked straight through it. The claim was true; the test did not establish it. It does now, as an equality against the base revision's key with the constant-<event>inserted, for all four events, plus a by-name blocklist of run-varying tokens.F4-F6 —
scripts/main-baseline.pyverdict()had no expected-job set, so it was purely subtractive: called with eight of nine covered jobs simply absent it printed GREEN withjobs covered: 1.EXPECTED_JOBSis pinned and cross-checked againstbaseline-summary'sneeds:list, matrix lanes match by id prefix (sanitize-cpu (thread)), a skipped expected job counts as never-ran, and any absence is RED.conclusion is None) were bucketed underfailed:. Fail-closed was right; the label was a false statement. They get apendingbucket.REMOTE_UNVERIFIEDexited 1, somain-baseline.py && echo okread absence as success — the exact confusion the module docstring says it prevents. Absence is now non-zero.Mutation table — 13 re-run, 13 caught, 0 survived
Each applied to a scratch copy of the four files, suite re-run, tree restored.
cancel-in-progresspolarity on all nine job groupstest_cancellation_resolves_to_the_right_boolean_for_every_event${{ github.sha }}to a job group keytest_each_resolved_group_is_the_base_key_plus_the_event_constant,test_no_group_key_carries_a_run_varying_token|| truetobaseline-summary's--emit-summarysteptest_the_verdict_step_never_masks_its_own_exit_statuscontinue-on-error: truetobaseline-summarytest_the_verdict_job_is_not_continue_on_errortest_the_baseline_lane_invokes_no_range_scoped_checker_and_exits_zerotest_push_and_pull_request_still_get_the_full_range_scoped_checksSUITEStest_registered_in_the_preflight_suite_arrayagent-recordCI jobtest_registered_in_the_agent_record_ci_jobverdict()ignores the expected-job settest_an_expected_job_the_payload_never_mentions_is_red, +3EXPECTED_JOBSsilently narrowed to one jobtest_expected_jobs_is_pinned_against_the_workflow_needs_list, +3failingagaintest_an_unfinished_job_is_pending_not_failedtest_no_completed_baseline_run_exits_nonzeroAgentRecordDiffRangeTestsexecutes bothagent-recordstep bodies under apython3argv shim on all four events and asserts both directions — no range-scoped call where there is no range, and the range-scoped calls still happen onpushandpull_request— which is why F1c (deleting rather than guarding) is caught too.Evidence, and what it does NOT establish
RED-first: a stub reading
run.conclusionfailstest_run_level_success_with_red_sanitizers_is_not_greenwithAssertionError: True is not false; before the workflow edits, all workflow-lane assertions failed. Green now:pytest tests/scripts/test_main_baseline.py -q44 passed / 171 subtests,check-agent-record,test_agent_record,test_doc_checkpoint,test_agent_gates,check-test-registration,check-release-workflow,check-container-workflow,check-container-matrix,check-role-discipline,yaml.safe_loadofci.yml, andscripts/agent-preflight.sh --stagedexit 0.A CI-config change cannot be proven by this PR's own CI run. That run is a
pull_requestevent, so it exercises neither thescheduletrigger (GitHub fires cron only for the workflow committed on the default branch) nor main-branch grouping. Everything green above is static. Six claims are owed a post-merge observation, and the spec's## Outcomestays unwritten until they are made:maininstead of being cancelled;baseline-summaryobtains the Actions API result for its own run with the defaultGITHUB_TOKENandactions: read;scripts/main-baseline.pyfinds and parses real baseline runs;baseline-summary'sneeds:can reach a green conclusion on aschedulepayload.agent-recorddemonstrably could not (F1). No static gate here distinguishes "green on the push lane" from "green on the baseline lane", so the first scheduled run is where each of the nine is observed for the first time;missingbucket stays empty against a real payload — the job names the API reports still matchEXPECTED_JOBS.After merge, the fastest way to settle all four is
gh workflow run ci.yml --ref mainrather than waiting for the cron.FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]