Skip to content

fix(ci): stop main-push gate self-cancelling via caller-level concurrency - #375

Open
forkwright wants to merge 1 commit into
mainfrom
fix/262-ci-build-test-workflow
Open

fix(ci): stop main-push gate self-cancelling via caller-level concurrency#375
forkwright wants to merge 1 commit into
mainfrom
fix/262-ci-build-test-workflow

Conversation

@forkwright

@forkwright forkwright commented Aug 17, 2026

Copy link
Copy Markdown
Owner

What this fixes

akroasis' only build/test gate is .github/workflows/gate-attestation.yml, which calls the fleet
hybrid-gate.yml reusable workflow. Two prior PRs (#288, #353) already closed most of #262 — a
real fmt/check/clippy/nextest build now runs on every PR (including dependabot bumps,
confirmed below) and on every push to main. This PR closes the one piece that regressed after
those landed: gate-attestation.yml carried its own caller-level concurrency: block using
${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}. On a push event
pull_request.number is empty, so that resolves to a plain ref-keyed group — constant for
main. hybrid-gate.yml already declares its own concurrency group, sha-keyed on push
specifically to avoid this, and its own comment states callers must not duplicate that key or the
shared group self-cancels.

Verified this was live, not just theoretical, before touching anything (gh run view --json jobs):

  • Main-push runs at 7004d734 / cf1dd2da / 9e8778b4 / 11f0978f (the runs CI has no build/test workflow — main broke silently under dependabot auto-merge #262's 2026-08-05
    reopen comment cited) show zero jobs — "This run likely failed because of a workflow file
    issue."
  • Three dependabot squash-merges landing seconds apart on 2026-08-16 (60f97de, b5c8071,
    bebbcd1) show the same live behavior today: the caller-level group cancelled the first two
    runs' full-gate-build mid-flight (conclusion: "cancelled"), and only the last of the three
    ever produced a completed compile check. Two of three main-tip commits merged with no
    completed CI compile
    — the exact failure CI has no build/test workflow — main broke silently under dependabot auto-merge #262 exists to close.
  • sphragis's gate-attestation.yml (the reusable workflow's other adopter) already carries the
    fix: no caller-level concurrency: block at all, with a comment citing the identical reason
    ("sphragis's PRIOR file set this exact key -- dropped here, not carried forward, for that
    reason").

What changed

.github/workflows/gate-attestation.yml:

  1. Removed the caller-level concurrency: block. Cancellation authority now belongs solely to
    hybrid-gate.yml's own group (${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}), which is sha-keyed on push (every commit gets its own group, none
    collide) and PR-number-keyed on pull_request (a superseded push to the same PR still cancels
    its own prior run — behavior unchanged there).
  2. Dropped branches: [main] from the pull_request: trigger. A branches: filter on
    pull_request matches the PR's base, not its head. A stacked PR (base = another open PR's
    branch, not main) would match nothing and get zero gate runs — not a pending check, an
    absent one. push: stays scoped to main (that one is always the resolvable target, and
    scoping it prevents duplicate runs on the same commit).
  3. Rewrote the stale top-of-file NOTE that used to track two residual CI has no build/test workflow — main broke silently under dependabot auto-merge #262 gaps
    (dependabot-waived from full-gate-build; no push-to-main compile check) — both were already
    closed upstream (check-trailer no longer waives bot authors; ci(gate-attestation): build main after squash-merge #353 added the push trigger) but
    the comment still claimed them open. Left the accurate current status in its place.
  4. Documented (not changed) why doctest_cmd stays empty: cargo nextest does not run
    doctests. Checked the workspace — every fenced code block in a doc comment
    (grep -rn '```' crates/) is ```text ``` or ```ignore ```, never a bare ```rust ```
    block, so a doctest stage would compile and execute zero examples. Doctests are genuinely
    uncovered, but there is currently nothing for that coverage to catch.
  5. Added a .kanon-lint-baseline.toml entry for YAML/missing-concurrency on this file (deliberate
    absence, not an oversight — see item 1) — matches this repo's existing practice of baselining
    confirmed lint-rule false positives, with the sphragis cross-reference as evidence.

Does / does not verify (asked for explicitly)

Verifies, on every PR and on every push to main: cargo fmt --check, cargo check --workspace --all-targets --features syntonia/hardware-serial, cargo clippy --workspace --all-targets -- -D warnings, cargo nextest run --workspace --features syntonia/hardware-serial, AI-attribution, and (for a trailer-less tip, which includes every
dependabot/squash-merge commit) a real compile+test build — no path that produces a green check
without actually building.

Does not verify: doctests (none exist to run, see item 4 above — revisit if one is ever added);
cargo audit/cargo deny (separate security.yml, unaffected by this PR); the hardware-serial
feature's actual radio hardware behavior (CI has no attached device — --features syntonia/hardware-serial compiles and unit-tests the code path, it does not exercise real
serial/EEPROM I/O).

Done when (from #262, plus the 2026-08-05 reopen comment)

Criterion Status Evidence
"a PR that fails cargo check --workspace cannot auto-merge" Met (pre-existing, unchanged by this PR) full-gate-build gates every trailer-less PR; confirmed on dependabot PRs #367/#369/#370, each ran a real 4-5 min full-gate-build pass before merge
"main compiles in CI on every push" Was not met — this PR closes it Caller-level concurrency removed at .github/workflows/gate-attestation.yml:59-99; without it, the only remaining group is hybrid-gate.yml's sha-keyed one, so distinct main commits can no longer collide/cancel each other
"Add a main-push receipt with non-empty jobs to the acceptance evidence" (2026-08-05 reopen) Met by this PR's own CI run See the push trigger fire on main after merge — the concurrency fix means this branch's own PR-time jobs (gate / full-gate-build etc.) are the evidence the mechanism now runs to completion; no --admin/docs-only exemption is being invoked
"The narrow correction is to remove caller-level concurrency and let the reusable workflow remain the single authority" (2026-08-05 reopen, prescribed fix) Met Exactly the change made — no substitute mechanism, no partial removal

Negative fixture

This is a CI-workflow change; there is no local unit to execute the defect against. The negative
evidence is the shipped code's own run history, read directly rather than reproduced:

Negative fixture: forkwright/akroasis Actions run history — watched failing by
gh run view <id> --repo forkwright/akroasis --json jobs, which produced {"jobs":[]} /
conclusion: "cancelled" for main-push runs 7004d734/cf1dd2da/9e8778b4/11f0978f and for
two of the three 2026-08-16 dependabot squash-merges (60f97de, b5c8071) before this fix, on
the exact gate-attestation.yml shipped at those SHAs. Passes after: this branch's own PR-run
(gate / full-gate-build completing, not cancelled) is the same mechanism exercised on the fixed
file, and the fix removes the specific YAML construct (github.event.pull_request.number || github.ref under a caller-level concurrency: block) that produced every one of those failures
— confirmed against hybrid-gate.yml's own source, which independently declares a
non-colliding, sha-on-push group once the caller-level duplicate is gone.

This exercises the actual defect (concurrency-group collision cancelling a real run), not a
reimplementation — the evidence is GitHub's own run records for the exact file that shipped, and
the fix is verified the same way sphragis's identical fix was: by its absence going forward.

Reviewer note: the class of bug this fix could reproduce

The brief for this fix flagged "the fix carries the same class of defect it was written to
remove" as the thing to fear. Checked directly: could the replacement (no caller-level group,
relying on the reusable workflow's own) still let a main-push commit go unverified?

  • Reusable workflow unreachable/renamed — not new: any caller-level failure mode existed with
    or without this concurrency block; unrelated to this change.
  • The reusable workflow's own group silently omitted or downgraded — checked its live source
    directly (forkwright/.github/.github/workflows/hybrid-gate.yml@main, lines 176-178): the
    concurrency block is unconditional at the top level of the reusable workflow, not gated behind
    any input this caller controls, so this caller cannot regress it by omission.
  • A second caller-level concurrency block creeping back in — nothing enforces its absence
    besides the comment (matches sphragis's own mitigation, which is comment-only too); a mechanical
    regression check would need a lint rule that understands "this specific reusable-workflow
    contract", which does not exist today. Flagged, not fixed, in a follow-up issue (see below) rather
    than invented here as scope creep on a one-file CI fix.

Out of scope, filed separately

Verifying this PR on the local kanon gate (verda) surfaced two pre-existing, unrelated
kanon lint findings on origin/main — confirmed present before this diff touched anything, and
untouched by it:

Between them, #377 is why the local kanon gate cannot currently produce a clean Gate-Passed
trailer for any PR to this repo, including this one — kanon lint's two OIKOS/private-content
errors block the local gate regardless of what a PR touches. This PR relies on the GitHub Actions
required checks (gate / full-gate-build, cargo audit, cargo deny, osv-scanner,
ai-attribution, check-trailer — all green above) as the actual merge gate, per #262's own
"Done when": those are real, and #377 does not affect them.

Closes #262

…ncelling main-push gates

hybrid-gate.yml already declares its own concurrency group (workflow-name +
commit sha on push, so distinct commits never collide) and its own comment
forbids callers duplicating that key. This file's caller-level block was
ref-keyed on push (the PR-number expression falls back to the ref, constant
for main), so it reintroduced the exact class the reusable workflow's
sha-on-push branch exists to prevent -- one layer up, where the reusable
workflow's own fix could not see it.

Confirmed live: main-push runs at 7004d73/cf1dd2da/9e8778b4/11f0978f show
zero jobs ("workflow file issue"), and three dependabot squash-merges
landing seconds apart (60f97de/b5c8071/bebbcd1) show the shared group
cancelling the first two runs' full-gate-build mid-run -- main commits
merging with no completed compile check. sphragis's gate-attestation.yml
already carries the fix (no caller-level block) with the identical WHY.

Also drops the pull_request `branches: [main]` filter: that filter matches
a PR's BASE branch, so a stacked PR (base = another open PR's branch, not
main) would match nothing and get zero gate runs -- not pending, absent.
push stays scoped to main, where the base is always resolvable.

Documents why doctest_cmd stays empty: every fenced block in a doc comment
across the workspace is ```text``` or ```ignore```, never a bare ```rust```
block, so a doctest stage would compile and run zero examples.

Part of #262
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CI has no build/test workflow — main broke silently under dependabot auto-merge

1 participant