CI: build/test gate, artifact-split enforcement, plan and licence invariants - #2
Merged
Conversation
…ariants PR #1 merged with statusCheckRollup empty -- zero check protection. This adds it before Phases 1-7 start landing code. Four jobs: go gofmt, vet, build, `go test -race`, and a check that `go mod tidy` is a no-op so a dropped or drifted pin fails loudly. artifact-split S9-AMENDED splits Anvil into anvil (no network-probing capability compiled in) and anvil-dast. IMPLEMENTATION-PLAN 2.2 ruled that a config boolean does not address the concern the split exists for -- and neither does an unenforced convention. This job runs TestSplit, then injects an internal/dast import and FAILS THE BUILD IF THE GUARD PASSES, so the guard cannot silently rot into a no-op. It also builds all four linux/{amd64,arm64} x {anvil,anvil-dast} static binaries and fails if mattn/go-sqlite3 ever enters the graph, since S12 mandates modernc.org/sqlite precisely to avoid cgo. plan Runs tools/plan_xref.py. IMPLEMENTATION-PLAN 2.6 says "re-run it after any edit to an area file"; making it a required check is the only way that actually happens. licence Asserts LICENSE is canonical Apache-2.0 by sha256, that C.1's three files exist and are non-empty, and that no S5 hard exclusion is in the dependency graph. The exclusion check matches module paths in go.mod/go.sum only, so plan/ and NOTICE stay free to NAME these artifacts in order to explain why they are excluded. Two supporting fixes, both found by running the gate locally first: internal/buildpin `go mod tidy` removes a module no package imports. Nothing imports modernc.org/sqlite yet -- the store is R.4 and its FTS5 guard is R.5 -- so tidy would have silently deleted the pin Phase 0 exists to establish. This package holds it until R.4 lands, and says so in a comment that names its own deletion condition. .gitattributes The Windows working tree had CRLF, so `gofmt -l` reported every Go file as unformatted locally while CI would have passed. Local verification that disagrees with CI is worse than no local verification, and every packet in the plan owes build evidence. Now `* text=auto eol=lf`, with the tree renormalised. Git had stored LICENSE correctly as LF throughout -- only the checkout was affected, confirmed by hashing the blob directly. Known gap, stated rather than hidden: `go test -race` cannot run on this Windows host -- the race detector needs a C toolchain and cgo.exe exits 2. That evidence comes from CI only. Everything else in this commit was verified locally first.
tom-snyder
added a commit
that referenced
this pull request
Aug 7, 2026
…ariants (#2) PR #1 merged with statusCheckRollup empty -- zero check protection. This adds it before Phases 1-7 start landing code. Four jobs: go gofmt, vet, build, `go test -race`, and a check that `go mod tidy` is a no-op so a dropped or drifted pin fails loudly. artifact-split S9-AMENDED splits Anvil into anvil (no network-probing capability compiled in) and anvil-dast. IMPLEMENTATION-PLAN 2.2 ruled that a config boolean does not address the concern the split exists for -- and neither does an unenforced convention. This job runs TestSplit, then injects an internal/dast import and FAILS THE BUILD IF THE GUARD PASSES, so the guard cannot silently rot into a no-op. It also builds all four linux/{amd64,arm64} x {anvil,anvil-dast} static binaries and fails if mattn/go-sqlite3 ever enters the graph, since S12 mandates modernc.org/sqlite precisely to avoid cgo. plan Runs tools/plan_xref.py. IMPLEMENTATION-PLAN 2.6 says "re-run it after any edit to an area file"; making it a required check is the only way that actually happens. licence Asserts LICENSE is canonical Apache-2.0 by sha256, that C.1's three files exist and are non-empty, and that no S5 hard exclusion is in the dependency graph. The exclusion check matches module paths in go.mod/go.sum only, so plan/ and NOTICE stay free to NAME these artifacts in order to explain why they are excluded. Two supporting fixes, both found by running the gate locally first: internal/buildpin `go mod tidy` removes a module no package imports. Nothing imports modernc.org/sqlite yet -- the store is R.4 and its FTS5 guard is R.5 -- so tidy would have silently deleted the pin Phase 0 exists to establish. This package holds it until R.4 lands, and says so in a comment that names its own deletion condition. .gitattributes The Windows working tree had CRLF, so `gofmt -l` reported every Go file as unformatted locally while CI would have passed. Local verification that disagrees with CI is worse than no local verification, and every packet in the plan owes build evidence. Now `* text=auto eol=lf`, with the tree renormalised. Git had stored LICENSE correctly as LF throughout -- only the checkout was affected, confirmed by hashing the blob directly. Known gap, stated rather than hidden: `go test -race` cannot run on this Windows host -- the race detector needs a C toolchain and cgo.exe exits 2. That evidence comes from CI only. Everything else in this commit was verified locally first.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR #1 merged with
statusCheckRollup: []— zero check protection. I flagged that at merge time; this closes it before Phases 1–7 start landing code.Why this shape
Anvil's plan makes three claims that are only true if something enforces them continuously:
00-SPINE.md§S9-AMENDED splitsanvilfromanvil-dast, and §2.2 rules that a boolean inside a shipped binary still supplies the capability to everyone. An unenforced convention is the same as a boolean — it holds right up until someone adds an import.plan_xref.pygets re-run after every area-file edit.IMPLEMENTATION-PLAN.md§2.6 says so. Documentation does not cause that to happen; a required check does.Jobs
gogo test -race, plusgo mod tidymust be a no-op so a dropped or drifted pin fails loudlyartifact-splitTestSplit, then injects aninternal/dastimport and fails the build if the guard passes. Builds all fourlinux/{amd64,arm64}×{anvil,anvil-dast}static binaries. Fails ifmattn/go-sqlite3ever enters the graph.plantools/plan_xref.py— 158 packets, 7 invariantslicenceLICENSEsha256 == canonical Apache-2.0; C.1's three files non-empty; no §S5 exclusion ingo.mod/go.sumThe negative control in
artifact-splitis the part I'd most want reviewed. A guard that has never failed has not been tested, and this one now has to prove it can fail on every run.Two fixes the gate found by being run locally first
internal/buildpin—go mod tidyremoves modules nothing imports. Nothing importsmodernc.org/sqliteyet (the store isR.4, its FTS5 guard isR.5), so the tidy check would have silently deleted the pin Phase 0 exists to establish. This package holds it and names its own deletion condition in a comment..gitattributes— the Windows working tree had CRLF, sogofmt -lflagged every Go file locally while CI (seeing LF) would have passed. Local verification that disagrees with CI is worse than none, and every packet owes build evidence. Now* text=auto eol=lf. Git had storedLICENSEcorrectly as LF the whole time — verified by hashing the blob directly (git cat-file -p HEAD:LICENSE | sha256sum→cfc7749b…), so only the checkout was affected and nothing onmainwas wrong.Risk
Low. No production code paths. The realistic failure mode is a CI job being wrong about its own environment, which the first run on this PR will surface.
Testing
Every job was run locally first, except one:
Known gap, stated rather than hidden:
go test -racecannot run on this Windows host — the race detector needs a C toolchain andcgo.exeexits 2. That one line of evidence comes from CI only.Follow-up
Once this is green,
mainshould get branch protection requiring these four checks. That needs admin rights on the repo and is a settings change, so I have not done it — say the word and I will, or you can flip it in Settings → Branches.