devantler-tech / devantler-tech/ksail

Release runs can be silently dropped, and `turnstyle` can go: adopt `concurrency.queue: max`

Open
#6,360 3 comments 1 reaction 1 assignee Claimed by @devantler View on GitHub
Dominant language
Go
Stars
165
Forks
12
Avg merge
5h 51m
Merged PRs (30d)
347

Description

> 🤖 Generated by the Agentic Engineer

## Why

GitHub Actions shipped a `queue` key for `concurrency`, and it closes a gap this repo is currently exposed to.

**`cancel-in-progress: false` does not mean "every run eventually executes."** It protects the run that is already executing; the run waiting behind it is still discarded when a newer one arrives:

> By default, any existing `pending` job or workflow in the same concurrency group will be canceled and the new queued job or workflow will take its place.

So on a burst of three events in one group, run 1 executes, **run 2 is silently cancelled**, run 3 executes. There is no failure, no annotation, and nothing in the run list to say a run was dropped.

| `queue` | Meaning |
|---|---|
| `single` (default) | "At most one job or workflow run can be `pending` in the concurrency group." |
| `max` | "Up to 100 jobs or workflow runs can be `pending` in the concurrency group." — FIFO |

`queue: max` with `cancel-in-progress: true` is a validation error, so it only ever applies where cancelling is already switched off.

Announcement: https://github.com/orgs/community/discussions/12835#discussioncomment-16602100
Reference: https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#concurrency

**Proven in this org already:** `devantler-tech/agent-skills` has run `queue: max` in its `release.yaml` since 2026-07-11, with every Release run green since. This is propagating a fix we have already validated, not a trial.

There are two distinct wins in this repo — one correctness, one simplification.

### 1. Release and CD runs can be silently dropped

`release.yaml` (group `Release`), `cd.yaml`, `maintenance.yaml` and `update-skills.yaml` all use `cancel-in-progress: false` with no `queue`. A release landing while another is still publishing means the pending one is discarded.

This is the same failure `devantler-tech/agent-skills` hit and fixed on 2026-07-11 in a PR titled *"serialize Release runs to prevent same-tag race"* — it added `queue: max` to a `Release` group with an identical shape to ksail's. The fix simply has not been propagated here yet.

### 2. `softprops/turnstyle` can likely be retired

`system-test-omni.yaml` and `system-test-hetzner.yaml` both pin `softprops/turnstyle` as a "⏳ Wait for cloud test queue" step, and then gate roughly every subsequent step in the job behind `if: steps.turnstyle.outcome == 'success'`.

That is a userland queue, adopted because native concurrency could not queue properly. It costs us:

- **Billed runner time while waiting** — `abort-after-seconds: 5400` means a waiting run can hold a runner for up to 90 minutes doing nothing but polling the API every 30s. Runs pending under native concurrency consume no runner minutes at all.
- **A third-party action in the supply chain** of a workflow that handles cloud credentials.
- **An `if:` guard threaded through the whole job**, which is noise on every step and easy to forget on a newly added one.

Both workflows already declare a workflow-level group (`system-test-omni` / `system-test-hetzner`) that overlaps with what turnstyle is waiting for. Worth confirming what turnstyle still contributes beyond that group before removing it — but with `queue: max` available, native queueing should now cover the intent, and the action plus every `if:` guard can go.

Note these two workflows are currently `workflow_dispatch`-only (the Omni schedule is paused per ksail#4973), which makes this a low-risk moment to change them.

## What

- Add `queue: max` to the serialized groups that must not drop runs: `Release`, `cd`, `maintenance`, `update-skills`.
- Replace `softprops/turnstyle` in the two system-test workflows with native queueing, and delete the resulting dead `if:` guards.
- For the system-test groups, decide deliberately between `queue: max` (never lose a requested cloud run) and an explicit `queue: single` (avoid stacking expensive cloud runs) — and record which, and why, in the workflow.
- Leave `ci.yaml`'s `cancel-in-progress: true` group alone; cancelling superseded CI is correct.

## Acceptance criteria

- [ ] Release/CD/maintenance/update-skills groups carry `queue: max`; two releases in quick succession both publish, in order.
- [ ] `softprops/turnstyle` no longer appears in this repo, and no orphaned `steps.turnstyle` conditions remain.
- [ ] The system-test workflows still serialize — verified by dispatching two runs and observing the second wait rather than run concurrently or die.
- [ ] Each system-test workflow states its chosen `queue` value explicitly with a one-line rationale.

Part of https://github.com/devantler-tech/monorepo/issues/2453 · Rough size: S

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.