Repository navigation
A cancelled delivery run with zero jobs is a superseded queue entry — measured, documented, guarded - #3718
Merged
Conversation
…and the lane's shape is guarded Measured 2026-09-08 09:00–10:09Z on main-cd: seven runs (3ee8dda ×3, 4e1a1b6 ×2, 1c61ed3, 6b2fe2a) reported `cancelled` with run_attempt=1 and jobs=0, each one 1–2 s after the next CD arrival was created, while the two in-flight runs (765fb7f, 6044910) ran to their seal untouched. That is GitHub's one-pending-slot replacement inside our own `main-cd-<ref>` group — the designed supersede for the push lane — not an account-budget kill (jobs never started; Plugins runs in the same minutes executed) and not a hand (seven cancellations 1–2 s after seven arrivals). It held a roll for a morning because the workflow's own comment claimed waiting runs "QUEUE — the second waits for the first". - main-cd.yml: the concurrency comment now says what "queued" means in this lane, the reading (jobs=0, cancellation instant = next arrival's creation), and why the push lane keeps ONE group on the ref while Build and Test gives every main commit its own. - ReadingCiSignals: new section with the measured table, the three causes a cancelled run can have and how the record alone tells them apart, and what to do (nothing — read the OTHER conclusion in the list, the failure on the run that executed). - ModuleBuildArchitecture → "Superseded runs on main": jobs=0 alone is not the #888 starvation signature; the in-flight run beside it is. - MainRunsAreNeverCancelledGuard: Delivery_NeverKillsTheRunInFlight_AndKeepsTheReconcileInItsOwnLane holds main-cd's three halves (cancel-in-progress false for every event; the reconcile in its own group, #2490; deliveries sharing one group on the ref), with a Theory control arm that fires on each plausible tidy-up and stays silent on the shipped pair. - What's New entry. No workflow behaviour changes: the concurrency block is byte-identical; only its comment moved. Refs #2490, #3077, #2412. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Minor but concrete documentation inaccuracies in the new “reading recipe” (REST endpoint/field naming) should be corrected to avoid misleading operators.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR documents and guards the intended GitHub Actions concurrency behavior where main-cd runs that are cancelled with zero jobs are superseded pending entries (not killed in-flight work), and adds tests to prevent future “tidy-ups” from breaking the delivery/reconcile lane invariants.
Changes:
- Adds a guard test that evaluates
main-cd.yml’sconcurrencyexpressions and a control-arm matrix that must fail on plausible mutations. - Documents the observed “zero-jobs cancelled” signature and how to distinguish it from real cancellations (budget/manual) in CI reading docs.
- Updates
main-cd.ymlcommentary and adds a What’s New entry to make the behavior discoverable and durable.
File summaries
| File | Description |
|---|---|
| test/MeshWeaver.Documentation.Test/MainRunsAreNeverCancelledGuard.cs | Adds delivery-lane concurrency guard + control-arm mutations to ensure the invariant is enforced by evaluation, not brittle matching. |
| src/MeshWeaver.Documentation/Data/WhatsNew/2026-09-08-a-cancelled-delivery-run-with-no-jobs-is-a-superseded-queue-entry.md | Adds a What’s New entry describing the “cancelled with zero jobs” interpretation and where it’s documented/guarded. |
| src/MeshWeaver.Documentation/Data/Architecture/ReadingCiSignals.md | Adds a new section documenting the measured pattern and operational reading recipe for zero-job cancellations. |
| src/MeshWeaver.Documentation/Data/Architecture/ModuleBuildArchitecture.md | Clarifies that jobs=0 alone is not a starvation signature for core delivery, and points to the detailed CI reading guidance. |
| .github/workflows/main-cd.yml | Updates comments above concurrency: to describe GitHub’s one-pending-slot replacement semantics and why the lane is intentionally ref-grouped. |
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| # 🚨 WHAT "QUEUED" MEANS HERE — the run list will read as a defect, and it is not one. GitHub keeps | ||
| # exactly ONE PENDING run per group, and every further arrival REPLACES it: the run that was | ||
| # waiting is CANCELLED the second a newer one is created, and it reports `cancelled` having run | ||
| # ZERO jobs (`run_attempt: 1`, `jobs.total_count: 0`, `updated_at` = the next arrival's |
Comment on lines
+1081
to
+1082
| **Measured 2026-09-08, every `main-cd` run created between 09:00Z and 10:09Z** (REST: | ||
| `actions/runs/<id>` for the timestamps and attempt, `…/runs/<id>/jobs` → `total_count`): |
Contributor
Contributor
Contributor
Contributor
Contributor
Contributor
Contributor
rbuergi
added this pull request to the merge queue
Sep 8, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Sep 8, 2026
…nd the instrument rule Two additions to the zero-job-cancellation section, both measured off run records: - The arithmetic: a run holding the slot seals in ~50 min (#8103, 6044910: 09:41:58 → completed/success 10:31:23; #8108 started its first job 10:31:25, two seconds later, off the group slot). main merged ~every 7 min in that window, so every arrival that landed while a run held the slot was evicted by the one after it (10:01, 10:06, 10:08, 10:21, 10:38, 10:43 — all jobs=0), and only the last arrival before the slot freed got through. The lever is the merge gap — a deliberate quiet window is the maintainer's call; cancelling other runs is not a lever. - The instrument rule: the list endpoint's `status` is not monotonic. At 10:57Z it reported #8108/#8109 as `queued`, which read as a 40-minute runner wait starving every seal behind it; the runs' own jobs endpoint at 11:12Z said 23 jobs, 22 completed — 42 min of real work, no starvation. "Started nothing" is decided from jobs.total_count, started_at and the per-run conclusion, never from a listing's status. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
A cancelled delivery run with zero jobs is a superseded queue entry — measured, documented, guarded
No workflow behaviour changes.
main-cd.yml'sconcurrency:block is byte-identical; only the comment above it moved, because it was wrong in a way that held a roll this morning.The measurement (2026-09-08, every
main-cdrun created 09:00–10:09Z)All seven:
run_attempt: 1,jobs.total_count: 0,triggering_actor: github-merge-queue[bot]. That is GitHub's one-pending-slot replacement inside our ownmain-cd-<ref>group (cancel-in-progress: falseprotects the run in flight; a waiting run is replaced by the next arrival). The commit order onmainis the arrival order, so every evicted run was an ancestor of the run that replaced it — nothing was starved; the slot always held the newest commit.What it is not, from the record alone: not the org's Actions budget (jobs never started, and
MeshWeaver.Pluginsruns in the same minutes executed normally); not a session or a person (seven cancellations 1–2 s after seven arrivals). What needed attention was the other conclusion in the same list — thefailureon the run that did execute (that is #3461's overlap, owned elsewhere; not touched here).What changes
main-cd.ymlcomment: the block claimed "concurrent runs QUEUE — the second waits for the first". GitHub keeps ONE waiting run and cancels the rest with zero jobs. The comment now says so, gives the reading, and states why the push lane keeps one group on the ref whiledotnet-test.ymlgives every main commit its own (the opposite shape, on purpose —#2412vs#2490/#3077).ReadingCiSignals→ new section "A CD run cancelled with ZERO jobs was evicted from the pending slot" — the table above, the three causes and their discriminators, what to do (nothing) and where to look instead.ModuleBuildArchitecture→ "Superseded runs on main":jobs=0alone is not the Disposal now KNOWS when it finishes: a terminal signal, and drains that cannot lie #888 starvation signature; the in-flight run beside it is.MainRunsAreNeverCancelledGuard.Delivery_NeverKillsTheRunInFlight_AndKeepsTheReconcileInItsOwnLanereadsmain-cd.ymland evaluates (not pattern-matches) its two expressions:cancel-in-progressfalse forworkflow_run,scheduleandworkflow_dispatch; the reconcile's group ≠ the push lane's; two deliveries share one group.DeliveryGuard_FiresOnEachMutation_AndStaysSilentOnTheShippedShapeis the control arm: the flag flipped (literal and expression), the reconcile folded into the push lane, a per-run and a per-commit group — each fires; the shipped pair is silent.Category: Feature).Verified locally
dotnet build test/MeshWeaver.Documentation.Test -c Release -warnaserror→ 0 warnings, 0 errors.dotnet test … --filter MainRunsAreNeverCancelledGuard --no-buildand--filter DocumentationLinkIntegrityTest --no-build→ see the CI run; counts reported in the thread.check-workflow-timeouts.py,check-workflow-yaml-keys.py,check-workflow-shell.py,check-pr-secret-preflight.py— self-tests and the tree, all clean.Refs #2490, #3077, #2412. Does not touch #3461 (a peer's change set).
🤖 Generated with Claude Code