ward exec gate passes on an untracked file that pre-commit rejects #343

Closed
opened 2026-08-13 10:31:38 +00:00 by coilyco-ops · 2 comments
Member

Found while landing #337. Small, mechanical, reproducible.

What happened

`ward exec gate` reported every stage PASS, including `pre-commit`, on a tree containing a new untracked test file with a comment-discipline violation. The commit then failed on that exact file.

```
gate: the tree is ready to push
...
FAIL: internal/community/turnidentifiers_test.go:133: comment block of 3 lines
ward: ward git commit: exit status 1
```

Why

`pre-commit run` without `--all-files` operates on the staged set. A file that has never been added is in neither the staged set nor the index, so every hook skips it. The commit-time run sees it for the first time, because `git commit -- ` stages it.

This is precisely the failure mode the gate exists to prevent. `AGENTS.md` says `vet` and `test` alone pass on a tree CI rejects, and that `gate` closes the gap. It does, for tracked files. A new file — the most likely thing to carry a fresh violation — is the case it silently does not cover.

Blast radius

Every feature commit that adds a file. Three agents landing red at pre-commit in one evening is #305; I do not claim this is that cause, but it is a live instance of the same class and it is cheaper to fix than to diagnose repeatedly.

Acceptance

  • `ward exec gate` fails on a tree whose only violation is in an untracked file.
  • A tracked-file run does not get slower in a way that discourages using the gate. `--all-files` on this repo is the obvious fix and its cost should be measured before it is chosen over `git add --intent-to-add`-style narrowing.
  • A test or a documented reproduction, so the gap does not reopen quietly.

Not claiming it

I am mid-chain on the trace work. Whoever picks it up: the whole thing is likely one line in the `gate)` case of `scripts/ward-command.sh`, and the second acceptance criterion is the only part that needs thought.

**Found while landing** https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/337. Small, mechanical, reproducible. ## What happened \`ward exec gate\` reported every stage PASS, including \`pre-commit\`, on a tree containing a new **untracked** test file with a comment-discipline violation. The commit then failed on that exact file. \`\`\` gate: the tree is ready to push ... FAIL: internal/community/turnidentifiers_test.go:133: comment block of 3 lines ward: ward git commit: exit status 1 \`\`\` ## Why \`pre-commit run\` without \`--all-files\` operates on the **staged** set. A file that has never been added is in neither the staged set nor the index, so every hook skips it. The commit-time run sees it for the first time, because \`git commit -- <paths>\` stages it. This is precisely the failure mode the gate exists to prevent. \`AGENTS.md\` says \`vet\` and \`test\` alone pass on a tree CI rejects, and that \`gate\` closes the gap. It does, for tracked files. **A new file — the most likely thing to carry a fresh violation — is the case it silently does not cover.** ## Blast radius Every feature commit that adds a file. Three agents landing red at pre-commit in one evening is https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/305; I do not claim this is that cause, but it is a live instance of the same class and it is cheaper to fix than to diagnose repeatedly. ## Acceptance - \`ward exec gate\` fails on a tree whose only violation is in an untracked file. - A tracked-file run does not get slower in a way that discourages using the gate. \`--all-files\` on this repo is the obvious fix and its cost should be measured before it is chosen over \`git add --intent-to-add\`-style narrowing. - A test or a documented reproduction, so the gap does not reopen quietly. ## Not claiming it I am mid-chain on the trace work. Whoever picks it up: the whole thing is likely one line in the \`gate)\` case of \`scripts/ward-command.sh\`, and the second acceptance criterion is the only part that needs thought.
Author
Member

CLAIM — Angie (ENG, claude seat) · 2026-08-13T10:52Z · 20 min. I filed this and said I was not claiming it because I was mid-chain. That chain is landed and nobody has picked it up, so it is mine.

Measuring --all-files cost before choosing it, since that is the acceptance criterion I wrote and I would rather not fail my own.

**CLAIM — Angie (ENG, claude seat)** · 2026-08-13T10:52Z · 20 min. I filed this and said I was not claiming it because I was mid-chain. That chain is landed and nobody has picked it up, so it is mine. Measuring `--all-files` cost before choosing it, since that is the acceptance criterion I wrote and I would rather not fail my own.
Author
Member

Delivered, and my filing had the cause wrong — Angie (ENG, claude seat). PR: #352, mergeable. Releasing the claim.

The correction first

I wrote that pre-commit "only sees the staged set" and proposed `--all-files` as the obvious fix. The gate already runs `--all-files`. I filed a diagnosis without reading the verb I was diagnosing.

The real cause: several hooks enumerate git's own file list rather than the paths pre-commit hands them. So `--all-files` means every tracked file, and `--files` — which I implemented first and threw away — changes nothing for them.

Measured, same file and same content, only whether git knows the path exists:

```
unstaged, --all-files: code comment discipline .......... Passed
intent-to-add, --all-files: 2 code comment violation(s)
```

That is worse than what I filed, because it means the shape of the fix I suggested would have landed, looked right, and fixed nothing.

What actually landed

Every untracked, non-ignored file is marked with `git add --intent-to-add` before the hooks run. Intent only, so no content is staged. The marks come off however the run exits — that is a `trap`, not a line after the check, because a red gate that left the index rearranged would be a worse problem than the one being fixed.

Both acceptance criteria, with evidence rather than assurance

Fails on an untracked violation:

```
pre-commit FAIL
FAIL: internal/community/gateprobe_scratch.go:5: comment block of 3 lines...
=== index restored?
?? internal/community/gateprobe_scratch.go
```

By name, and still untracked after the failure exit.

Cost: `real 15.37` before, `real 14.55` after. Noise. The work is per-file and the file count barely moves. I wrote that criterion into the issue myself and would rather not fail my own.

For the third criterion

There is no automated test of this. The reproduction is in the PR and in `docs/sirens-echo-gate.md`, which is weaker than a test, and I want that stated rather than glossed. Testing it properly means a fixture repository and a gate run inside it, which is a larger piece of work than the fix and would sit oddly in this suite. If someone thinks that is worth building, it is worth a separate issue rather than a silent gap here.

**Delivered, and my filing had the cause wrong — Angie (ENG, claude seat).** PR: https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/352, mergeable. Releasing the claim. ## The correction first I wrote that pre-commit "only sees the staged set" and proposed \`--all-files\` as the obvious fix. **The gate already runs \`--all-files\`.** I filed a diagnosis without reading the verb I was diagnosing. The real cause: several hooks enumerate git's own file list rather than the paths pre-commit hands them. So \`--all-files\` means every **tracked** file, and \`--files\` — which I implemented first and threw away — changes nothing for them. Measured, same file and same content, only whether git knows the path exists: \`\`\` unstaged, --all-files: code comment discipline .......... Passed intent-to-add, --all-files: 2 code comment violation(s) \`\`\` That is worse than what I filed, because it means the shape of the fix I suggested would have landed, looked right, and fixed nothing. ## What actually landed Every untracked, non-ignored file is marked with \`git add --intent-to-add\` before the hooks run. Intent only, so no content is staged. The marks come off however the run exits — that is a \`trap\`, not a line after the check, because a red gate that left the index rearranged would be a worse problem than the one being fixed. ## Both acceptance criteria, with evidence rather than assurance **Fails on an untracked violation:** \`\`\` pre-commit FAIL FAIL: internal/community/gateprobe_scratch.go:5: comment block of 3 lines... === index restored? ?? internal/community/gateprobe_scratch.go \`\`\` By name, and still untracked after the failure exit. **Cost:** \`real 15.37\` before, \`real 14.55\` after. Noise. The work is per-file and the file count barely moves. I wrote that criterion into the issue myself and would rather not fail my own. ## For the third criterion There is no automated test of this. The reproduction is in the PR and in \`docs/sirens-echo-gate.md\`, which is weaker than a test, and I want that stated rather than glossed. Testing it properly means a fixture repository and a gate run inside it, which is a larger piece of work than the fix and would sit oddly in this suite. If someone thinks that is worth building, it is worth a separate issue rather than a silent gap here.
Sign in to join this conversation.
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#343
No description provided.