claim Plan 14 Part 1 fix round 6: no census means main's rollback target
Under merge ruling 6 the rollback target is an effect, so runningVersion returns nothing without a census and main's cur stands (an interrupted repoint rolls back to what the link names, as on main). Census-backed retention is unchanged. Adds the interrupted-repoint row to the main-equivalence table (it catches the old behaviour) and splits the crash-before-capture test by census mode. Census overlay fix6.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 files changed,
+102
−69
internal/host/plan14_fix3_live_linux_test.go+61 −51internal/host/plan14_fix5_live_linux_test.go+23 −1internal/host/rootrestart.go+9 −17internal/provision/plan14_legacy_identity_test.go+3 −0internal/provision/plan14_root_reads_test.go+3 −0internal/provision/testdata/plan14-root-reads-fix6.tsv+3 −0
CI passed — run details
CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok amadan.net/carlos/platform.git/launchctx 0.015s
? amadan.net/carlos/platform.git/launchctx/controlname [no test files]
? amadan.net/carlos/platform.git/launchctx/wireid [no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok github.com/carlosframework/platform 341.418s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 4.218s
ok github.com/carlosframework/platform/cmd/moveprobe 92.900s
ok github.com/carlosframework/platform/internal/activator 228.946s
ok github.com/carlosframework/platform/internal/agentsock 0.024s
ok github.com/carlosframework/platform/internal/alerting 0.004s
ok github.com/carlosframework/platform/internal/analytics 0.041s
ok github.com/carlosframework/platform/internal/appconfig 2.755s
ok github.com/carlosframework/platform/internal/apperr 0.105s
ok github.com/carlosframework/platform/internal/applog 0.253s
ok github.com/carlosframework/platform/internal/awsinv 0.031s
ok github.com/carlosframework/platform/internal/boxchoice 0.043s
ok github.com/carlosframework/platform/internal/boxid 0.976s
? github.com/carlosframework/platform/internal/boxid/boxidtest [no test files]
? github.com/carlosframework/platform/internal/boxid/internal/signtest [no test files]
ok github.com/carlosframework/platform/internal/boxvolumes 0.027s
ok github.com/carlosframework/platform/internal/caps 0.031s
ok github.com/carlosframework/platform/internal/certwarm 0.016s
ok github.com/carlosframework/platform/internal/cgroup 0.254s
ok github.com/carlosframework/platform/internal/changelog 0.391s
ok github.com/carlosframework/platform/internal/clamd 0.060s
? github.com/carlosframework/platform/internal/cliflag [no test files]
ok github.com/carlosframework/platform/internal/console 369.276s
? github.com/carlosframework/platform/internal/controlname [no test files]
? github.com/carlosframework/platform/internal/controlsock [no test files]
ok github.com/carlosframework/platform/internal/dbdoor 6.088s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.013s
ok github.com/carlosframework/platform/internal/dbverify 0.012s
ok github.com/carlosframework/platform/internal/dnsrec 0.013s
ok github.com/carlosframework/platform/internal/docsite 0.008s
ok github.com/carlosframework/platform/internal/domains 2.539s
ok github.com/carlosframework/platform/internal/doorkey 0.028s
ok github.com/carlosframework/platform/internal/economics 0.005s
ok github.com/carlosframework/platform/internal/edge 28.233s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.013s
ok github.com/carlosframework/platform/internal/fleetapi 0.199s
ok github.com/carlosframework/platform/internal/gate 1.791s
ok github.com/carlosframework/platform/internal/genselect 0.026s
ok github.com/carlosframework/platform/internal/geo 0.026s
ok github.com/carlosframework/platform/internal/geoip 0.042s
ok github.com/carlosframework/platform/internal/host 100.953s
ok github.com/carlosframework/platform/internal/hostactors 0.710s
ok github.com/carlosframework/platform/internal/hostbox 3.534s
ok github.com/carlosframework/platform/internal/hostclaim 1.343s
ok github.com/carlosframework/platform/internal/hostevidence 0.024s
ok github.com/carlosframework/platform/internal/hostexec 7.952s
ok github.com/carlosframework/platform/internal/hostfence 0.052s
ok github.com/carlosframework/platform/internal/hostfiles 11.300s
ok github.com/carlosframework/platform/internal/hostfloor 0.022s
ok github.com/carlosframework/platform/internal/hostguard 1.083s
ok github.com/carlosframework/platform/internal/hostops 2.601s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.005s
ok github.com/carlosframework/platform/internal/hostquarantine 0.004s
ok github.com/carlosframework/platform/internal/hostrelocate 1.133s
ok github.com/carlosframework/platform/internal/hostresolve 0.334s
ok github.com/carlosframework/platform/internal/hostwrite 0.022s
ok github.com/carlosframework/platform/internal/infraview 0.021s
ok github.com/carlosframework/platform/internal/instancectl 1.809s
ok github.com/carlosframework/platform/internal/instanceexec 2.927s
ok github.com/carlosframework/platform/internal/instancefs 0.005s
ok github.com/carlosframework/platform/internal/instancekey 0.004s
ok github.com/carlosframework/platform/internal/instanceupdates 0.021s
ok github.com/carlosframework/platform/internal/layout 2.333s
ok github.com/carlosframework/platform/internal/lease 9.561s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.020s
ok github.com/carlosframework/platform/internal/ledger 0.060s
ok github.com/carlosframework/platform/internal/mail 0.386s
ok github.com/carlosframework/platform/internal/maintenance 0.169s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.989s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.140s
ok github.com/carlosframework/platform/internal/metrics 0.034s
ok github.com/carlosframework/platform/internal/moves 24.122s
ok github.com/carlosframework/platform/internal/moves/archive 1.457s
ok github.com/carlosframework/platform/internal/objstore 0.498s
ok github.com/carlosframework/platform/internal/ops 0.339s
ok github.com/carlosframework/platform/internal/park 0.415s
ok github.com/carlosframework/platform/internal/privatefile 0.009s
ok github.com/carlosframework/platform/internal/provision 15.676s
ok github.com/carlosframework/platform/internal/publicrelease 0.009s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 4.707s
ok github.com/carlosframework/platform/internal/relay 7.297s
ok github.com/carlosframework/platform/internal/release 22.656s
ok github.com/carlosframework/platform/internal/rollout 4.456s
ok github.com/carlosframework/platform/internal/rolloutwords 0.005s
ok github.com/carlosframework/platform/internal/rootaws 0.012s
ok github.com/carlosframework/platform/internal/rootparticipation 0.027s
ok github.com/carlosframework/platform/internal/rootstate 0.027s
ok github.com/carlosframework/platform/internal/s3compat 0.005s
ok github.com/carlosframework/platform/internal/safefile 0.004s
ok github.com/carlosframework/platform/internal/schedule 2.931s
ok github.com/carlosframework/platform/internal/sim/hostlife 19.285s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 4.556s
ok github.com/carlosframework/platform/internal/status 0.092s
ok github.com/carlosframework/platform/internal/steering 0.007s
ok github.com/carlosframework/platform/internal/stopevidence 0.670s
ok github.com/carlosframework/platform/internal/svcrole 0.006s
ok github.com/carlosframework/platform/internal/telemetry 0.005s
ok github.com/carlosframework/platform/internal/termfence 0.006s
ok github.com/carlosframework/platform/internal/tunnelclient 0.007s
ok github.com/carlosframework/platform/internal/unixdial 0.017s
ok github.com/carlosframework/platform/internal/updatewords 0.009s
ok github.com/carlosframework/platform/internal/usage 0.005s
ok github.com/carlosframework/platform/internal/version 0.003s
ok github.com/carlosframework/platform/internal/vet 1.963s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
minio: cached, digest verified
mc: cached, digest verified
minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
buckets and scoped user ready
=== RUN TestDeliveredContractMinio
=== RUN TestDeliveredContractMinio/put
=== RUN TestDeliveredContractMinio/get_round-trips_the_body
=== RUN TestDeliveredContractMinio/list_shows_the_key
=== RUN TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN TestDeliveredContractMinio/delete_removes_it
=== RUN TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
--- PASS: TestDeliveredContractMinio/put (0.00s)
--- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
--- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
--- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
--- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok github.com/carlosframework/platform/internal/objstore 0.022s
=== RUN TestT4StorageProbeMinio
2026/10/13 17:10:08 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DE25EEA26AD9CE, HostID: 7c420053e0fbcd7f41653fdd92ffa2802c903d05303b1aa15adc2e8bb65d183f, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
--- PASS: TestT4StorageProbeMinio (0.00s)
PASS
ok github.com/carlosframework/platform/internal/host 0.020s
minio contract: PASS
diff --git a/internal/host/plan14_fix3_live_linux_test.go b/internal/host/plan14_fix3_live_linux_test.go| index c7b3861dc..d539d5836 100644 |
| --- a/internal/host/plan14_fix3_live_linux_test.go |
| +++ b/internal/host/plan14_fix3_live_linux_test.go |
| @@ -450,64 +450,74 @@ func TestR2CrashBeforeRestartCapture(t *testing.T) { |
| if !plan14RootFixture(t) { |
| return |
| } |
| - for _, fail := range []bool{false, true} { |
| - t.Run(map[bool]string{false: "forward", true: "rollback"}[fail], func(t *testing.T) { |
| - f := r2Fixture(t, "list") |
| - crashed := errors.New("crash after repoint before restart capture") |
| - func() { |
| - defer func() { |
| - if recover() != crashed { |
| - t.Fatal("missing crash") |
| - } |
| - }() |
| - _ = release.Pass(t.Context(), f.e.root.reg, f.e.store, f.pub, f.u.opt, func(string) error { panic(crashed) }, release.WithRunningVersion(f.c.runningVersion)) |
| - }() |
| - f.wantLive(t, "v2") |
| - f.wantRunning(t, "v1") |
| - state, err := f.c.attempts() |
| - if err != nil { |
| - t.Fatal(err) |
| - } |
| - if _, err := readRollbackAttempt(state, f.rt.Host); !os.IsNotExist(err) { |
| - t.Fatal("attempt already captured", err) |
| - } |
| - m := release.Manifest{App: f.rt.App, Version: "v3", Kind: "binary", Artifacts: map[string]release.Artifact{release.AdoptPlatform(): {}}} |
| - if err := release.Ship(t.Context(), f.e.store, "bab", m, map[string][]byte{release.AdoptPlatform(): []byte("third build")}); err != nil { |
| - t.Fatal(err) |
| - } |
| - f.promote(t, "v3") |
| - // A new resolver and restart callback model the next root process. |
| - f.source.index = nil |
| - if fail { |
| - f.u.afterEffect = func() { |
| - if f.u.exe == release.VersionPath(f.u.opt, "", "v3", f.rt.App) { |
| - f.u.props.ActiveState, f.u.props.SubState, f.u.props.MainPID = "failed", "failed", 0 |
| - } |
| + // Merge ruling 6: without a census the rollback target is main's cur |
| + // (the interrupted repoint's v2); only a census-backed box retains the |
| + // verified running build (v1) as its rollback target. |
| + for _, mode := range []string{"no census", "census"} { |
| + for _, fail := range []bool{false, true} { |
| + t.Run(mode+"/"+map[bool]string{false: "forward", true: "rollback"}[fail], func(t *testing.T) { |
| + f := r2Fixture(t, "list") |
| + if mode == "census" { |
| + f.c.legacy = func() (bool, error) { return false, nil } |
| } |
| - } |
| - err = f.pass(t) |
| - if fail { |
| - if err == nil { |
| - t.Fatal("failed build accepted") |
| - } |
| - f.wantLive(t, "v1") |
| + // Main's rollback target, and the retained one with a census. |
| + target := map[string]string{"no census": "v2", "census": "v1"}[mode] |
| + crashed := errors.New("crash after repoint before restart capture") |
| + func() { |
| + defer func() { |
| + if recover() != crashed { |
| + t.Fatal("missing crash") |
| + } |
| + }() |
| + _ = release.Pass(t.Context(), f.e.root.reg, f.e.store, f.pub, f.u.opt, func(string) error { panic(crashed) }, release.WithRunningVersion(f.c.runningVersion)) |
| + }() |
| + f.wantLive(t, "v2") |
| f.wantRunning(t, "v1") |
| - } else { |
| + state, err := f.c.attempts() |
| if err != nil { |
| t.Fatal(err) |
| } |
| - f.wantLive(t, "v3") |
| - f.wantRunning(t, "v3") |
| - prior, err := os.Readlink(filepath.Join(f.u.opt, "previous", f.rt.Host)) |
| - if err != nil || prior != release.VersionPath(f.u.opt, "", "v1", f.rt.App) { |
| - t.Fatal("lost running authority", prior, err) |
| + if _, err := readRollbackAttempt(state, f.rt.Host); !os.IsNotExist(err) { |
| + t.Fatal("attempt already captured", err) |
| } |
| - pins, err := release.LivePins(f.u.opt) |
| - if err != nil || !pins["v1"] { |
| - t.Fatal("rollback build not pinned", pins, err) |
| + m := release.Manifest{App: f.rt.App, Version: "v3", Kind: "binary", Artifacts: map[string]release.Artifact{release.AdoptPlatform(): {}}} |
| + if err := release.Ship(t.Context(), f.e.store, "bab", m, map[string][]byte{release.AdoptPlatform(): []byte("third build")}); err != nil { |
| + t.Fatal(err) |
| } |
| - } |
| - }) |
| + f.promote(t, "v3") |
| + // A new resolver and restart callback model the next root process. |
| + f.source.index = nil |
| + if fail { |
| + f.u.afterEffect = func() { |
| + if f.u.exe == release.VersionPath(f.u.opt, "", "v3", f.rt.App) { |
| + f.u.props.ActiveState, f.u.props.SubState, f.u.props.MainPID = "failed", "failed", 0 |
| + } |
| + } |
| + } |
| + err = f.pass(t) |
| + if fail { |
| + if err == nil { |
| + t.Fatal("failed build accepted") |
| + } |
| + f.wantLive(t, target) |
| + f.wantRunning(t, target) |
| + } else { |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + f.wantLive(t, "v3") |
| + f.wantRunning(t, "v3") |
| + prior, err := os.Readlink(filepath.Join(f.u.opt, "previous", f.rt.Host)) |
| + if err != nil || prior != release.VersionPath(f.u.opt, "", target, f.rt.App) { |
| + t.Fatal("previous pointer", prior, err) |
| + } |
| + pins, err := release.LivePins(f.u.opt) |
| + if err != nil || !pins[target] { |
| + t.Fatal("rollback build not pinned", pins, err) |
| + } |
| + } |
| + }) |
| + } |
| } |
| } |
diff --git a/internal/host/plan14_fix5_live_linux_test.go b/internal/host/plan14_fix5_live_linux_test.go| index 0676ce7be..75022acfb 100644 |
| --- a/internal/host/plan14_fix5_live_linux_test.go |
| +++ b/internal/host/plan14_fix5_live_linux_test.go |
| @@ -137,6 +137,28 @@ func fix5Run(t *testing.T, kind, history string, main bool) []fix5Result { |
| u.resetError = true |
| case "TestR4ForwardTryRestartError": |
| u.tryError = true |
| + case "interrupted repoint": |
| + // A root pass crashed after pointing v2 live and before restarting |
| + // v1's process; the next build, v3, then fails to come up. Main |
| + // rolls back to what the link names (v2), not to the running v1. |
| + account := release.RouteAccount(f.rt) |
| + if err := release.StageArtifact(f.u.opt, account, "v2", f.rt.App, []byte("new build")); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := release.PointLive(f.u.opt, f.rt.Host, account, "v2", f.rt.App); err != nil { |
| + t.Fatal(err) |
| + } |
| + m := release.Manifest{App: f.rt.App, Version: "v3", Kind: "binary", Artifacts: map[string]release.Artifact{release.AdoptPlatform(): {}}} |
| + if err := release.Ship(t.Context(), f.e.store, "bab", m, map[string][]byte{release.AdoptPlatform(): []byte("third build")}); err != nil { |
| + t.Fatal(err) |
| + } |
| + f.promote(t, "v3") |
| + v3 := release.VersionPath(f.u.opt, account, "v3", f.rt.App) |
| + target.afterEffect = func() { |
| + if target.exe == v3 { |
| + target.props.ActiveState, target.props.SubState, target.props.MainPID = "failed", "failed", 0 |
| + } |
| + } |
| } |
| getenv := func(string) string { return "5s" } |
| observer := f.e.root.obs |
| @@ -246,7 +268,7 @@ func TestPlan14Fix5MainEquivalence(t *testing.T) { |
| t.Run(map[string]string{"": "claims-off", "1": "claims-on"}[flag], func(t *testing.T) { |
| t.Setenv("CARLOS_HOST_CLAIM", flag) |
| for _, kind := range []string{"none", "record", "list", "guard", "different stamp"} { |
| - for _, history := range []string{"healthy", "crash", "failed-then-reset-failed before rollback", "auto-restart", "inactive", "incident reset before", "incident reset after", "TestR4ForwardResetFailedError", "TestR4ForwardTryRestartError"} { |
| + for _, history := range []string{"healthy", "crash", "failed-then-reset-failed before rollback", "auto-restart", "inactive", "incident reset before", "incident reset after", "TestR4ForwardResetFailedError", "TestR4ForwardTryRestartError", "interrupted repoint"} { |
| t.Run(kind+"/"+history, func(t *testing.T) { |
| want := fix5Run(t, kind, history, true) |
| got := fix5Run(t, kind, history, false) |
diff --git a/internal/host/rootrestart.go b/internal/host/rootrestart.go| index eb87e6355..93109feb0 100644 |
| --- a/internal/host/rootrestart.go |
| +++ b/internal/host/rootrestart.go |
| @@ -114,25 +114,17 @@ func (c rootRestartChecks) runningVersion(ctx context.Context, rt registry.Route |
| if rt.Unit == "" || rt.Unit == activator.UnitName(rt.Host) { |
| return "", nil |
| } |
| - b, err := c.resolve(ctx, rt) |
| - if err == nil && b.Route.Unit != rt.Unit { |
| - // Without a census, retain only the unit main will restart. |
| - legacy, checkErr := c.allowLegacy() |
| - if checkErr != nil { |
| - return "", checkErr |
| - } |
| - if legacy { |
| - return "", nil |
| - } |
| + // Merge ruling 6: without a census the binding never chooses an |
| + // effect, and the rollback target is one. Main's cur stands. |
| + legacy, err := c.allowLegacy() |
| + if err != nil { |
| + return "", err |
| } |
| + if legacy { |
| + return "", nil |
| + } |
| + b, err := c.resolve(ctx, rt) |
| if err != nil { |
| - legacy, checkErr := c.allowLegacy() |
| - if checkErr != nil { |
| - return "", checkErr |
| - } |
| - if legacy { |
| - return "", nil |
| - } |
| return "", err |
| } |
| if b.PID > 0 { |
diff --git a/internal/provision/plan14_legacy_identity_test.go b/internal/provision/plan14_legacy_identity_test.go| index 146f54083..f96fea929 100644 |
| --- a/internal/provision/plan14_legacy_identity_test.go |
| +++ b/internal/provision/plan14_legacy_identity_test.go |
| @@ -184,6 +184,9 @@ func TestPlan14Fix5OverlayPreservesLegacySeal(t *testing.T) { |
| if err := loadRootReadInventory(root+"/internal/provision/testdata/plan14-root-reads-fix5.tsv", &inv, true); err != nil { |
| t.Fatal(err) |
| } |
| + if err := loadRootReadInventory(root+"/internal/provision/testdata/plan14-root-reads-fix6.tsv", &inv, true); err != nil { |
| + t.Fatal(err) |
| + } |
| if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| t.Fatal(errs) |
| } |
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go| index 43d6dc9a1..4f2a404c4 100644 |
| --- a/internal/provision/plan14_root_reads_test.go |
| +++ b/internal/provision/plan14_root_reads_test.go |
| @@ -637,6 +637,9 @@ func TestPlan14RootReadCensus(t *testing.T) { |
| if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-fix5.tsv"), &inv, true); err != nil { |
| t.Fatal(err) |
| } |
| + if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-fix6.tsv"), &inv, true); err != nil { |
| + t.Fatal(err) |
| + } |
| errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...) |
| errs = append(errs, checkRootReadDeclarations(got, inv)...) |
| if len(errs) > 0 { |
diff --git a/internal/provision/testdata/plan14-root-reads-fix6.tsv b/internal/provision/testdata/plan14-root-reads-fix6.tsv| new file mode 100644 |
| index 000000000..34d429164 |
| --- /dev/null |
| +++ b/internal/provision/testdata/plan14-root-reads-fix6.tsv |
| @@ -0,0 +1,3 @@ |
| +# Plan 14 fix6: under merge ruling 6 the rollback target is an effect, so runningVersion retains nothing without a census; earlier overlays and the legacy freeze are unchanged. |
| +internal/host/rootrestart.go rootRestartChecks.runningVersion authority B3 rev3=merge-ruling-6;basis=Without a census return no running build, so main's cur stays the rollback target and no record is resolved; census-backed retention unchanged. registry:Host@151,registry:Unit@104,registry:Unit@121,whole:Route@439 173ae670856b3e93089b04f45d6356b88910f7cd8eeb7c3bc7770c9ff4ab13c9 |
| +@coverage internal/host efb1aa8d275862827daeacf4955d653fb48bae69e8fccacb0efdb881334e0419 |