feat(corpus): fetch the API documents the tests read, rather than committing them - #131
Merged
Conversation
…mitting them This repository generates Terraform providers; it is not the home of any vendor's artefacts, and today 95% of its tracked files are exactly that. The documents have to come from somewhere, though: inference bugs are found by specifications that disagree with each other, and every one this generator has had was found by a second vendor rather than by a synthetic fixture that agreed with the code. So the documents are fetched at test time and pinned here. Pinned means pinned. corpus.lock.json records a version, a SHA-256 and the path and operation counts, and a fetch matching none of them fails rather than proceeding quietly. That is the whole point rather than a detail: tests assert facts about these documents -- enum members, candidate counts, inferred bindings -- so a vendor editing a specification changes what those tests mean. The lock turns that from an inexplicable failure weeks later into a review taken deliberately with corpus refresh. Nothing here reimplements pinning. internal/snapshot already stores documents immutably with a checksum and self-describing metadata; this adds the one thing it lacked for use as a cache, a directory name derived from the pin rather than from the wall clock, so a pinned document can be found again instead of merely being present. Three decisions worth stating, because each has a failure mode behind it. Documents are materialised into a staging directory and renamed into place. `go test ./...` runs packages as separate processes, so several race to fill a cold cache, and snapshot.Pin refuses to overwrite -- immutability is the property it exists to defend. Without atomic publication the first run after any lock change fails spuriously in whichever packages lost the race, which would look like flakiness and be diagnosed as anything but this. An unfetchable document skips locally and fails when TFPFGEN_CORPUS_REQUIRED is set. Failing everywhere would turn a developer's flaky connection into a red suite and teach people to ignore red; skipping everywhere would let CI skip its way to green, which is a standing lie about coverage. A document that was fetched but did not match its pin is never skipped anywhere. Fetching prefers a mirror over upstream, and this is not hypothetical future-proofing. Writing this commit, ThousandEyes had already moved: the pinned 7.0.98 was gone from the unified-oas path, which serves current rather than a version, and 7.0.99 was in its place. GitHub's description has the same shape of problem, published from main. A mirror holding the exact pinned bytes cannot weaken anything, because the digest is checked identically whichever source answered -- it is a CDN, not a source of truth -- and without one every refresh is forced on the vendor's schedule. No mirror is configured yet; refresh warns about each pin that lacks one. The ThousandEyes pin is 7.0.99 for that reason: 7.0.98 is no longer fetchable from anywhere. Touches no existing test. Nothing reads this package yet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
First step of removing per-API-product artefacts from this repo. Touches no existing test — nothing reads this package yet, so main cannot move.
Why
95% of this repository's tracked files are artefacts of specific API products (
pilot/4,673 files,recordings/1,340,blueprints/203,openapi/7). The toolkit itself is 280. Those artefacts belong in the provider repos the pipeline generates.But the tests still need real documents. Every inference defect this generator has had was found by a second vendor's specification disagreeing with the first — not by a synthetic fixture that agrees with the code. So documents are fetched at test time and pinned, rather than vendored.
What
internal/corpuswraps the existinginternal/snapshotrather than reimplementing pinning. Snapshot already stores documents immutably with a checksum and self-describing metadata; the only thing it lacked as a cache was a directory name derived from the pin rather than the wall clock, so a pinned document can be found again instead of merely being present.internal/corpus/corpus.lock.jsonis the reviewable artefact — version, SHA-256, path and operation counts per document. A fetch matching none of them fails rather than proceeding.That is the point rather than a detail. Tests assert facts about these documents — enum members, candidate counts, inferred bindings — so a vendor editing a specification changes what those tests mean. The lock converts an inexplicable failure weeks later into a deliberate review.
New CLI:
corpus sync | check | refresh, documented indocs/cli.md.Three decisions, each with a failure mode behind it
Staged then renamed.
go test ./...runs packages as separate processes, so several race to fill a cold cache, andsnapshot.Pinrefuses to overwrite — immutability is the property it defends. Without atomic publication the first run after any lock change fails spuriously in whichever packages lost the race. That would present as flakiness and be diagnosed as anything but this.Skip locally, fail in CI. Failing everywhere turns a developer's flaky connection into a red suite, which teaches people to ignore red. Skipping everywhere lets CI skip its way to green, which is a standing lie about coverage. A document that was fetched but did not match its pin is never skipped anywhere.
Mirror preferred over upstream — and this is the part that stopped being hypothetical while I was writing it.
ThousandEyes had already moved
The first cold sync failed:
That URL serves current, not a version. 7.0.98 — the snapshot committed in this repo — is no longer fetchable from anywhere. GitHub's description has the same shape of problem, published from
main.So the pin here is 7.0.99, taken deliberately through
corpus refresh, andrefreshwarns on every pin lacking a mirror. A mirror is a prerequisite for this scheme, not a nicety — without one, refreshes are forced on the vendor's schedule. A mirror cannot weaken anything, since the digest is checked identically whichever source answered; it is a CDN, not a source of truth. None is configured yet — that needs a decision on where it lives.Verification
httptest, no network): lock integrity, cold-fetches-warm-does-not with a request counter, the mismatch message, recovery from a corrupted cache, mirror-preferred and mirror-cannot-change-meaning, offline-is-not-corruption, cache redirection.go build ./...,go vet ./...,go test ./..., gofumpt all clean..tfpfgen/cache/is already gitignored — verified withgit check-ignore.🤖 Generated with Claude Code