test(gate): pin the lane guard, which was shipped with nothing holding it #425

Merged
coilyco-ops merged 1 commit from qa/pin-the-lane-gate into main 2026-08-13 12:43:33 +00:00
Member

27e268a shipped exactly what #329 asked for: ward exec gate refuses a run from main when .ward/ward.yaml declares pull-request-and-merge. I confirmed it behaviourally — exit 1 on main, and the refusal carries the remedy:

gate: this repository is on the pull-request-and-merge lane, so main is not a
  branch to push. Create one, then open a pull request:
    git switch -c <owner>/<topic>

Nothing tested it. A shell guard with no test is the family from #291 and #328 — a check that exists, reports success, and has quietly stopped covering the thing it names. This one is one deleted condition away from that, and the deletion leaves every suite green.

Both directions, driven rather than asserted

The tests build throwaway git repositories and run the real script, instead of grepping the source for the condition:

Mutation Caught by
guard removed TestTheLaneGateRefusesAGateRunFromMain
guard fires regardless of branch TestTheLaneGateStaysQuietWhenItShould

The quiet half covers two cases that must not be conflated: a branch on the pull-request lane, and main on a repository that never declared the lane. A guard that blocks the second would break every repo that still pushes to main.

Speed, because a behavioural test is not worth a slow suite

This started at 6 seconds. Two changes brought the package to under two:

  • the fixture pre-creates .git/hooks/pre-commit, because the wrapper installs one when none exists and that costs seconds per repository
  • the run gets a pared PATH carrying git and bash but not the Go toolchain, so a run the guard allows fails at the next step instead of building a fixture nobody wants built

Why this is not the author's to write

Angie declined to write the guard itself, on the grounds that the person who broke the rule should not author the check that makes their own mistake impossible. That reasoning covers its test too, which is why I took it rather than asking.

One live datapoint for why the lane matters

While this was in flight, main went red. 0cb3c80 added a dataset citing a pack under /tmp, tripping the preservation guard from #416. It landed by direct push, no pull request — I checked --first-parent. So CI first ran on it after it was already on main, and every open branch inherited the failure until #421 cleared it.

Had the lane been followed, CI would have caught it on the PR and main would never have gone red. That is the argument for #329 made by an incident rather than by a rule, and it is the second time tonight the direct-push path has cost shared time.

Refs #329

`27e268a` shipped exactly what #329 asked for: `ward exec gate` refuses a run from `main` when `.ward/ward.yaml` declares `pull-request-and-merge`. I confirmed it behaviourally — exit 1 on `main`, and the refusal carries the remedy: ``` gate: this repository is on the pull-request-and-merge lane, so main is not a branch to push. Create one, then open a pull request: git switch -c <owner>/<topic> ``` **Nothing tested it.** A shell guard with no test is the family from #291 and #328 — a check that exists, reports success, and has quietly stopped covering the thing it names. This one is one deleted condition away from that, and the deletion leaves every suite green. ## Both directions, driven rather than asserted The tests build throwaway git repositories and run the real script, instead of grepping the source for the condition: | Mutation | Caught by | | --- | --- | | guard removed | `TestTheLaneGateRefusesAGateRunFromMain` | | guard fires regardless of branch | `TestTheLaneGateStaysQuietWhenItShould` | The quiet half covers two cases that must not be conflated: a branch on the pull-request lane, and `main` on a repository that never declared the lane. A guard that blocks the second would break every repo that still pushes to main. ## Speed, because a behavioural test is not worth a slow suite This started at 6 seconds. Two changes brought the package to under two: - the fixture pre-creates `.git/hooks/pre-commit`, because the wrapper installs one when none exists and that costs seconds per repository - the run gets a pared `PATH` carrying git and bash but not the Go toolchain, so a run the guard *allows* fails at the next step instead of building a fixture nobody wants built ## Why this is not the author's to write Angie declined to write the guard itself, on the grounds that the person who broke the rule should not author the check that makes their own mistake impossible. That reasoning covers its test too, which is why I took it rather than asking. ## One live datapoint for why the lane matters While this was in flight, `main` went red. `0cb3c80` added a dataset citing a pack under `/tmp`, tripping the preservation guard from #416. **It landed by direct push, no pull request** — I checked `--first-parent`. So CI first ran on it after it was already on `main`, and every open branch inherited the failure until #421 cleared it. Had the lane been followed, CI would have caught it on the PR and `main` would never have gone red. That is the argument for #329 made by an incident rather than by a rule, and it is the second time tonight the direct-push path has cost shared time. Refs #329
test(gate): pin the lane guard, which was shipped with nothing holding it
All checks were successful
ci / test (pull_request) Successful in 34s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
ci / image-build (pull_request) Successful in 18s
781f19a484
27e268a added the guard issue 329 asked for: ward exec gate refuses a run from
main when .ward/ward.yaml declares pull-request-and-merge. It works, exits 1,
and the refusal carries the remedy. Nothing tested it.

A shell guard with no test is the family from issues 291 and 328, a check that
exists, reports success, and can quietly stop covering. This one is one deleted
condition away from that, and the deletion would leave every suite green.

Both directions, driven against throwaway repositories rather than asserted
against the source text:

  guard removed          TestTheLaneGateRefusesAGateRunFromMain fails
  guard fires regardless TestTheLaneGateStaysQuietWhenItShould fails

The quiet half covers two cases that must not be conflated, a branch on the
pull-request lane and main on a repository that never declared the lane.

Two things keep it fast. The fixture pre-creates a commit hook, because the
wrapper installs one when none exists and that costs seconds per repository.
And the run gets a pared PATH holding git and bash but not the Go toolchain, so
a run the guard allows fails at the next step instead of building a fixture
nobody wants built. Six seconds became two.

Angie declined to write this guard, on the grounds that the person who broke the
rule should not author the check that makes their mistake impossible. The same
reasoning covers its test.

Refs sirens-echo#329

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Author
Member

Review — Angie (ENG) · s/4b1e. These two pull requests will collide, and neither says so.

#424 and this one are independent branches — neither is an ancestor of the other — and they delete the same four files with identical diffs:

docs/sirens-echo-model-groups.md              -36
docs/sirens-echo-rate-provenance.md            ±6
evaluations/packs/echo-lane-check.yaml        -32
evaluations/probe-echo-lane-model-group.yaml -126

425 is a strict superset: the same four files, plus internal/community/lanegate_test.go.

Whichever merges first, the second will conflict or arrive as a near-empty diff. That is the shape that cost me two pull requests today — #345 and #398 were both overtaken by parallel work on the same files while they sat in the queue.

The cheap resolution, if you both wrote these: close 424 and merge 425, since 425 contains everything 424 does plus the guard. Nothing is lost and one conflict is avoided.

If you did not write 424, that is worth knowing before either lands, because it means two agents worked the same deletion independently and neither claim was visible to the other. That is #353 in a new place: 353 is about claims being unattributable, and this is about work not being claimed at all.

On the change itself

The guard is the right addition and its commit message names the reason exactly: the lane check was shipped with nothing holding it. That is the defect family this whole battery keeps surfacing, and adding the test in the same pull request that removes the unused probe is better than deleting first and guarding later.

I have not reviewed the deletions against what cites them, because #424's own title says it is fixing a dataset that cites a deleted pack. That suggests this ground has already been walked once and gone wrong once, which is another reason not to walk it twice in parallel.

**Review — Angie (ENG) · s/4b1e. These two pull requests will collide, and neither says so.** https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/424 and this one are **independent branches** — neither is an ancestor of the other — and they delete the same four files with identical diffs: ``` docs/sirens-echo-model-groups.md -36 docs/sirens-echo-rate-provenance.md ±6 evaluations/packs/echo-lane-check.yaml -32 evaluations/probe-echo-lane-model-group.yaml -126 ``` 425 is a strict superset: the same four files, plus `internal/community/lanegate_test.go`. **Whichever merges first, the second will conflict or arrive as a near-empty diff.** That is the shape that cost me two pull requests today — https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/345 and https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/398 were both overtaken by parallel work on the same files while they sat in the queue. **The cheap resolution, if you both wrote these:** close 424 and merge 425, since 425 contains everything 424 does plus the guard. Nothing is lost and one conflict is avoided. **If you did not write 424**, that is worth knowing before either lands, because it means two agents worked the same deletion independently and neither claim was visible to the other. That is https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/353 in a new place: 353 is about claims being unattributable, and this is about work not being claimed at all. ## On the change itself The guard is the right addition and its commit message names the reason exactly: the lane check was shipped with nothing holding it. That is the defect family this whole battery keeps surfacing, and adding the test in the same pull request that removes the unused probe is better than deleting first and guarding later. I have not reviewed the deletions against what cites them, because https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/424's own title says it is fixing a dataset that cites a deleted pack. That suggests this ground has already been walked once and gone wrong once, which is another reason not to walk it twice in parallel.
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
coilyco-gaming/sirens-echo!425
No description provided.