WIP claim Plan 14 track E checkpoint 44 (not for merge)
Protected timers: intention before materialization, exact retirement ownership; descriptor-transfer containment counterexample; item 4a sized.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 files changed,
+434
−20
internal/host/claimretire_transport.go+1 −0internal/host/plan14_generation_timer_retirement_test.go+8 −8internal/host/plan14_generation_transfer_root_linux_test.go+164 −0internal/host/plan14_timer_intention_root_linux_test.go+25 −0internal/host/plan14_timer_intention_test.go+185 −0internal/host/rootinventory.go+3 −0internal/host/rootinventory_timer.go+30 −1internal/host/rootinventory_timer_test.go+2 −2internal/hostbox/reconcile_adapter.go+3 −2internal/hostbox/retirement_timers.go+4 −1internal/provision/plan14_e_write_census_test.go+1 −1internal/provision/testdata/plan14-root-reads-e.tsv+8 −5
CI passed — run details
CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok amadan.net/carlos/platform.git/launchctx 0.012s
? 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 306.854s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 2.983s
ok github.com/carlosframework/platform/cmd/moveprobe 93.264s
ok github.com/carlosframework/platform/cmd/zddprobe 0.707s
ok github.com/carlosframework/platform/internal/activator 249.174s
ok github.com/carlosframework/platform/internal/agentsock 0.015s
ok github.com/carlosframework/platform/internal/alerting 0.003s
ok github.com/carlosframework/platform/internal/analytics 0.020s
ok github.com/carlosframework/platform/internal/appconfig 2.504s
ok github.com/carlosframework/platform/internal/apperr 0.074s
ok github.com/carlosframework/platform/internal/applog 0.124s
ok github.com/carlosframework/platform/internal/awsinv 0.004s
ok github.com/carlosframework/platform/internal/boxchoice 0.004s
ok github.com/carlosframework/platform/internal/boxid 0.667s
? 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.008s
ok github.com/carlosframework/platform/internal/caps 0.012s
ok github.com/carlosframework/platform/internal/certwarm 0.004s
ok github.com/carlosframework/platform/internal/cgroup 0.700s
ok github.com/carlosframework/platform/internal/changelog 0.306s
ok github.com/carlosframework/platform/internal/clamd 0.054s
? github.com/carlosframework/platform/internal/cliflag [no test files]
ok github.com/carlosframework/platform/internal/console 391.846s
? 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 5.902s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.009s
ok github.com/carlosframework/platform/internal/dbverify 0.004s
ok github.com/carlosframework/platform/internal/dnsrec 0.003s
ok github.com/carlosframework/platform/internal/docsite 0.005s
ok github.com/carlosframework/platform/internal/domains 2.368s
ok github.com/carlosframework/platform/internal/doorkey 0.006s
ok github.com/carlosframework/platform/internal/economics 0.004s
ok github.com/carlosframework/platform/internal/edge 26.982s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.008s
ok github.com/carlosframework/platform/internal/fleetapi 0.135s
ok github.com/carlosframework/platform/internal/gate 1.655s
ok github.com/carlosframework/platform/internal/genselect 0.015s
ok github.com/carlosframework/platform/internal/geo 0.015s
ok github.com/carlosframework/platform/internal/geoip 0.030s
ok github.com/carlosframework/platform/internal/historyreadback 0.157s
ok github.com/carlosframework/platform/internal/host 145.655s
ok github.com/carlosframework/platform/internal/hostactors 0.344s
ok github.com/carlosframework/platform/internal/hostbox 2.347s
ok github.com/carlosframework/platform/internal/hostclaim 0.542s
ok github.com/carlosframework/platform/internal/hostevidence 0.013s
ok github.com/carlosframework/platform/internal/hostexec 4.977s
ok github.com/carlosframework/platform/internal/hostfence 0.036s
ok github.com/carlosframework/platform/internal/hostfiles 10.556s
ok github.com/carlosframework/platform/internal/hostfloor 0.013s
ok github.com/carlosframework/platform/internal/hostguard 1.044s
ok github.com/carlosframework/platform/internal/hostops 1.627s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.012s
ok github.com/carlosframework/platform/internal/hostquarantine 0.013s
ok github.com/carlosframework/platform/internal/hostrelocate 0.490s
ok github.com/carlosframework/platform/internal/hostresolve 0.167s
ok github.com/carlosframework/platform/internal/hostwrite 0.012s
ok github.com/carlosframework/platform/internal/infraview 0.012s
ok github.com/carlosframework/platform/internal/instancectl 4.177s
ok github.com/carlosframework/platform/internal/instanceexec 2.773s
ok github.com/carlosframework/platform/internal/instancefs 0.017s
ok github.com/carlosframework/platform/internal/instancekey 0.014s
ok github.com/carlosframework/platform/internal/instanceupdates 0.018s
ok github.com/carlosframework/platform/internal/layout 1.542s
ok github.com/carlosframework/platform/internal/lease 9.821s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.017s
ok github.com/carlosframework/platform/internal/ledger 0.033s
ok github.com/carlosframework/platform/internal/mail 0.228s
ok github.com/carlosframework/platform/internal/maintenance 0.085s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.596s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.101s
ok github.com/carlosframework/platform/internal/metrics 0.005s
ok github.com/carlosframework/platform/internal/moves 21.140s
ok github.com/carlosframework/platform/internal/moves/archive 0.812s
ok github.com/carlosframework/platform/internal/objstore 0.204s
ok github.com/carlosframework/platform/internal/ops 0.204s
ok github.com/carlosframework/platform/internal/park 0.245s
ok github.com/carlosframework/platform/internal/privatefile 0.006s
ok github.com/carlosframework/platform/internal/provision 16.463s
ok github.com/carlosframework/platform/internal/publicrelease 0.025s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 5.393s
ok github.com/carlosframework/platform/internal/relay 7.244s
ok github.com/carlosframework/platform/internal/release 22.448s
ok github.com/carlosframework/platform/internal/rollout 3.793s
ok github.com/carlosframework/platform/internal/rolloutwords 0.003s
ok github.com/carlosframework/platform/internal/rootaws 0.007s
ok github.com/carlosframework/platform/internal/rootparticipation 0.010s
ok github.com/carlosframework/platform/internal/rootstate 0.029s
ok github.com/carlosframework/platform/internal/runs 0.576s
ok github.com/carlosframework/platform/internal/s3compat 0.005s
ok github.com/carlosframework/platform/internal/safefile 0.003s
ok github.com/carlosframework/platform/internal/schedule 2.495s
ok github.com/carlosframework/platform/internal/seeds 0.013s
ok github.com/carlosframework/platform/internal/sim/hostlife 9.966s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.510s
ok github.com/carlosframework/platform/internal/status 0.047s
ok github.com/carlosframework/platform/internal/steering 0.008s
ok github.com/carlosframework/platform/internal/stopevidence 0.293s
ok github.com/carlosframework/platform/internal/svcrole 0.002s
ok github.com/carlosframework/platform/internal/telemetry 0.004s
ok github.com/carlosframework/platform/internal/termfence 0.004s
ok github.com/carlosframework/platform/internal/tunnelclient 0.004s
ok github.com/carlosframework/platform/internal/unixdial 0.002s
ok github.com/carlosframework/platform/internal/updatewords 0.004s
ok github.com/carlosframework/platform/internal/usage 0.004s
ok github.com/carlosframework/platform/internal/version 0.001s
ok github.com/carlosframework/platform/internal/vet 1.552s
=== 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
linux/arm64 carlos binary: 47055010 bytes (ceiling 52000000)
--- 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.024s
=== RUN TestT4StorageProbeMinio
2026/10/15 14:03:31 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DEB8E8B3001658, 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.01s)
PASS
ok github.com/carlosframework/platform/internal/host 0.022s
minio contract: PASS
diff --git a/internal/host/claimretire_transport.go b/internal/host/claimretire_transport.go| index 0061339f2..d53fad314 100644 |
| --- a/internal/host/claimretire_transport.go |
| +++ b/internal/host/claimretire_transport.go |
| @@ -159,6 +159,7 @@ func (h ClaimRootHTTP) retire(ctx context.Context, target hostguard.FenceTarget) |
| a.DataRoot = h.Roots["data"] |
| a.Box = target.Box |
| a.Reconciler.Guard = h.Stop.Coordinator.Guard |
| + a.RequireTimerInventory = a.RequireTimerInventory || a.Reconciler.Guard.Root != nil |
| a.Stop = func(context.Context, registry.Route) (hostguard.FenceTarget, error) { return target, nil } |
| a.Retirement.Filesystem = hostbox.TypedParker{Roots: h.Roots, ParkedRoots: h.Roots, Incarnation: target.Incarnation, Host: target.Host, Retirement: target.FenceID} |
| a.Retirement.Authority = hostbox.InstallationCleanup{Guard: a.Reconciler.Guard, Verify: verifyRetirementReceipt} |
diff --git a/internal/host/plan14_generation_timer_retirement_test.go b/internal/host/plan14_generation_timer_retirement_test.go| index c41e4da8c..8233873aa 100644 |
| --- a/internal/host/plan14_generation_timer_retirement_test.go |
| +++ b/internal/host/plan14_generation_timer_retirement_test.go |
| @@ -11,11 +11,8 @@ import ( |
| func TestPlan14GenerationTimerRetirement(t *testing.T) { |
| t.Setenv("CARLOS_HOST_CLAIM", "true") |
| - for _, mismatch := range []bool{false, true} { |
| - name := "exact" |
| - if mismatch { |
| - name = "replacement" |
| - } |
| + for _, name := range []string{"exact", "replacement", "unrecorded", "legacy-unrecorded"} { |
| + mismatch := name == "replacement" || name == "unrecorded" |
| t.Run(name, func(t *testing.T) { |
| coordinator, req, _ := claimStopFixture(t) |
| result, err := coordinator.FenceAndStop(t.Context(), req) |
| @@ -40,7 +37,7 @@ func TestPlan14GenerationTimerRetirement(t *testing.T) { |
| } |
| timer := registry.Timer{Host: req.Host, Name: "backup", Kind: "cron", Registration: "timer-1", ClaimOwnership: owner} |
| timer.ClaimRowID = "timer-1" |
| - if mismatch { |
| + if name == "replacement" { |
| timer.Registration = "replacement" |
| timer.ClaimRowID = "replacement" |
| } |
| @@ -51,11 +48,14 @@ func TestPlan14GenerationTimerRetirement(t *testing.T) { |
| {Kind: "route", ArtifactID: "route", ManagedRoot: "registry", RelativePath: req.Host, RowIdentity: owner.ClaimRowID, Ownership: owner.ClaimOwner, Materialization: "materialized"}, |
| {Kind: "timer", ArtifactID: "timer", ManagedRoot: "registry", RelativePath: req.Host + "/backup", RowIdentity: "timer-1", Ownership: owner.ClaimOwner, Materialization: "materialized"}, |
| }} |
| + if name == "unrecorded" || name == "legacy-unrecorded" { |
| + in.Entries = in.Entries[:1] |
| + } |
| if err := inventory.WriteStopInventory(t.Context(), in); err != nil { |
| t.Fatal(err) |
| } |
| journal := filepath.Join(base, "retire") |
| - root := ClaimRootHTTP{Stop: ClaimStopHTTP{Coordinator: coordinator}, Roots: map[string]string{"data": base}, Reconcile: hostbox.ReconcileAdapter{Inventory: inventory, Registry: reg, Retirement: hostbox.TypedRetirement{JournalDir: journal}}} |
| + root := ClaimRootHTTP{Stop: ClaimStopHTTP{Coordinator: coordinator}, Roots: map[string]string{"data": base}, Reconcile: hostbox.ReconcileAdapter{RequireTimerInventory: name != "legacy-unrecorded", Inventory: inventory, Registry: reg, Retirement: hostbox.TypedRetirement{JournalDir: journal}}} |
| // Remote acknowledgement is deliberately unavailable. Local completion |
| // must already be durable, and cannot be confused with the remote result. |
| if err := root.retire(t.Context(), result.Target); err == nil { |
| @@ -70,7 +70,7 @@ func TestPlan14GenerationTimerRetirement(t *testing.T) { |
| t.Fatal(err) |
| } |
| if mismatch { |
| - if got == nil || len(timers) != 1 || timers[0].Registration != "replacement" { |
| + if got == nil || len(timers) != 1 || timers[0].Registration != timer.Registration { |
| t.Fatal("replacement changed") |
| } |
| return |
diff --git a/internal/host/plan14_generation_transfer_root_linux_test.go b/internal/host/plan14_generation_transfer_root_linux_test.go| new file mode 100644 |
| index 000000000..ba8080d0e |
| --- /dev/null |
| +++ b/internal/host/plan14_generation_transfer_root_linux_test.go |
| @@ -0,0 +1,164 @@ |
| +//go:build linux && rootrunner |
| + |
| +package host |
| + |
| +import ( |
| + "bufio" |
| + "errors" |
| + "fmt" |
| + "os" |
| + "os/exec" |
| + "path/filepath" |
| + "strconv" |
| + "strings" |
| + "syscall" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/release" |
| + "golang.org/x/sys/unix" |
| +) |
| + |
| +func TestPlan14GenerationDescriptorTransferRootRunner(t *testing.T) { |
| + const modeKey = "PLAN14_E44_TRANSFER" |
| + path := os.Getenv("PLAN14_E44_TRANSFER_PATH") |
| + switch os.Getenv(modeKey) { |
| + case "mount": |
| + if err := unix.Mount("tmpfs", filepath.Dir(path), "tmpfs", 0, "mode=0755"); err != nil { |
| + t.Fatal(err) |
| + } |
| + defer unix.Unmount(filepath.Dir(path), 0) |
| + cmd := exec.Command(os.Args[0], "-test.run=^TestPlan14GenerationDescriptorTransferRootRunner$", "-test.timeout=30s") |
| + cmd.Env = append(os.Environ(), modeKey+"=receive") |
| + cmd.Dir = "/" |
| + cmd.ExtraFiles = []*os.File{os.NewFile(3, "transfer")} |
| + cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr |
| + cmd.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: 65534, Gid: 65534, Groups: []uint32{65534}}} |
| + if err := cmd.Run(); err != nil { |
| + t.Fatal(err) |
| + } |
| + return |
| + case "receive": |
| + if os.Geteuid() != 65534 { |
| + t.Fatal("receiver must be unprivileged") |
| + } |
| + if _, err := os.ReadFile(path); !errors.Is(err, os.ErrNotExist) { |
| + t.Fatal("staged path not masked", err) |
| + } |
| + fmt.Printf("READY %d\n", os.Getpid()) |
| + data, control := make([]byte, 1), make([]byte, unix.CmsgSpace(4)) |
| + _, n, _, _, err := unix.Recvmsg(3, data, control, 0) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + messages, err := unix.ParseSocketControlMessage(control[:n]) |
| + if err != nil || len(messages) != 1 { |
| + t.Fatal("rights message", err) |
| + } |
| + fds, err := unix.ParseUnixRights(&messages[0]) |
| + if err != nil || len(fds) != 1 { |
| + t.Fatal("rights descriptor", err) |
| + } |
| + file := os.NewFile(uintptr(fds[0]), "unreserved") |
| + defer file.Close() |
| + body := make([]byte, 10) |
| + if n, err := file.Read(body); err != nil || string(body[:n]) != "unreserved" { |
| + t.Fatal("received staged bytes", string(body), err) |
| + } |
| + fmt.Printf("HELD %d\n", fds[0]) |
| + if _, err := unix.Read(3, data); err != nil { |
| + t.Fatal(err) |
| + } |
| + return |
| + case "send": |
| + if os.Geteuid() != 65534 { |
| + t.Fatal("sender must be unprivileged") |
| + } |
| + file, err := os.Open(path) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer file.Close() |
| + if err := unix.Sendmsg(3, []byte{1}, unix.UnixRights(int(file.Fd())), nil, 0); err != nil { |
| + t.Fatal(err) |
| + } |
| + return |
| + } |
| + dir := generationRootRunnerDir(t) |
| + body, err := os.ReadFile(os.Args[0]) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + binary := filepath.Join(dir, "transfer.test") |
| + if err := os.WriteFile(binary, body, 0755); err != nil { |
| + t.Fatal(err) |
| + } |
| + path = filepath.Join(dir, "versions", "unreserved") |
| + if err := os.Mkdir(filepath.Dir(path), 0755); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := os.WriteFile(path, []byte("unreserved"), 0644); err != nil { |
| + t.Fatal(err) |
| + } |
| + sockets, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + parent, child := os.NewFile(uintptr(sockets[0]), "parent"), os.NewFile(uintptr(sockets[1]), "child") |
| + defer parent.Close() |
| + defer child.Close() |
| + cmd := exec.CommandContext(t.Context(), "unshare", "--mount", binary, "-test.run=^TestPlan14GenerationDescriptorTransferRootRunner$", "-test.timeout=45s") |
| + cmd.Dir = dir |
| + cmd.Env = append(os.Environ(), modeKey+"=mount", "PLAN14_E44_TRANSFER_PATH="+path) |
| + cmd.ExtraFiles = []*os.File{child} |
| + cmd.Stderr = os.Stderr |
| + output, err := cmd.StdoutPipe() |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := cmd.Start(); err != nil { |
| + t.Fatal(err) |
| + } |
| + defer func() { cmd.Process.Kill(); cmd.Wait() }() |
| + scan := bufio.NewScanner(output) |
| + number := func(prefix string) int { |
| + t.Helper() |
| + for scan.Scan() { |
| + if s, ok := strings.CutPrefix(scan.Text(), prefix); ok { |
| + n, err := strconv.Atoi(s) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + return n |
| + } |
| + t.Log(scan.Text()) |
| + } |
| + t.Fatal("missing child observation", prefix, scan.Err()) |
| + return 0 |
| + } |
| + pid := number("READY ") |
| + sender := exec.CommandContext(t.Context(), binary, "-test.run=^TestPlan14GenerationDescriptorTransferRootRunner$", "-test.timeout=15s") |
| + sender.Dir = dir |
| + sender.Env = append(os.Environ(), modeKey+"=send", "PLAN14_E44_TRANSFER_PATH="+path) |
| + sender.ExtraFiles = []*os.File{parent} |
| + sender.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: 65534, Gid: 65534, Groups: []uint32{65534}}} |
| + if out, err := sender.CombinedOutput(); err != nil { |
| + t.Fatalf("unprivileged sender: %v %s", err, out) |
| + } |
| + fd := number("HELD ") |
| + if err := generationLinkRef(fmt.Sprintf("/proc/%d/fd/%d", pid, fd), release.GenerationBinding{Path: path, Tree: filepath.Dir(path)}); err == nil { |
| + t.Fatal("transferred staged descriptor was missed") |
| + } |
| + t.Setenv("CARLOS_OPT_DIR", dir) |
| + if _, _, err := productionGenerationKernelCensus(t.Context()); err == nil { |
| + t.Fatal("uncontained descriptor recipient certified") |
| + } |
| + if _, err := parent.Write([]byte{1}); err != nil { |
| + t.Fatal(err) |
| + } |
| + for scan.Scan() { |
| + t.Log(scan.Text()) |
| + } |
| + if err := cmd.Wait(); err != nil { |
| + t.Fatal(err) |
| + } |
| +} |
diff --git a/internal/host/plan14_timer_intention_root_linux_test.go b/internal/host/plan14_timer_intention_root_linux_test.go| new file mode 100644 |
| index 000000000..c7d70585a |
| --- /dev/null |
| +++ b/internal/host/plan14_timer_intention_root_linux_test.go |
| @@ -0,0 +1,25 @@ |
| +//go:build linux && rootrunner |
| + |
| +package host |
| + |
| +import ( |
| + "path/filepath" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/rootstate" |
| +) |
| + |
| +func TestPlan14TimerIntentionRootRunner(t *testing.T) { |
| + plan14TimerIntention(t, func(t *testing.T) rootstate.Store { |
| + state, err := rootstate.Open(filepath.Join(generationRootRunnerDir(t), "authority"), true, false) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + return state |
| + }) |
| +} |
| + |
| +func TestPlan14TimerRetirementRootRunner(t *testing.T) { |
| + generationRootRunnerDir(t) |
| + TestPlan14GenerationTimerRetirement(t) |
| +} |
diff --git a/internal/host/plan14_timer_intention_test.go b/internal/host/plan14_timer_intention_test.go| new file mode 100644 |
| index 000000000..5dfa6eaca |
| --- /dev/null |
| +++ b/internal/host/plan14_timer_intention_test.go |
| @@ -0,0 +1,185 @@ |
| +package host |
| + |
| +import ( |
| + "context" |
| + "crypto/sha256" |
| + "database/sql" |
| + "encoding/hex" |
| + "fmt" |
| + "os" |
| + "path/filepath" |
| + "strings" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/hostbox" |
| + "github.com/carlosframework/platform/internal/hostclaim" |
| + "github.com/carlosframework/platform/internal/hostguard" |
| + "github.com/carlosframework/platform/internal/provision" |
| + "github.com/carlosframework/platform/internal/registry" |
| + "github.com/carlosframework/platform/internal/rootstate" |
| +) |
| + |
| +func TestPlan14TimerIntentionBeforeMaterialization(t *testing.T) { |
| + plan14TimerIntention(t, func(t *testing.T) rootstate.Store { return &rollbackDisk{dir: t.TempDir()} }) |
| +} |
| + |
| +type timerSyncFault struct { |
| + rootstate.Store |
| + phase string |
| +} |
| + |
| +func (s *timerSyncFault) Write(name string, body []byte) error { |
| + if err := s.Store.Write(name, body); err != nil { |
| + return err |
| + } |
| + pending := strings.Contains(string(body), `"PendingInstallation"`) |
| + if s.phase == "pending" && pending || s.phase == "complete" && !pending { |
| + s.phase = "" |
| + return os.ErrInvalid |
| + } |
| + return nil |
| +} |
| + |
| +func plan14TimerIntention(t *testing.T, open func(*testing.T) rootstate.Store) { |
| + for _, attack := range []string{"none", "already-materialized", "late-intention", "changed-registration", "changed-row", "omit-intention", "crash-after-intention", "crash-after-row", "intention-sync", "materialization-sync"} { |
| + t.Run(attack, func(t *testing.T) { |
| + t.Setenv("CARLOS_HOST_CLAIM", "1") |
| + f := newRecoveryFixture(t) |
| + o := f.read() |
| + h := o.Claim.Holder |
| + plan14PutRecord(t, f.claims, provision.Record{Schema: 1, Host: o.Host, Account: h.Account, App: h.App, Channel: "stable", CreatedAt: f.now}) |
| + _, etag, _ := f.claims.Get(t.Context(), h.Key) |
| + journals, err := hostbox.NewJournalService(f.journal) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + registryPath := filepath.Join(t.TempDir(), "registry.db") |
| + reg, err := registry.Open(registryPath) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer reg.Close() |
| + sum := sha256.Sum256([]byte(fmt.Sprintf("%s\x00%s\x00%d\x00%s", o.Host, h.Incarnation, h.Stamp, "box-1"))) |
| + owner := registry.ClaimOwnership{ClaimIncarnation: h.Incarnation, ClaimStamp: h.Stamp, ClaimOwner: hex.EncodeToString(sum[:]), ClaimRowID: "route"} |
| + if err := reg.Upsert(registry.Route{Host: o.Host, Account: h.Account, App: h.App, DBPath: provision.NestedDBPath(f.guard.DataDir, o.Host), ClaimOwnership: owner}); err != nil { |
| + t.Fatal(err) |
| + } |
| + in := hostbox.StopInventory{Complete: true, Host: o.Host, Incarnation: h.Incarnation, InstallationStamp: h.Stamp, Box: "box-1", Enrollment: "en", DirectoryRevision: "1", Entries: []hostbox.StopInventoryEntry{{Kind: "route", ArtifactID: "route", ManagedRoot: "registry", RelativePath: o.Host, RowIdentity: owner.ClaimRowID, Ownership: owner.ClaimOwner, Materialization: "materialized"}}} |
| + if err := journals.Inventory.WriteStopInventory(t.Context(), in); err != nil { |
| + t.Fatal(err) |
| + } |
| + g := f.guard |
| + state := &timerSyncFault{Store: open(t)} |
| + defer state.Close() |
| + g.Root = hostguard.NewRootAuthority(state) |
| + if err := g.EstablishRoot(t.Context(), hostguard.RootInstallation{Artifacts: hostguard.InstallationArtifacts{Schema: 1, StateDirectory: true}, Host: o.Host, Incarnation: h.Incarnation, Stamp: h.Stamp, Account: h.Account, App: h.App, Record: h.Key, Box: "box-1", Enrollment: "en", Migration: 1, InventoryRevision: 1}, func(*hostclaim.Claim) error { return nil }); err != nil { |
| + t.Fatal(err) |
| + } |
| + actor := hostbox.Actor{Box: "box-1", Enrollment: "en", Revision: 1} |
| + root := ClaimRootHTTP{Stop: ClaimStopHTTP{Coordinator: ClaimStopCoordinator{Guard: g, Evidence: f.claims}}, Journals: journals, InventoryActor: func(context.Context) (hostbox.Actor, error) { return actor, nil }, Roots: map[string]string{"data": g.DataDir}, Reconcile: hostbox.ReconcileAdapter{Registry: reg, Census: &hostbox.EmptyInstallationCensus{}}} |
| + var last hostbox.JournalRequest |
| + _, client := hostbox.RemoteJournals(func(ctx context.Context, q hostbox.JournalRequest) (hostbox.JournalResponse, error) { |
| + last = q |
| + return root.commitRootInventory(ctx, q) |
| + }) |
| + expect := hostbox.InventoryExpectation{Revision: 1, RecordKey: h.Key, RecordETag: etag, Target: hostguard.FenceTarget{Host: o.Host, Incarnation: h.Incarnation, Stamp: h.Stamp, Session: h.Session, Box: actor.Box, Enrollment: actor.Enrollment, ClaimETag: o.ETag, Op: o.Claim.Op.ID, Phase: o.Claim.Op.Phase}} |
| + write := func() error { |
| + _, err := client.WriteExpected(t.Context(), in, expect) |
| + if err == nil { |
| + expect.Revision++ |
| + } |
| + return err |
| + } |
| + timer := registry.Timer{Host: o.Host, Name: "daily", Kind: "schedule", Spec: "every 1h0m0s", Path: "/daily", Registration: "planned", ClaimOwnership: owner} |
| + timer.ClaimRowID = timer.Registration |
| + row := func() { |
| + t.Helper() |
| + if err := reg.ReplaceTimers(o.Host, []registry.Timer{timer}); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + in.Entries = append(in.Entries, hostbox.StopInventoryEntry{Kind: "timer", ArtifactID: "timer", ManagedRoot: "registry", RelativePath: o.Host + "/daily", RowIdentity: timer.Registration, Ownership: owner.ClaimOwner, Materialization: "intended"}) |
| + if attack == "already-materialized" || attack == "late-intention" { |
| + row() |
| + if attack == "already-materialized" { |
| + in.Entries[1].Materialization = "materialized" |
| + } |
| + if err := write(); err == nil { |
| + t.Fatal("adopted timer without prior root intention") |
| + } |
| + return |
| + } |
| + if attack == "intention-sync" { |
| + state.phase = "pending" |
| + if err := write(); err == nil { |
| + t.Fatal("acknowledged unsynced intention") |
| + } |
| + if err := root.recoverRootInventory(t.Context()); err != nil { |
| + t.Fatal("recover intended write", err) |
| + } |
| + if _, err := root.commitRootInventory(t.Context(), last); err != nil { |
| + t.Fatal("replay recovered intention", err) |
| + } |
| + expect.Revision++ |
| + } else if err := write(); err != nil { |
| + t.Fatal("record intention", err) |
| + } |
| + if attack == "crash-after-intention" { |
| + if err := write(); err != nil { |
| + t.Fatal("resume absent intended row", err) |
| + } |
| + } |
| + row() |
| + if attack == "crash-after-row" { |
| + if err := write(); err != nil { |
| + t.Fatal("resume intended exact row", err) |
| + } |
| + } |
| + in.Entries[1].Materialization = "materialized" |
| + if attack == "materialization-sync" { |
| + state.phase = "complete" |
| + } |
| + switch attack { |
| + case "changed-registration", "changed-row": |
| + db, err := sql.Open("sqlite", registryPath) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + column := "registration" |
| + if attack == "changed-row" { |
| + column = "claim_row_id" |
| + } |
| + _, err = db.Exec("UPDATE timers SET "+column+"='successor' WHERE host=? AND name=?", o.Host, timer.Name) |
| + db.Close() |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + case "omit-intention": |
| + in.Entries = in.Entries[:1] |
| + } |
| + err = write() |
| + if attack == "materialization-sync" { |
| + if err == nil { |
| + t.Fatal("acknowledged unsynced materialization") |
| + } |
| + _, err = root.commitRootInventory(t.Context(), last) |
| + if err == nil { |
| + expect.Revision++ |
| + } |
| + } |
| + valid := attack == "none" || attack == "crash-after-intention" || attack == "crash-after-row" || attack == "intention-sync" || attack == "materialization-sync" |
| + if (err == nil) != valid { |
| + t.Fatalf("materialization valid=%t: %v", valid, err) |
| + } |
| + if valid { |
| + if err := write(); err != nil { |
| + t.Fatal("materialized replay", err) |
| + } |
| + got, err := journals.Inventory.ReadStopInventory(t.Context(), o.Host) |
| + if err != nil || len(got.Entries) != 2 || got.Entries[1] != in.Entries[1] { |
| + t.Fatal("lost exact timer", got, err) |
| + } |
| + } |
| + }) |
| + } |
| +} |
diff --git a/internal/host/rootinventory.go b/internal/host/rootinventory.go| index f77be991e..d226e0cdf 100644 |
| --- a/internal/host/rootinventory.go |
| +++ b/internal/host/rootinventory.go |
| @@ -231,6 +231,9 @@ func (h ClaimRootHTTP) commitRootInventoryPending(ctx context.Context, q hostbox |
| if revision != q.Revision { |
| return hostbox.ErrChanged |
| } |
| + if err := h.verifyNewTimerIntentions(prior, in); err != nil { |
| + return err |
| + } |
| if revision == 0 { |
| if (!firstLanding && len(in.Entries) != 0) || rootFirstInstallation(observed.Claim, actor) != nil { |
| return hostbox.ErrChanged |
diff --git a/internal/host/rootinventory_timer.go b/internal/host/rootinventory_timer.go| index f949ef1da..39f178e38 100644 |
| --- a/internal/host/rootinventory_timer.go |
| +++ b/internal/host/rootinventory_timer.go |
| @@ -26,8 +26,37 @@ func (h ClaimRootHTTP) verifyInventoryTimer(host string, owner registry.ClaimOwn |
| } |
| return nil |
| } |
| - if (e.Materialization != "intended" && e.Materialization != "materialized") || timer.Host != host || timer.Name != name || timer.ClaimIncarnation != owner.ClaimIncarnation || timer.ClaimStamp != owner.ClaimStamp || timer.ClaimOwner != e.Ownership || timer.ClaimRowID != e.RowIdentity { |
| + if (e.Materialization != "intended" && e.Materialization != "materialized") || timer.Host != host || timer.Name != name || timer.ClaimIncarnation != owner.ClaimIncarnation || timer.ClaimStamp != owner.ClaimStamp || timer.ClaimOwner != e.Ownership || timer.ClaimRowID != e.RowIdentity || timer.Registration != e.RowIdentity { |
| return hostbox.ErrTypedInventory |
| } |
| return nil |
| } |
| + |
| +func (h ClaimRootHTTP) verifyNewTimerIntentions(prior, next hostbox.StopInventory) error { |
| + for _, e := range next.Entries { |
| + if e.Kind != "timer" { |
| + continue |
| + } |
| + known := false |
| + for _, old := range prior.Entries { |
| + if old.Kind == "timer" && old.ArtifactID == e.ArtifactID { |
| + known = true |
| + break |
| + } |
| + } |
| + if known { |
| + continue |
| + } |
| + if e.Materialization != "intended" { |
| + return hostbox.ErrTypedInventory |
| + } |
| + row, err := h.Reconcile.Registry.GetTimer(next.Host, strings.TrimPrefix(e.RelativePath, next.Host+"/")) |
| + if err != nil && !errors.Is(err, os.ErrNotExist) { |
| + return err |
| + } |
| + if row != nil { |
| + return hostbox.ErrTypedInventory |
| + } |
| + } |
| + return nil |
| +} |
diff --git a/internal/host/rootinventory_timer_test.go b/internal/host/rootinventory_timer_test.go| index 4721c895b..4e195d950 100644 |
| --- a/internal/host/rootinventory_timer_test.go |
| +++ b/internal/host/rootinventory_timer_test.go |
| @@ -15,7 +15,7 @@ import ( |
| ) |
| func TestPlan14RootTimerInventory(t *testing.T) { |
| - for _, attack := range []string{"none", "intended", "missing", "omitted-timer", "row identity", "owner", "path", "root", "shape"} { |
| + for _, attack := range []string{"unrecorded", "intended", "missing", "omitted-timer", "row identity", "owner", "path", "root", "shape"} { |
| t.Run(attack, func(t *testing.T) { |
| t.Setenv("CARLOS_HOST_CLAIM", "1") |
| f := newRecoveryFixture(t) |
| @@ -80,7 +80,7 @@ func TestPlan14RootTimerInventory(t *testing.T) { |
| } |
| _, client := hostbox.RemoteJournals(root.commitRootInventory) |
| _, err = client.WriteExpected(t.Context(), in, hostbox.InventoryExpectation{Revision: 1, RecordKey: h.Key, RecordETag: etag, Target: hostguard.FenceTarget{Host: o.Host, Incarnation: h.Incarnation, Stamp: h.Stamp, Session: h.Session, Box: actor.Box, Enrollment: actor.Enrollment, ClaimETag: o.ETag, Op: o.Claim.Op.ID, Phase: o.Claim.Op.Phase}}) |
| - valid := attack == "none" || attack == "intended" |
| + valid := attack == "intended" |
| if valid && err != nil || !valid && err == nil { |
| t.Fatal(attack, err) |
| } |
diff --git a/internal/hostbox/reconcile_adapter.go b/internal/hostbox/reconcile_adapter.go| index fd85509ee..51826df67 100644 |
| --- a/internal/hostbox/reconcile_adapter.go |
| +++ b/internal/hostbox/reconcile_adapter.go |
| @@ -22,6 +22,7 @@ import ( |
| ) |
| type ReconcileAdapter struct { |
| + RequireTimerInventory bool |
| DataRoot string |
| RemoteRetire func(context.Context, hostguard.FenceTarget) error |
| Census *EmptyInstallationCensus |
| @@ -335,7 +336,7 @@ func (a ReconcileAdapter) Retire(ctx context.Context, row registry.Route) error |
| timers = append(timers, x) |
| } |
| } |
| - combined, err := retirementWithTimers(inventory, timers) |
| + combined, err := retirementWithTimers(inventory, timers, a.RequireTimerInventory) |
| if err != nil { |
| return err |
| } |
| @@ -359,7 +360,7 @@ func (a ReconcileAdapter) Retire(ctx context.Context, row registry.Route) error |
| if t.ClaimIncarnation != row.ClaimIncarnation || t.ClaimStamp != row.ClaimStamp || t.ClaimOwner != row.ClaimOwner || t.ClaimRowID == "" || t.Registration == "" { |
| return ErrTypedInventory |
| } |
| - inventory, err = retirementWithTimers(inventory, []Artifact{{Kind: ArtifactTimer, Root: "registry", Name: row.Host + "/" + t.Name, Owner: row.ClaimOwner, RowID: t.ClaimRowID + "~" + t.Registration}}) |
| + inventory, err = retirementWithTimers(inventory, []Artifact{{Kind: ArtifactTimer, Root: "registry", Name: row.Host + "/" + t.Name, Owner: row.ClaimOwner, RowID: t.ClaimRowID + "~" + t.Registration}}, a.RequireTimerInventory) |
| if err != nil { |
| return err |
| } |
diff --git a/internal/hostbox/retirement_timers.go b/internal/hostbox/retirement_timers.go| index 2e3019361..aa3ac34db 100644 |
| --- a/internal/hostbox/retirement_timers.go |
| +++ b/internal/hostbox/retirement_timers.go |
| @@ -2,7 +2,7 @@ package hostbox |
| import "strings" |
| -func retirementWithTimers(in RetirementInventory, timers []Artifact) (RetirementInventory, error) { |
| +func retirementWithTimers(in RetirementInventory, timers []Artifact, requireInventory bool) (RetirementInventory, error) { |
| in.Artifacts = append([]Artifact(nil), in.Artifacts...) |
| for _, timer := range timers { |
| found := false |
| @@ -20,6 +20,9 @@ func retirementWithTimers(in RetirementInventory, timers []Artifact) (Retirement |
| found = true |
| } |
| if !found { |
| + if requireInventory { |
| + return RetirementInventory{}, ErrTypedInventory |
| + } |
| in.Artifacts = append(in.Artifacts, timer) |
| } |
| } |
diff --git a/internal/provision/plan14_e_write_census_test.go b/internal/provision/plan14_e_write_census_test.go| index 1f9a0a00b..d301ee7e7 100644 |
| --- a/internal/provision/plan14_e_write_census_test.go |
| +++ b/internal/provision/plan14_e_write_census_test.go |
| @@ -6,7 +6,7 @@ var plan14EWriteCensusOverlay = []censusEntry{ |
| {"internal/host/rootgeneration_inventory.go", "generationReconciler.reconcile", "key-site", "key:RecordKey", "f3bcd466fa3bc92b50f93f6986b4c566db198247ef0e37fe6daf3e2fee760a92"}, |
| {"internal/host/rootgenerations_launch.go", "rootGenerationSource.resolve", "key-site", "key:RecordKey", "d69a3c0c86b03d5469708ac8bf084f4f516c0b61a18519d46cc3c0a0616f5381"}, |
| {"internal/historyreadback/workflow.go", "Operation.deliverGrant", "nonrecord", "sel:Put", "fb410ef6274ef7de0997a804c837ad1ecf29d8826ba1c4dcda00c2ae986fc434"}, |
| - {"internal/host/rootinventory.go", "ClaimRootHTTP.commitRootInventoryPending", "key-site", "key:RecordKey", "12b08da25b91b0827412cfe0119bdcc9a98bda5a60c56a987e0d44f7016d3bab"}, |
| + {"internal/host/rootinventory.go", "ClaimRootHTTP.commitRootInventoryPending", "key-site", "key:RecordKey", "b08d3c55f02e0b9e9c39455ce37f854a254c5f65af9fc74443fee414e2756f1b"}, |
| {"internal/host/rootinventory.go", "ClaimRootHTTP.recoverRootInventory", "key-site", "key:RecordKey", "a828684ad31a409d3e1fc306216d907681f514cb813302eb965a040e5084feda"}, |
| {"internal/host/roothistory_creation.go", "rootRestartRequestSource.creation", "key-site", "key:RecordKey", "af79a66b746b0f42388010b38ea8130dca6a8d29c4ba5623bfc9c3cc40dd9601"}, |
| {"internal/host/rootrestart_requests.go", "rootRestartRequests.consume", "key-site", "key:RecordKey", "0a9fbbe6f201077e276298b7e666e1ee2a345f1c420335290fec38a676b1ad84"}, |
diff --git a/internal/provision/testdata/plan14-root-reads-e.tsv b/internal/provision/testdata/plan14-root-reads-e.tsv| index 1a33c0ac4..3e02081d0 100644 |
| --- a/internal/provision/testdata/plan14-root-reads-e.tsv |
| +++ b/internal/provision/testdata/plan14-root-reads-e.tsv |
| @@ -54,7 +54,7 @@ internal/release/roothistory_replace.go replacementReconstruction authority E1 S |
| internal/release/signedhead.go StageSignedHead authority E1 Canonical route is supplied by the root unit verifier. Verifies signed channel, digest ratchet and manifest/artifact before staging; unavailable pin census never authorizes restaging differing bytes. Fixtures: TestPlan14Incident20261006WithLedger and TestPlan14SignedNamedRecoveryAuthority. registry:Account@202,registry:Account@302,registry:Account@403,registry:App@214,registry:App@415,registry:Channel@222,registry:Host@434 ca02f04e55556ba0233dfa0848b0c533a9d9aa06cad6abd33cfd2968b3bd838a |
| internal/rootstate/link_linux.go ReadTrustedLink authority A,E1 Checks the full root-owned parent chain before descriptor-based live-link reading. Fixture: TestPlan14TrustedLiveLink; privileged acceptance remains required. file:Open@156 90f8e8de4e2c71f07b5d749cbe4bb1c1dfa426576a82138823449f56fc9ec5ab |
| internal/rootstate/link_linux.go directory.readLink authority A,E1 Pins the symlink inode with O_PATH and checks owner/type/link count before readlinkat. Fixture: TestPlan14TrustedLiveLink. file:Fstat@356,file:Openat@183,file:Readlinkat@554 a8559fe63dce48830916004ecfe7fbbe5d419568c7daf7a3073cf1b5bb12c3d7 |
| -@coverage internal/host 826ec7eaf12152b9a877635e7a73892edaf102da8f09b0cd155b441a52d2e544 |
| +@coverage internal/host b5495e98a2ccc36bbb3e242aabfc623afa5a5ef248f778604c23607c002b88b0 |
| @coverage internal/release 23bf042522655b27434395d03f05f6ab5e0d86025f58431b842d286a58bda0b7 |
| @coverage internal/rootstate a773d6dbc0e4150e0054f7a94d0e1493c88d0d57f4f4530138b3427e2c217bc5 |
| internal/release/roothistory_status.go RootHistory.MigrationStatus authority E1 Exact completion binds the accepted authorization digest from durable private history to enrolled-root signed readback. Pending originals, changed evidence and canonical sources cannot supply retirement completion. Fixtures: TestPlan14HistoryReconstructionCompletion, TestPlan14HistoryRetirementCompletion, TestPlan14ReadReconstructionCompletion, TestPlan14ReadRetirementCompletion, TestPlan14RecoveryRetirementProductionCallback, TestPlan14RootHistoryReconstructionCrash, TestPlan14HistoryRetirementProcessCrash. file:Read@1062,file:Read@3891 f0ab7ad2d2d70f8c22ebfd575f5a1c6931f7eba30ccdba80ad43d0efffc88154 |
| @@ -167,7 +167,7 @@ internal/host/roothistory_creation.go rootHistoryInstallation authority E2 E37: |
| internal/host/roothistory_creation.go rootRestartRequestSource.creation authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. registry:Account@219,registry:App@243,registry:Host@133,registry:Host@194 af79a66b746b0f42388010b38ea8130dca6a8d29c4ba5623bfc9c3cc40dd9601 |
| internal/host/roothistory_creation.go type rootHistoryAccess authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. 9c97bcd7878d79022ed942d8e6c00e944a00e0fa27a1bc5c732b4f4c10f4e9dd |
| internal/host/roothistory_launch.go ClaimLaunchRoot.rootInstalledProgram authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. file:OpenFile@541,file:Read@1043 e8d530dd1b8f613dfbd41d34a1b08b19aa3f74e0522c8f41db768ccd4e9e80d2 |
| -internal/host/rootinventory.go ClaimRootHTTP.commitRootInventoryPending authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. file:Read@1383,file:Read@3423,registry:Account@4787,registry:Account@4802,registry:App@4823,registry:App@4834,registry:ClaimIncarnation@4293,registry:ClaimOwner@4343,registry:ClaimOwner@5398,registry:ClaimOwnership@4885,registry:ClaimOwnership@4907,registry:ClaimOwnership@5712,registry:ClaimOwnership@7713,registry:ClaimRowID@4507,registry:ClaimRowID@4584,registry:ClaimStamp@4321,registry:DBPath@4851,registry:DBPath@4865,registry:Host@4757,registry:Host@4769,whole:ClaimOwnership@4889,whole:ClaimOwnership@4917,whole:ClaimOwnership@5722,whole:ClaimOwnership@7724,whole:Route@3615,whole:Route@3998,whole:Route@4014,whole:Route@4069,whole:Route@4720,whole:Route@4733,whole:Route@4742,whole:Route@4980,whole:Route@5212,whole:Route@5960,whole:Route@6096,whole:Route@6320,whole:Route@7557,whole:Route@8964,whole:Route@9025,whole:Route@9565 12b08da25b91b0827412cfe0119bdcc9a98bda5a60c56a987e0d44f7016d3bab |
| +internal/host/rootinventory.go ClaimRootHTTP.commitRootInventoryPending authority E2 PROPOSED E44: root timer intention must precede materialization; exact registration and row identity required; root retirement cannot infer ownership from registry enumeration. Tests: TestPlan14TimerIntentionBeforeMaterialization, TestPlan14GenerationTimerRetirement; root timer intention, sync-failure retry and retirement acceptance. Scheduler producer wiring remains incomplete. file:Read@1383,file:Read@3423,registry:Account@4787,registry:Account@4802,registry:App@4823,registry:App@4834,registry:ClaimIncarnation@4293,registry:ClaimOwner@4343,registry:ClaimOwner@5398,registry:ClaimOwnership@4885,registry:ClaimOwnership@4907,registry:ClaimOwnership@5712,registry:ClaimOwnership@7795,registry:ClaimRowID@4507,registry:ClaimRowID@4584,registry:ClaimStamp@4321,registry:DBPath@4851,registry:DBPath@4865,registry:Host@4757,registry:Host@4769,whole:ClaimOwnership@4889,whole:ClaimOwnership@4917,whole:ClaimOwnership@5722,whole:ClaimOwnership@7806,whole:Route@3615,whole:Route@3998,whole:Route@4014,whole:Route@4069,whole:Route@4720,whole:Route@4733,whole:Route@4742,whole:Route@4980,whole:Route@5212,whole:Route@5960,whole:Route@6096,whole:Route@6320,whole:Route@7639,whole:Route@9046,whole:Route@9107,whole:Route@9647 b08d3c55f02e0b9e9c39455ce37f854a254c5f65af9fc74443fee414e2756f1b |
| internal/release/roothistory_creation.go RootHistory.ConsumeCreation authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. 7ec65199b5afc894ae4aefded6a40f485086d0f8a4c86391eca193f473dca2a1 |
| internal/release/roothistory_creation.go RootHistory.FreshInstallation authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. 71b326a36350139c9c592b0b5b4b91409831fcffdd0c61d68cf5ec2bf54bbd5b |
| internal/release/roothistory_creation.go RootHistory.RecordCreation authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. 0b545ab63902eb8f2c8988991786c964a2af53d65eff46d8e53a0747da492b6d |
| @@ -295,7 +295,7 @@ internal/host/rootgeneration_census_linux.go generationKernel.census authority E |
| internal/host/rootgeneration_inventory.go generationInventoryAt authority E2 E26: checked read-only payload enumeration includes inventory and revision orphans; completeness binds current and retained predecessor inventories, exact current revision and both source reads. Fixtures: TestPlan14GenerationPayloadCompleteness, TestPlan14GenerationPayloadRetainedPredecessor, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationPayloadInventory, TestPlan14GenerationPayloadReaderDoesNotInitialize. Checked root reader acceptance passed at E27; running-tenant containment remains unfinished. PROPOSED E43: root; release-held context and D3 reservation/census verification under release-before-participation exclusion; no-census effects stay on the legacy path. TestPlan14LaunchVerifierToken, TestPlan14LaunchVerifierCallerOrder, TestPlan14LaunchVerifierNoCensus, TestPlan14LaunchVerifierRootRunner. file:Open@173,file:Open@533 57f4c32cd65afcefad93a41d5266e2b12acc624ff808f47d40e31a79bbc7a827 |
| internal/hostbox/generationinventory.go LaunchJournal.GenerationLaunches authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel witnesses passed on the E27 root runner. E29 physical-payload and installed-unit witnesses passed on the controller runner; E30 required-artifact witness pending. file:QueryContext@270 6694bd3a35b8d91f53e67134145c44854086f08593110ed1b905bca9587be290 |
| internal/hostbox/generationinventory.go OpenGenerationLaunchReader authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel witnesses passed on the E27 root runner. E29 physical-payload and installed-unit witnesses passed on the controller runner; E30 required-artifact witness pending. file:Lstat@218,file:Open@283 552d8629a7932c4ae5dca9d2beef4559f7e8a2864259d11e2ff98c699fdcd999 |
| -@coverage internal/hostbox 3b30e41161008d1ae3f1176da62f07c2f770da3e717e437b2f0bdc44fca7869d |
| +@coverage internal/hostbox c3d05fdb9b9e5ed56d29df815e73bd857e8a2a6f31168deecf628c0bf82ccf1a |
| internal/host/rootgeneration_census_linux.go productionGenerationKernelCensus authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel witnesses passed on the E27 root runner. E29 physical-payload and installed-unit witnesses passed on the controller runner; E30 required-artifact witness pending. 85f085029e0cd557c8ff8eb2d522fdf131c388229c14515fda0f45453d40eebb |
| internal/host/rootgeneration_census_other.go productionGenerationKernelCensus authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel witnesses passed on the E27 root runner. E29 physical-payload and installed-unit witnesses passed on the controller runner; E30 required-artifact witness pending. a1217cf881e9d05336fc16d6050f7f7a1a2184a0c20ff2988d0cf0f9012eaa45 |
| internal/host/rootgeneration_inventory.go generationInstallation authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel witnesses passed on the E27 root runner. E29 physical-payload and installed-unit witnesses passed on the controller runner; E30 required-artifact witness pending. 3e5e15d50752510acb44b10eb29621757a87bff54ceae015f704dd7f3a848ecb |
| @@ -341,8 +341,8 @@ internal/host/rootgeneration_parked.go generationReadRetirements authority E2 E3 |
| internal/host/rootgeneration_parked.go generationRetirementRecords authority E2 E38: reconcile completed root retirement against the retained stop receipt and exact inventory; inspect parked identities, require retired rows absent or an independently inventoried successor, and retain generation pins. Fixtures: TestPlan14GenerationParkedPayload, TestPlan14GenerationParkedCrash, TestPlan14GenerationParkedKeepsReservation, TestPlan14GenerationParkedReadback; TestPlan14GenerationParkedRootRunner is runner-pending. Timer producer matches retained entries once and preserves successors on replay: TestPlan14GenerationTimerRetirement. No claims-off path changes. file:Read@707 02eca3d34294974c09e93199efb37a223e7615eaa32faffdafb982b5d4ba53e7 |
| internal/host/rootgeneration_parked.go generationRetirements authority E2 E38: reconcile completed root retirement against the retained stop receipt and exact inventory; inspect parked identities, require retired rows absent or an independently inventoried successor, and retain generation pins. Fixtures: TestPlan14GenerationParkedPayload, TestPlan14GenerationParkedCrash, TestPlan14GenerationParkedKeepsReservation, TestPlan14GenerationParkedReadback; TestPlan14GenerationParkedRootRunner is runner-pending. Timer producer matches retained entries once and preserves successors on replay: TestPlan14GenerationTimerRetirement. No claims-off path changes. be9fd54e443994c423e0e315637b8cf05a361cbb32913b9bc9f891fbb65ad59f |
| internal/host/rootgeneration_parked.go productionGenerationRetirements authority E2 E38: reconcile completed root retirement against the retained stop receipt and exact inventory; inspect parked identities, require retired rows absent or an independently inventoried successor, and retain generation pins. Fixtures: TestPlan14GenerationParkedPayload, TestPlan14GenerationParkedCrash, TestPlan14GenerationParkedKeepsReservation, TestPlan14GenerationParkedReadback; TestPlan14GenerationParkedRootRunner is runner-pending. Timer producer matches retained entries once and preserves successors on replay: TestPlan14GenerationTimerRetirement. No claims-off path changes. file:Open@221,file:Open@306 fe83ff81bb9966f50de7fb3a049b8acd8e87f5d86346809090fc46b5c1e65189 |
| -internal/hostbox/reconcile_adapter.go ReconcileAdapter.Retire authority C,E2 E38: reconcile completed root retirement against the retained stop receipt and exact inventory; inspect parked identities, require retired rows absent or an independently inventoried successor, and retain generation pins. Fixtures: TestPlan14GenerationParkedPayload, TestPlan14GenerationParkedCrash, TestPlan14GenerationParkedKeepsReservation, TestPlan14GenerationParkedReadback; TestPlan14GenerationParkedRootRunner is runner-pending. Timer producer matches retained entries once and preserves successors on replay: TestPlan14GenerationTimerRetirement. No claims-off path changes. registry:ClaimIncarnation@1309,registry:ClaimIncarnation@322,registry:ClaimIncarnation@3392,registry:ClaimIncarnation@3414,registry:ClaimOwner@1366,registry:ClaimOwner@1434,registry:ClaimOwner@2190,registry:ClaimOwner@2596,registry:ClaimOwner@3472,registry:ClaimOwner@3488,registry:ClaimOwner@3726,registry:ClaimOwnership@147,registry:ClaimRowID@3506,registry:ClaimRowID@3749,registry:ClaimStamp@1338,registry:ClaimStamp@3438,registry:ClaimStamp@3454,registry:ClaimStamp@362,registry:Host@1257,registry:Host@2095,registry:Host@2641,registry:Host@2698,registry:Host@288,registry:Host@3311,registry:Host@3694,registry:Name@3711,registry:Registration@3528,registry:Registration@3770,whole:ClaimOwnership@151,whole:Route@1069,whole:Route@1152,whole:Route@231,whole:Timer@3377 bbee00fbe7b9ae791cc78fab1a06d79945b475b3a5e31d082ae37edd9c69f611 |
| -internal/hostbox/retirement_timers.go retirementWithTimers authority C,E2 E38: reconcile completed root retirement against the retained stop receipt and exact inventory; inspect parked identities, require retired rows absent or an independently inventoried successor, and retain generation pins. Fixtures: TestPlan14GenerationParkedPayload, TestPlan14GenerationParkedCrash, TestPlan14GenerationParkedKeepsReservation, TestPlan14GenerationParkedReadback; TestPlan14GenerationParkedRootRunner is runner-pending. Timer producer matches retained entries once and preserves successors on replay: TestPlan14GenerationTimerRetirement. No claims-off path changes. b4c4d4bcf2b3de23af676655cc4aa2901c7c0fd9a189d906fbffc81cd7d1bbf7 |
| +internal/hostbox/reconcile_adapter.go ReconcileAdapter.Retire authority C,E2 PROPOSED E44: root timer intention must precede materialization; exact registration and row identity required; root retirement cannot infer ownership from registry enumeration. Tests: TestPlan14TimerIntentionBeforeMaterialization, TestPlan14GenerationTimerRetirement; root timer intention, sync-failure retry and retirement acceptance. Scheduler producer wiring remains incomplete. registry:ClaimIncarnation@1309,registry:ClaimIncarnation@322,registry:ClaimIncarnation@3417,registry:ClaimIncarnation@3439,registry:ClaimOwner@1366,registry:ClaimOwner@1434,registry:ClaimOwner@2190,registry:ClaimOwner@2596,registry:ClaimOwner@3497,registry:ClaimOwner@3513,registry:ClaimOwner@3751,registry:ClaimOwnership@147,registry:ClaimRowID@3531,registry:ClaimRowID@3774,registry:ClaimStamp@1338,registry:ClaimStamp@3463,registry:ClaimStamp@3479,registry:ClaimStamp@362,registry:Host@1257,registry:Host@2095,registry:Host@2641,registry:Host@2698,registry:Host@288,registry:Host@3336,registry:Host@3719,registry:Name@3736,registry:Registration@3553,registry:Registration@3795,whole:ClaimOwnership@151,whole:Route@1069,whole:Route@1152,whole:Route@231,whole:Timer@3402 42d0e63edc29cf8a83f8265fb1b0260a88334a7ebc3f7b9530d938d9ffa5e4f1 |
| +internal/hostbox/retirement_timers.go retirementWithTimers authority C,E2 PROPOSED E44: root timer intention must precede materialization; exact registration and row identity required; root retirement cannot infer ownership from registry enumeration. Tests: TestPlan14TimerIntentionBeforeMaterialization, TestPlan14GenerationTimerRetirement; root timer intention, sync-failure retry and retirement acceptance. Scheduler producer wiring remains incomplete. 67060d1e6077a5cb6a15ea2749e070e97f830177987570037564916f5ed9bda0 |
| internal/activator/activator.go Service.respawnWorkerLocked authority E2 E39: close the unsupported claimed worker path before backend entry, replacement or supervised stop. Claims-off paths remain unchanged. This is refusal coverage, not positive root-reserved worker launch support. Fixtures: TestPlan14GenerationWorkerStartBoundary, TestPlan14GenerationWorkerRealBackendRefusal, TestPlan14GenerationWorkerReplacementBoundary, TestPlan14GenerationWorkerSupervisionBoundary. Pre-flight 29 and E39 worker launch audit. whole:Route@11828,whole:Route@13334,whole:Route@360,whole:Route@4164,whole:Route@6157,whole:Route@6338 706f2a4268a06dae2eca22093f841750139c5bd9dcaa0e91657621c5eea1bbe9 |
| internal/activator/workerbuild.go Service.startWorker authority E2 E39: close the unsupported claimed worker path before backend entry, replacement or supervised stop. Claims-off paths remain unchanged. This is refusal coverage, not positive root-reserved worker launch support. Fixtures: TestPlan14GenerationWorkerStartBoundary, TestPlan14GenerationWorkerRealBackendRefusal, TestPlan14GenerationWorkerReplacementBoundary, TestPlan14GenerationWorkerSupervisionBoundary. Pre-flight 29 and E39 worker launch audit. registry:Host@1201,registry:Host@1379,registry:Host@2314,whole:Route@2420,whole:Route@931 be27c7d049a6009a8a22798569e457f5126f14d5e7c56aafb5b1bd260717f6f5 |
| internal/activator/workerswap.go Service.ReplaceWorker authority E2 E39: close the unsupported claimed worker path before backend entry, replacement or supervised stop. Claims-off paths remain unchanged. This is refusal coverage, not positive root-reserved worker launch support. Fixtures: TestPlan14GenerationWorkerStartBoundary, TestPlan14GenerationWorkerRealBackendRefusal, TestPlan14GenerationWorkerReplacementBoundary, TestPlan14GenerationWorkerSupervisionBoundary. Pre-flight 29 and E39 worker launch audit. whole:Route@2163 2666d54bdc876904d9c2c3d6de5c2f82dc688f07082d92d84ddfc7e01cda6a7e |
| @@ -373,3 +373,6 @@ internal/host/rootlaunchverifier.go rootLaunchVerifier.verify authority E2 PROPO |
| internal/rootparticipation/production.go Ledger.ActivationReady authority E2 PROPOSED E43: root; release-held context and D3 reservation/census verification under release-before-participation exclusion; no-census effects stay on the legacy path. TestPlan14LaunchVerifierToken, TestPlan14LaunchVerifierCallerOrder, TestPlan14LaunchVerifierNoCensus, TestPlan14LaunchVerifierRootRunner. 99a3a8f61a8b769da5bce734d6d8a937b861ed38ef1e4f9cc1eb2b4d473286fb |
| @coverage internal/hostfloor 37459f6d8d47ed5ec3892e22483e34abde2dabc0775cf0846b6fe00fb66dcd75 |
| @coverage internal/rootparticipation 24ff9d273269e9be6c54794c23f1183ce0535c253e714158e34a9bca9e3fdd81 |
| +internal/host/claimretire_transport.go ClaimRootHTTP.retire authority C,G,J1 PROPOSED E44: root timer intention must precede materialization; exact registration and row identity required; root retirement cannot infer ownership from registry enumeration. Tests: TestPlan14TimerIntentionBeforeMaterialization, TestPlan14GenerationTimerRetirement; root timer intention, sync-failure retry and retirement acceptance. Scheduler producer wiring remains incomplete. registry:ClaimOwnership@1081,registry:Host@1062,registry:Host@792,whole:ClaimOwnership@1085,whole:ClaimOwnership@894,whole:Route@1830,whole:Route@854 425059d65adaafa5680ad92974559fbeb56ba0a3a7ff3867bb63937648989e06 |
| +internal/host/rootinventory_timer.go ClaimRootHTTP.verifyInventoryTimer authority C,J1 PROPOSED E44: root timer intention must precede materialization; exact registration and row identity required; root retirement cannot infer ownership from registry enumeration. Tests: TestPlan14TimerIntentionBeforeMaterialization, TestPlan14GenerationTimerRetirement; root timer intention, sync-failure retry and retirement acceptance. Scheduler producer wiring remains incomplete. registry:ClaimIncarnation@943,registry:ClaimIncarnation@969,registry:ClaimOwner@1035,registry:ClaimOwner@299,registry:ClaimRowID@1070,registry:ClaimStamp@1015,registry:ClaimStamp@995,registry:Host@899,registry:Name@921,registry:Registration@1107,whole:Timer@709 f54f9aeaf48b8a258b9102d630719552a7e3ff495a53ab53123b79420723365f |
| +internal/host/rootinventory_timer.go ClaimRootHTTP.verifyNewTimerIntentions authority E2 PROPOSED E44: root timer intention must precede materialization; exact registration and row identity required; root retirement cannot infer ownership from registry enumeration. Tests: TestPlan14TimerIntentionBeforeMaterialization, TestPlan14GenerationTimerRetirement; root timer intention, sync-failure retry and retirement acceptance. Scheduler producer wiring remains incomplete. whole:Timer@609 179376ffb25fdc973418cfeae238cf1c69e3ec95495d6ac145ac6fa660f8a1c0 |