docs(agents): record why CI / docker is not a required status #20

Merged
jlxq0 merged 1 commit from chore/record-branch-protection into main 2026-08-27 02:07:34 +00:00
Owner

Records why CI / docker is excluded from the required statuses on main, which is the
half of the change that is not visible in the API.

main had no rule of any kind

Confirmed before arming: GET /branch_protections returned []. So git push origin main
went straight through, and every green enforced in this repo this week was discipline
rather than a gate.

Now armed: enable_push=false, apply_to_admins=true, enable_status_check=true,
status_check_contexts=["CI / cargo*"], required_approvals=0. Zero approvals is what
makes it cost nothing — I still merge my own work, CI merely stops being bypassable.

Why CI / docker is not required

A job skipped because the job it needs: failed still posts success to the commit
status.
Reproduced in this repository, not taken on report:

commit CI / cargo CI / docker docker tasks in the run
45b68251 failure 02:52:14Z success 02:52:15Z none
d8e59351 failure 02:40:36Z success 02:40:36Z none
54de8c40 failure 02:27:59Z success 02:27:59Z none

The third column is the point. GET /actions/tasks shows a single cargo task for each of
those commits and no docker task at all, so the success is not a docker job that passed
quickly — it is a status for work that never ran.

Requiring it would be worse than not requiring it: the rule would read as broader coverage
while being satisfied by the very failure it was meant to catch. docker still runs and
still gates a release, building the exact linux/amd64 image and smoking it before publish.
It just cannot be a required status while it carries a needs:.

The glob is load-bearing

Contexts carry an event suffix — CI / cargo (pull_request) on a PR head, CI / cargo (push) on a branch push. A literal context string matches one and silently never matches
the other, which is an unarmed gate that reads as armed. CI / cargo* covers both, and
both suffixes are observed above and on cef453a / 0366d5b.

Acceptance

A rule read back from the server says it was stored, not that it bites, so it was pushed
against rather than inspected.

Probe branch, rule armed between two pushes. First push with no rule: accepted. Rule
armed on that branch. Second push:

remote: Forgejo: Not allowed to push to protected branch probe/branch-protection
 ! [remote rejected] probe/branch-protection -> probe/branch-protection (pre-receive hook declined)
exit=1

Probe rule and probe branch both deleted, HTTP 204 each; git ls-remote --heads origin
confirms neither remains.

And on main itself, because a rule proven on a throwaway branch is not a measurement
of the branch that matters:

remote: Forgejo: Not allowed to push to protected branch main
 ! [remote rejected] main -> main (pre-receive hook declined)
exit=1

apply_to_admins=true, so it refuses the repository owner. This PR is the real merge under
the new flags.

Also

chore/one-audit-suppression survived #17's merge and was still on the remote. Deleted,
after confirming origin/main..origin/chore/one-audit-suppression was empty.

jlxq0/mantis#141.

Records why `CI / docker` is excluded from the required statuses on `main`, which is the half of the change that is not visible in the API. ## `main` had no rule of any kind Confirmed before arming: `GET /branch_protections` returned `[]`. So `git push origin main` went straight through, and every green enforced in this repo this week was discipline rather than a gate. Now armed: `enable_push=false`, `apply_to_admins=true`, `enable_status_check=true`, `status_check_contexts=["CI / cargo*"]`, `required_approvals=0`. Zero approvals is what makes it cost nothing — I still merge my own work, CI merely stops being bypassable. ## Why `CI / docker` is not required **A job skipped because the job it `needs:` failed still posts `success` to the commit status.** Reproduced in this repository, not taken on report: | commit | `CI / cargo` | `CI / docker` | docker tasks in the run | |---|---|---|---| | `45b68251` | failure 02:52:14Z | **success** 02:52:15Z | none | | `d8e59351` | failure 02:40:36Z | **success** 02:40:36Z | none | | `54de8c40` | failure 02:27:59Z | **success** 02:27:59Z | none | The third column is the point. `GET /actions/tasks` shows a single `cargo` task for each of those commits and no `docker` task at all, so the `success` is not a docker job that passed quickly — it is a status for work that never ran. Requiring it would be worse than not requiring it: the rule would read as broader coverage while being satisfied by the very failure it was meant to catch. `docker` still runs and still gates a release, building the exact linux/amd64 image and smoking it before publish. It just cannot be a *required status* while it carries a `needs:`. ## The glob is load-bearing Contexts carry an event suffix — `CI / cargo (pull_request)` on a PR head, `CI / cargo (push)` on a branch push. A literal context string matches one and silently never matches the other, which is an unarmed gate that reads as armed. `CI / cargo*` covers both, and both suffixes are observed above and on `cef453a` / `0366d5b`. ## Acceptance A rule read back from the server says it was stored, not that it bites, so it was pushed against rather than inspected. **Probe branch, rule armed between two pushes.** First push with no rule: accepted. Rule armed on that branch. Second push: ``` remote: Forgejo: Not allowed to push to protected branch probe/branch-protection ! [remote rejected] probe/branch-protection -> probe/branch-protection (pre-receive hook declined) exit=1 ``` Probe rule and probe branch both deleted, `HTTP 204` each; `git ls-remote --heads origin` confirms neither remains. **And on `main` itself**, because a rule proven on a throwaway branch is not a measurement of the branch that matters: ``` remote: Forgejo: Not allowed to push to protected branch main ! [remote rejected] main -> main (pre-receive hook declined) exit=1 ``` `apply_to_admins=true`, so it refuses the repository owner. This PR is the real merge under the new flags. ## Also `chore/one-audit-suppression` survived #17's merge and was still on the remote. Deleted, after confirming `origin/main..origin/chore/one-audit-suppression` was empty. `jlxq0/mantis#141`.
docs(agents): record why CI / docker is not a required status
All checks were successful
CI / cargo (pull_request) Successful in 2m15s
CI / docker (pull_request) Successful in 41s
708102b653
`main` now has branch protection: `CI / cargo*` required, `enable_push=false`,
`apply_to_admins=true`, `required_approvals=0`. It had no rule of any kind
before, so every green enforced this week was discipline rather than a gate.

`CI / docker` is excluded on purpose and the reason has to be written down or
the next person tidying up adds it back. A job skipped because the job it
`needs:` failed still posts `success` to the commit status. Measured on three
commits in this repo — 45b68251, d8e59351, 54de8c40 — where `CI / cargo` posted
failure and `CI / docker` posted success within a second, and no docker task
existed in the run at all. Requiring it would build a gate that is green
because the work did not happen.

The glob matters too: contexts carry an event suffix, `(pull_request)` on a PR
head and `(push)` on a branch push, so a literal string matches one and
silently never matches the other.

Verified by pushing rather than by reading the rule back: with it armed,
`git push origin main` is refused with `pre-receive hook declined`, exit 1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018CXx4uyuDhqwungt4SMN4S
jlxq0 merged commit 08da09c4f0 into main 2026-08-27 02:07:34 +00:00
jlxq0 deleted branch chore/record-branch-protection 2026-08-27 02:07:34 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jlxq0/typst-mcp!20
No description provided.