Skip to content

feat: one required status check that fails closed - #66

Merged
stxkxs merged 1 commit into
mainfrom
add-merge-gate
Aug 8, 2026
Merged

feat: one required status check that fails closed#66
stxkxs merged 1 commit into
mainfrom
add-merge-gate

Conversation

@stxkxs

@stxkxs stxkxs commented Aug 8, 2026

Copy link
Copy Markdown
Member

Groundwork for turning branch protection on across the org. No repository settings change here — the gate blocks nothing until it is named as a required check.

Why one gate instead of naming the real jobs

Names are not stable. Matrix legs are named after their inputs. landing-zone publishes 144 check names, ~120 of them like Validate (components/aws/tenant-substrate). Delete a component and any required context naming it never reports again — and a required context that never reports leaves every PR Pending forever, from a change that looks unrelated to CI.

Naming jobs cannot keep up. A job added to a workflow is not automatically required. The branch-protection list is a hand-maintained copy of the CI graph, kept in a second place, drifting toward less coverage.

One gate per workflow gives one stable required name, with coverage checked by the gate rather than by whoever last edited settings.

Two details carry it

if: always() — without it a failed dependency skips the gate, and GitHub counts a skipped check as passing for branch protection. From the docs: "A job that is skipped will report its status as 'Success'. It will not prevent a pull request from merging, even if it is a required check." So the gate would report green in exactly the case it exists to catch. incident-response had this bug for real (incident-response#76).

Restricted to pull_request — the gate treats a skipped dependency as a failure, and a job carrying if: github.event_name == 'pull_request' is legitimately skipped on a push to main. Three repos have such jobs, including landing-zone's entire plan matrix. The gate exists to gate merges, and merges come from PRs; on push it is skipped and gates nothing.

What the shared action refuses to pass on

nanohype/.github#26, pinned by SHA:

condition why
any dependency not success skipped / cancelled / neutral are green to GitHub; here they are not
needs: empty a refactor would otherwise leave a gate passing by observing nothing
a job in the workflow not in needs: an unwatched job cannot block a merge, and this is the only required check
workflow unreadable, or zero jobs coverage it cannot verify is coverage it does not get to claim

The third keeps the hand-written needs: list honest as jobs are added — it is what caught incident-response's gate silently missing its docs job.

Coverage was verified mechanically before commit: the gate parses, watches every other job in its workflow, carries always(), and destroys no existing job. Plan: ~/.claude/plans/repo-settings-asserter.md.

Branch protection is about to go on across the org, and it needs a check
name that is stable and that actually gates. Naming the real jobs gives
neither.

Names are not stable. Matrix legs are named after their inputs, so a
required context can stop reporting because a component was deleted — and
a required context that never reports leaves every pull request Pending
forever, from a change that looks unrelated to CI. Naming jobs also cannot
keep up: a job added to a workflow is not automatically required, so the
branch-protection list is a hand-kept copy of the CI graph that drifts in
the direction of less coverage.

This adds one gate job per PR-triggered workflow, depending on every other
job in it, delegating to the shared action in nanohype/.github. That single
name becomes the only required check.

Two details carry the whole thing:

`if: always()` — without it a failed dependency SKIPS the gate, and GitHub
counts a skipped check as passing for branch protection, so the gate would
report green in exactly the case it exists to catch.

Restricted to pull_request — the gate treats a skipped dependency as a
failure, and a job carrying `if: github.event_name == 'pull_request'` is
legitimately skipped on a push to main. The gate exists to gate merges, and
merges come from pull requests.

The shared action refuses to pass on a dependency that is not success
(skipped and cancelled included), on an empty needs list, and on any job in
the workflow that the gate does not watch. That last check is what keeps
the hand-written needs list honest as jobs are added.

No repository settings change here. The gate blocks nothing until it is
named as a required check.
@stxkxs
stxkxs merged commit fc2c774 into main Aug 8, 2026
16 checks passed
@stxkxs
stxkxs deleted the add-merge-gate branch August 8, 2026 06:02
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.

1 participant