After the Gitea 1.27 + act_runner 2.0.0 upgrade, verify-stack never starts: the run sits in waiting forever with no logs for that job, while the other five jobs pass — so main stays pending/red (P0). See #134.
Gitea 1.27 reworked cancellation/aggregation so that always()/cancelled()-gated needs jobs route through a new transitional Cancelling state + server↔runner capability negotiation ("Requires Gitea Runner 2.0.0"). On this 1.27 + 2.0.0 pairing that handshake doesn't resolve, so the job is never dispatched and never leaves waiting. Plain jobs (no if/needs) are unaffected — exactly the observed pattern. It worked pre-upgrade (old runner).
Fix
Drop the if: ${{ !cancelled() }}; keep needs: [mutation]. Default if: success() dispatches normally and still serialises the two memory-heavy jobs (OOM avoidance, #126).
Trade-off: the !cancelled() (added in #127) let verify-stack run even when the mutation ratchet fails. Now a failing mutation skips verify-stack; the fix-and-re-push re-run exercises it, so the signal isn't lost — just deferred to the green-mutation run. If we later want both signals on one run, serialise via a concurrency group rather than needs + always().
Documented as §7 in docs/runbooks/gitea-actions-gotchas.md.
Note on the stuck run
Run 582 (the #133 merge) will not clear itself and must be force-cancelled from the Actions UI (plain cancel can also stall on this version, gitea#35782). This PR's own run is the first real test of the fix — if verify-stack dispatches and runs here, the fix holds.
## What & why
After the Gitea 1.27 + act_runner 2.0.0 upgrade, `verify-stack` never starts: the run sits in `waiting` forever with no logs for that job, while the other five jobs pass — so `main` stays pending/red (P0). See #134.
Closes #134
### Root cause
`verify-stack` was the only job gated by a status-function `if` on top of `needs`:
```yaml
verify-stack:
needs: [mutation]
if: ${{ !cancelled() }}
```
Gitea 1.27 reworked cancellation/aggregation so that `always()`/`cancelled()`-gated `needs` jobs route through a new transitional **`Cancelling`** state + server↔runner **capability negotiation** ("Requires Gitea Runner 2.0.0"). On this 1.27 + 2.0.0 pairing that handshake doesn't resolve, so the job is never dispatched and never leaves `waiting`. Plain jobs (no `if`/`needs`) are unaffected — exactly the observed pattern. It worked pre-upgrade (old runner).
### Fix
Drop the `if: ${{ !cancelled() }}`; keep `needs: [mutation]`. Default `if: success()` dispatches normally and still serialises the two memory-heavy jobs (OOM avoidance, #126).
**Trade-off:** the `!cancelled()` (added in #127) let verify-stack run even when the mutation ratchet fails. Now a failing mutation skips verify-stack; the fix-and-re-push re-run exercises it, so the signal isn't lost — just deferred to the green-mutation run. If we later want both signals on one run, serialise via a `concurrency` group rather than `needs` + `always()`.
Documented as §7 in `docs/runbooks/gitea-actions-gotchas.md`.
## Note on the stuck run
Run 582 (the #133 merge) will **not** clear itself and must be force-cancelled from the Actions UI (plain cancel can also stall on this version, gitea#35782). This PR's own run is the first real test of the fix — if `verify-stack` dispatches and runs here, the fix holds.
## Definition of Done
- [x] Linked issue (#134).
- [x] Conventional Commit referencing the issue.
- [x] CI green — this PR's run is the verification (verify-stack must dispatch).
- [x] Runbook updated (gotchas §7).
- [x] Closed by the merging PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What & why
After the Gitea 1.27 + act_runner 2.0.0 upgrade,
verify-stacknever starts: the run sits inwaitingforever with no logs for that job, while the other five jobs pass — somainstays pending/red (P0). See #134.Closes #134
Root cause
verify-stackwas the only job gated by a status-functionifon top ofneeds:Gitea 1.27 reworked cancellation/aggregation so that
always()/cancelled()-gatedneedsjobs route through a new transitionalCancellingstate + server↔runner capability negotiation ("Requires Gitea Runner 2.0.0"). On this 1.27 + 2.0.0 pairing that handshake doesn't resolve, so the job is never dispatched and never leaveswaiting. Plain jobs (noif/needs) are unaffected — exactly the observed pattern. It worked pre-upgrade (old runner).Fix
Drop the
if: ${{ !cancelled() }}; keepneeds: [mutation]. Defaultif: success()dispatches normally and still serialises the two memory-heavy jobs (OOM avoidance, #126).Trade-off: the
!cancelled()(added in #127) let verify-stack run even when the mutation ratchet fails. Now a failing mutation skips verify-stack; the fix-and-re-push re-run exercises it, so the signal isn't lost — just deferred to the green-mutation run. If we later want both signals on one run, serialise via aconcurrencygroup rather thanneeds+always().Documented as §7 in
docs/runbooks/gitea-actions-gotchas.md.Note on the stuck run
Run 582 (the #133 merge) will not clear itself and must be force-cancelled from the Actions UI (plain cancel can also stall on this version, gitea#35782). This PR's own run is the first real test of the fix — if
verify-stackdispatches and runs here, the fix holds.Definition of Done
🤖 Generated with Claude Code