gate that the staged tree builds, not just the working tree

Earned by a real mistake this session: `web.Handler` gained a parameter, its
caller in cmd/ was updated in the working tree, and `git add internal docs` left
that caller out. verify.sh was green throughout, because every gate looks at the
files on disk rather than at the commit being made. The result was a commit that
did not compile — the kind of thing git bisect trips over for as long as the repo
exists. I rewrote the two local commits rather than adding a fix-up on top.

The check builds a throwaway checkout of the index via `git write-tree`, so it
cannot touch the real index or working tree, and it only runs when something is
staged. Proved both directions: staging a signature change without its caller
fails, a clean tree passes.

Eighth gate defect found by running the harness against real work rather than
reasoning about it — and the first that was a missing gate rather than a wrong one.
This commit is contained in:
Claude Opus 5
2026-07-31 02:25:01 +06:00
committed by bdeshi
parent 09b94d74f2
commit e6c0673374
2 changed files with 22 additions and 0 deletions
+6
View File
@@ -52,6 +52,12 @@ must do — not the theme. `verify.sh` fails if the embedded reference theme cha
changing, because in practice those two drift together — and it fails on a `<script>` tag in that theme, changing, because in practice those two drift together — and it fails on a `<script>` tag in that theme,
because a reference theme that grows taste stops being a reference (ADR-0026). because a reference theme that grows taste stops being a reference (ADR-0026).
**What is staged has to build, not just the working tree.** Every other gate looks at your files; this one
looks at the commit you are about to make, by building a throwaway checkout of the index. It exists because
staging a subset — a changed signature without its caller — produced a commit that did not compile while
`verify.sh` reported green, and a broken commit is something `git bisect` trips over for as long as the repo
lives. It only runs when something is staged.
**The injection boundary is a gate now, not a memory.** goldmark drops raw HTML from authored Markdown by **The injection boundary is a gate now, not a memory.** goldmark drops raw HTML from authored Markdown by
default, and that default was the only thing standing between a Markdown file and script injection. A default, and that default was the only thing standing between a Markdown file and script injection. A
feature wanting to emit HTML renders a theme template instead (ADR-0036), so nothing here needs raw HTML feature wanting to emit HTML renders a theme template instead (ADR-0036), so nothing here needs raw HTML
+16
View File
@@ -210,6 +210,22 @@ if [ -n "$unformatted" ]; then bad "gofmt: $(echo "$unformatted" | tr '\n' ' ')"
if go vet $pkgs >/tmp/vet.log 2>&1; then pass "go vet"; else bad "go vet"; sed 's/^/ /' /tmp/vet.log; fi if go vet $pkgs >/tmp/vet.log 2>&1; then pass "go vet"; else bad "go vet"; sed 's/^/ /' /tmp/vet.log; fi
if go build $pkgs >/tmp/build.log 2>&1; then pass "go build"; else bad "go build"; sed 's/^/ /' /tmp/build.log; fi if go build $pkgs >/tmp/build.log 2>&1; then pass "go build"; else bad "go build"; sed 's/^/ /' /tmp/build.log; fi
# The working tree building says nothing about what is being committed. Staging a subset — a changed
# signature without its caller — lands a commit that does not compile, which `git bisect` then trips over
# forever. So when something is staged, build the staged tree itself, from a throwaway checkout of the index
# so nothing here can touch the real one.
if ! git diff --cached --quiet 2>/dev/null; then
staged=$(mktemp -d)
if git archive "$(git write-tree)" 2>/dev/null | tar -x -C "$staged" 2>/dev/null &&
(cd "$staged" && go build ./... >/tmp/staged-build.log 2>&1); then
pass "the staged tree builds"
else
bad "what is staged does not build on its own — a partial stage, most likely a caller left behind"
sed 's/^/ /' /tmp/staged-build.log 2>/dev/null
fi
rm -rf "$staged"
fi
if echo "$gofiles" | grep -q '_test\.go$'; then if echo "$gofiles" | grep -q '_test\.go$'; then
if go test -race $pkgs >/tmp/test.log 2>&1; then if go test -race $pkgs >/tmp/test.log 2>&1; then
pass "go test ($(grep -c '^ok' /tmp/test.log) packages)" pass "go test ($(grep -c '^ok' /tmp/test.log) packages)"