test(wfctl): state-file v0.14.2 → v1.0.0 read-path pre-flight (Task 31)#608
Merged
Conversation
Task 31 of the strict-contracts force-cutover plan
(docs/plans/2026-05-10-strict-contracts-force-cutover.md, rev5).
Adds the cycle 1 I-4 pre-flight test that catches the cascade-block
risk for PR 5 BEFORE it merges. The strict-contracts cutover changes
the wfctl <-> plugin gRPC envelope but MUST NOT change the on-disk
JSON state-file format (interfaces.ResourceState / iacStateRecord
schema invariant per the design §State-file compat invariant).
- test/fixtures/state-v0.14.2.json — fixture in v0.14.2 schema
shape (snake_case keys, Status field, no SchemaVersion). The
fixture is synthetic — it carries an _fixture_metadata header
noting the operator MUST replace it with a real
coredump-staging/iac-state/<resource>.json snapshot from the
Spaces backend during PR 4 prep (per Task 31 §Step 1). The
synthetic version lets the test run in CI; the operator-captured
replacement provides real-world fidelity.
- cmd/wfctl/state_compat_test.go — two pre-flight tests:
- TestStateFileCompat_v0_14_2_to_v1_0_0: reads the fixture via
iacStateRecord (the v1.0.0 wire decoder used by loadFSState
and spacesWfctlStateStore.ListResources), then converts via
iacRecordToResourceState and asserts ID, Type, Provider,
ProviderID, AppliedConfig, and Outputs all survive the
decode + conversion path.
- TestStateFileCompat_v0_14_2_NoUnknownFieldsLost: re-decodes
the fixture with json.Decoder.DisallowUnknownFields() to
surface any v0.14.2 field that v1.0.0's decoder silently
drops. Strips the _fixture_metadata header before strict
decode (synthetic-fixture annotation only; once the operator
replaces the fixture with a real capture, the strip is a
no-op).
If these tests FAIL: PR 5 cascade-block surfaces (per Task 31
§If FAIL).
- File a separate workflow PR (feat: state-file v0.14.2 compat
shim) that adds the compatibility layer.
- Hold PR 5 (and consequently PRs 6–9) until the shim PR
merges.
- Document the gap in PR 4's CHANGELOG.
Verification: GOWORK=off go test -race ./cmd/wfctl/... PASS;
GOWORK=off go vet ./cmd/wfctl/ clean.
Rollback: revert this commit. The pre-flight is purely additive —
test fixture + Go test. Production state-file decode pipeline is
unaffected.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a wfctl pre-flight compatibility test to ensure strict-contracts gRPC envelope changes don’t inadvertently break the on-disk IaC JSON state-file format (v0.14.2 → v1.0.0 decode path), using a committed fixture intended to be replaced later with a real captured snapshot.
Changes:
- Added a v0.14.2-shaped state JSON fixture (
snake_case,status, noSchemaVersion) with a_fixture_metadataheader. - Added wfctl tests that (1) decode v0.14.2 state through the current
iacStateRecordpath and convert tointerfaces.ResourceState, and (2) strict-decode withDisallowUnknownFields()after stripping_fixture_metadata.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| test/fixtures/state-v0.14.2.json | Adds a synthetic v0.14.2-shaped state-file fixture for CI and later operator replacement. |
| cmd/wfctl/state_compat_test.go | Adds compatibility tests and a helper to locate fixtures across different go test working directories. |
Comment on lines
+75
to
+76
| if state.ProviderID == "" { | ||
| t.Errorf("ProviderID is empty after decode (must default to ResourceID when source is empty)") |
Comment on lines
+78
to
+83
| if state.AppliedConfig == nil { | ||
| t.Errorf("AppliedConfig is nil after decode (config map dropped during conversion)") | ||
| } | ||
| if state.Outputs == nil { | ||
| t.Errorf("Outputs is nil after decode (outputs map dropped during conversion)") | ||
| } |
Comment on lines
+115
to
+116
| dec := json.NewDecoder(strings.NewReader(string(stripped))) | ||
| dec.DisallowUnknownFields() |
⏱ Benchmark Results✅ No significant performance regressions detected. benchstat comparison (baseline → PR)
|
Per cycle 4 code-review PR 608 MINOR-1/2/3 (Copilot-flagged):
MINOR-1 — ProviderID strict-equality (was non-empty check). The
old check would pass silently if `provider_id` stops decoding
because iacRecordToResourceState falls back to ResourceID. Pin to
state.ProviderID == record.ProviderID (fixture has
DO00FIXTURE1234567890) so a silent provider_id decode regression
fails the test loudly.
MINOR-2 — AppliedConfig + Outputs key assertions (were nil-only
checks). The old checks would pass when the maps decoded as
empty {} — silent map-content drop. Pin one known fixture key per
map: AppliedConfig["name"] == "iac-state-spaces-key";
Outputs["access_key"] non-empty.
MINOR-3 — bytes.NewReader cleanup. Replaces
strings.NewReader(string(stripped)) ([]byte→string→Reader) with
bytes.NewReader(stripped) (direct []byte→Reader). Removes the
unnecessary string conversion. strings import dropped, bytes
added.
Verification: GOWORK=off go test ./cmd/wfctl/ -run TestStateFileCompat
-count=1 → PASS (2/2); gofmt clean.
Rollback: revert this commit; assertions return to non-empty
form (which still validates the read pipeline doesn't panic but
permits silent map-content drops).
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
Task 31 of the strict-contracts force-cutover plan (docs/plans/2026-05-10-strict-contracts-force-cutover.md, rev5).
Adds the cycle 1 I-4 pre-flight test that catches the cascade-block risk for PR 5 BEFORE that PR merges. The strict-contracts cutover changes the wfctl ↔ plugin gRPC envelope but MUST NOT change the on-disk JSON state-file format (
interfaces.ResourceState/iacStateRecordschema invariant per the design's State-file compat invariant).Independent of the PR 2 stack — modifies only test infrastructure. Base is
main.What's added
test/fixtures/state-v0.14.2.json— fixture in v0.14.2 schema shape (snake_case keys,Statusfield, noSchemaVersion). Synthetic, carries an_fixture_metadataheader noting the operator should replace it with a realcoredump-staging/iac-state/<resource>.jsonsnapshot from the Spaces backend during PR 4 prep (per plan §Step 1). The synthetic version lets the test run in CI; the operator-captured replacement provides real-world fidelity.cmd/wfctl/state_compat_test.go— two pre-flight tests:TestStateFileCompat_v0_14_2_to_v1_0_0— reads the fixture viaiacStateRecord(the v1.0.0 wire decoder used byloadFSStateandspacesWfctlStateStore.ListResources), then converts viaiacRecordToResourceStateand assertsID,Type,Provider,ProviderID,AppliedConfig,Outputsall survive the decode + conversion pathTestStateFileCompat_v0_14_2_NoUnknownFieldsLost— re-decodes withDisallowUnknownFields()so any v0.14.2 field that v1.0.0 silently drops surfaces as a test failure. Strips the_fixture_metadataheader before strict decode (synthetic annotation only — the strip becomes a no-op once the operator replaces with a real capture)If these tests FAIL
Per Task 31 §If FAIL — PR 5 cascade-block surfaces:
feat: state-file v0.14.2 compat shim) adding the compatibility layerVerification
GOWORK=off go test -race ./cmd/wfctl/...→ PASS (2/2 new tests pass)GOWORK=off go vet ./cmd/wfctl/→ cleanRollback
Revert this commit. The pre-flight is purely additive — test fixture + Go test. Production state-file decode pipeline is unaffected.
Test plan
TestStateFileCompat_v0_14_2_to_v1_0_0passesTestStateFileCompat_v0_14_2_NoUnknownFieldsLostpasses (no unexpected fields)🤖 Generated with Claude Code