moves: the revert's post-probe re-read takes only the probe wake's own writes (census-ruling-home-part2)
The Plan 14 controller ruled the home-part2 overlay on 2026-10-16 as census-ruling-home-part2, on the condition that revertRun.probing's re-read accepts only the probe wake's own writes. The overlay header records the ruling in place of the RULING OWED marker. revertProbeWrites replays the probe wake's claim writes over the step's own read with the claim's own transitions: hostclaim.AdmitWriter's cold admission of a new generation for this box's writer (its origin head moved by one), then the restore's re-tag through FS-R11's claimRestoreObservation.retag/advance, unchanged. The result must equal the fresh claim exactly, so a change to the op or its journal, the holder or its session, a chore, a quiet key or another box's inventory is a conflict and the step is left to its next pass, as the stale CAS left it. TestRevertProbedRefusesAForeignWrite: seven foreign writes landed between the real probe and the probed CAS, each refused, and each again in place of the probe's own last write (the gen alone cannot tell them apart). TestRevertProbedAfterARealAdmission still passes. revertRun.probing is re-pinned at the same disposition (digest and the read's offset), its frozen declaration replaced, the internal/host envelope re-pinned and the seal moved; its B8 record-write digest is re-pinned too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 files changed,
+220
−13
internal/host/movesrevert.go+117 −5internal/host/movesrevert_test.go+92 −0internal/provision/plan14_root_reads_test.go+5 −3internal/provision/plan9_b8_census_test.go+1 −1internal/provision/testdata/plan14-root-reads-home-part2.tsv+5 −4
CI cancelled โ superseded by a newer push — run details
CI log
=== gate === cd launchctx && go vet ./... && go test ./... -count=1 ok amadan.net/carlos/platform.git/launchctx 0.011s ? 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 299.325s ? github.com/carlosframework/platform/cmd/changelog [no test files] ok github.com/carlosframework/platform/cmd/console 2.818s ok github.com/carlosframework/platform/cmd/moveprobe 93.212s ok github.com/carlosframework/platform/cmd/zddprobe 0.707s ok github.com/carlosframework/platform/internal/activator 249.480s ok github.com/carlosframework/platform/internal/agentsock 0.015s ok github.com/carlosframework/platform/internal/alerting 0.004s ok github.com/carlosframework/platform/internal/analytics 0.018s ok github.com/carlosframework/platform/internal/appconfig 2.479s ok github.com/carlosframework/platform/internal/apperr 0.070s ok github.com/carlosframework/platform/internal/applog 0.123s 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.704s ? 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.011s ok github.com/carlosframework/platform/internal/certwarm 0.002s ok github.com/carlosframework/platform/internal/cgroup 0.690s ok github.com/carlosframework/platform/internal/changelog 0.302s ok github.com/carlosframework/platform/internal/clamd 0.054s ? github.com/carlosframework/platform/internal/cliflag [no test files] amadan: job cancelled โ a newer push superseded it
diff --git a/internal/host/movesrevert.go b/internal/host/movesrevert.go| index f91f25cfb..5d36711cf 100644 |
| --- a/internal/host/movesrevert.go |
| +++ b/internal/host/movesrevert.go |
| @@ -6,6 +6,7 @@ import ( |
| "errors" |
| "fmt" |
| "log" |
| + "reflect" |
| "time" |
| "github.com/carlosframework/platform/internal/hostclaim" |
| @@ -594,13 +595,20 @@ func (r *revertRun) probing(ctx context.Context, obs hostclaim.Observed) (bool, |
| out = recoveryProbeWaiting |
| } |
| if out == recoveryProbeAnswered { |
| - // The probe's wake wrote the claim (its admission and attempt |
| - // reservation), so the step's own read is stale: read it again, as |
| - // the rollback's probed does. SetRevPhase holds the op, the kind and |
| - // rev_phase probing, so nothing else is taken on trust. |
| - if obs, err = hostclaim.Read(ctx, r.s, host); err != nil { |
| + // The probe's wake wrote the claim (its admission, then its |
| + // restore's re-tag), so the step's own read is stale: read it |
| + // again, as the rollback's probed does. The fresh claim is taken |
| + // only when it is the step's read plus those writes and nothing |
| + // else (revertProbeWrites, census-ruling-home-part2's condition); |
| + // any other change is refused as the stale CAS was. |
| + fresh, err := hostclaim.Read(ctx, r.s, host) |
| + if err != nil { |
| return false, fmt.Errorf("re-read the host claim: %w", err) |
| } |
| + if err := revertProbeWrites(cfg.Box, cfg.DataDir, obs, fresh); err != nil { |
| + return false, fmt.Errorf("the revert's probed: %w", err) |
| + } |
| + obs = fresh |
| if err := r.update(ctx, obs, "the revert's probed", hostclaim.SetRevPhase(id, hostclaim.PhaseProbing, hostclaim.PhaseProbed, r.rec.Move.Source != nil)); err != nil { |
| return false, err |
| } |
| @@ -651,6 +659,110 @@ func (r *revertRun) probing(ctx context.Context, obs hostclaim.Observed) (bool, |
| return r.stuck(ctx, reason, why) |
| } |
| +// revertProbeWrites is census-ruling-home-part2's condition on the revert's |
| +// re-read after its probe answers: fresh must be earlier, the step's own |
| +// read, with only the probe wake's own claim writes over it. Those are the |
| +// claim's own transitions, in order: hostclaim.AdmitWriter's cold admission |
| +// of a new generation for box, this op's source writer (with the origin its |
| +// CAS records), then, when the admitted stamp is new, the restore's re-tag |
| +// of box's database paths, checked by FS-R11's claimRestoreObservation.retag. |
| +// Each is replayed over earlier and the result must equal fresh exactly, as |
| +// FS-R11's advance compares, so the op, its phase and journal, the holder's |
| +// identity, every chore, fence, quiet key and every other box's inventory |
| +// stand as the step read them, and the admission must be the first write |
| +// after that read (its session is the claim gen it was made at). A claim no |
| +// write touched passes. The probe's attempt reservation is not a claim |
| +// write here: a reverting op's attempts are journalled on the box |
| +// (movesProbeAttempts), and hostclaim.ReserveWakeRetryAttempt refuses a |
| +// claim that is not serving. Anything else is hostclaim.ErrConflict. |
| +func revertProbeWrites(box, dataDir string, earlier, fresh hostclaim.Observed) error { |
| + if sameClaimObservation(earlier, fresh) { |
| + return nil |
| + } |
| + conflict := func(why string) error { |
| + return fmt.Errorf("%w: the host claim changed under the probe other than by its own writes: %s", hostclaim.ErrConflict, why) |
| + } |
| + if earlier.Absent || fresh.Absent || earlier.Host != fresh.Host || earlier.ETag == "" || earlier.Claim == nil || fresh.Claim == nil || |
| + earlier.Claim.Holder == nil || fresh.Claim.Holder == nil { |
| + return conflict("it is not the claim the step read") |
| + } |
| + e, f := earlier.Claim, fresh.Claim |
| + n := len(f.Holder.Admissions) |
| + if n == 0 { |
| + return conflict("no admission") |
| + } |
| + last := f.Holder.Admissions[n-1] |
| + if last.Box != box || last.Mode != "cold" || last.LossID != "" || last.Generation == "" || last.Generation == e.Holder.Generation { |
| + return conflict("the last admission is not a new generation for this box's writer") |
| + } |
| + in := hostclaim.Admission{ |
| + ObservedOpID: e.Op.ID, ObservedPhase: e.Op.Phase, ObservedSession: e.Holder.Session, ObservedGeneration: e.Holder.Generation, |
| + Box: box, Mode: "cold", NewGeneration: last.Generation, PredecessorCertified: last.WritesLost == nil, KeepStamp: f.Holder.Stamp != last.Session, |
| + Coverage: last.Coverage, Region: last.Region, PredecessorRegion: last.PredecessorRegion, AdditionalWritesLost: last.AdditionalWritesLost, |
| + } |
| + if last.WritesLost != nil { |
| + in.RemoteMaxTXID = last.WritesLost.RemoteMaxTXID |
| + } |
| + admitted, err := revertClaimCopy(e) |
| + if err != nil { |
| + return err |
| + } |
| + if err := hostclaim.AdmitWriter(in)(admitted); err != nil { |
| + return conflict(err.Error()) |
| + } |
| + admitted.Gen = e.Gen + 1 |
| + // The admission's CAS moves the origin head by exactly one node, or |
| + // leaves it when the chain's last node is already this origin. |
| + if eh, fh := e.Origins, f.Origins; (eh == nil) != (fh == nil) || eh != nil && *eh != *fh { |
| + var count uint64 |
| + if eh != nil { |
| + count = eh.Count |
| + } |
| + if fh == nil || fh.Count != count+1 { |
| + return conflict("the origin head moved by more than the admission") |
| + } |
| + head := *fh |
| + admitted.Origins = &head |
| + } |
| + // The copy drops the origin AdmitWriter staged for Update's CAS. |
| + if admitted, err = revertClaimCopy(admitted); err != nil { |
| + return err |
| + } |
| + switch f.Gen { |
| + case admitted.Gen: |
| + if !reflect.DeepEqual(admitted, f) { |
| + return conflict("the admission") |
| + } |
| + case admitted.Gen + 1: |
| + db := provision.DefaultDBPath(dataDir, earlier.Host) |
| + if e.Inventory[box].Layout == "nested" { |
| + db = provision.NestedDBPath(dataDir, earlier.Host) |
| + } |
| + mid := hostclaim.Observed{Host: earlier.Host, ETag: earlier.ETag + "+admitted", Claim: admitted} |
| + ob := &claimRestoreObservation{observed: mid, box: box, dataDir: dataDir, db: db} |
| + if err := ob.retag(mid, fresh); err != nil { |
| + return conflict("the restore's re-tag") |
| + } |
| + default: |
| + return conflict(fmt.Sprintf("gen %d to %d", e.Gen, f.Gen)) |
| + } |
| + return nil |
| +} |
| + |
| +// revertClaimCopy is a deep copy of c through its JSON form, as the claim |
| +// store decodes it. |
| +func revertClaimCopy(c *hostclaim.Claim) (*hostclaim.Claim, error) { |
| + body, err := json.Marshal(c) |
| + if err != nil { |
| + return nil, err |
| + } |
| + var out hostclaim.Claim |
| + if err := json.Unmarshal(body, &out); err != nil { |
| + return nil, err |
| + } |
| + return &out, nil |
| +} |
| + |
| // probed (ยง7.4 probed): reopened_at on the record (J26), the conditional open |
| // on hold_op = move id under the host's hold lock (J12), the claim opened. |
| func (r *revertRun) probed(ctx context.Context, obs hostclaim.Observed) (bool, error) { |
diff --git a/internal/host/movesrevert_test.go b/internal/host/movesrevert_test.go| index 47242a196..ee8c7516b 100644 |
| --- a/internal/host/movesrevert_test.go |
| +++ b/internal/host/movesrevert_test.go |
| @@ -679,6 +679,98 @@ func TestRevertProbedAfterARealAdmission(t *testing.T) { |
| } |
| } |
| +// The census condition on that re-read (census-ruling-home-part2): the |
| +// fresh claim is taken only when it differs from the step's own read by the |
| +// probe wake's own writes (its admission of a new generation for this box's |
| +// writer and its restore's re-tag of this box's database). Here the real |
| +// probe answers as above, and one foreign write lands after it and before |
| +// the probed CAS: a change to the op, to the holder or its session, or to a |
| +// claim field the probe never writes. The step refuses it as a conflict, |
| +// the claim stays at probing with the foreign change in place, and the row |
| +// stays held: the step is left to its next pass, as the stale CAS left it. |
| +func TestRevertProbedRefusesAForeignWrite(t *testing.T) { |
| + for _, e := range []struct { |
| + name string |
| + edit func(*hostclaim.Claim) |
| + }{ |
| + {"op target", func(c *hostclaim.Claim) { c.Op.Intent["target"] = "other-box" }}, |
| + {"op journal", func(c *hostclaim.Claim) { c.Op.Journal[hostclaim.JournalSrcGen] = "other" }}, |
| + {"holder session", func(c *hostclaim.Claim) { |
| + c.Holder.Session++ |
| + c.Holder.Admissions[len(c.Holder.Admissions)-1].Session++ |
| + }}, |
| + {"holder writer box", func(c *hostclaim.Claim) { c.Holder.WriterBox = "uk-box" }}, |
| + {"quiet key", func(c *hostclaim.Claim) { |
| + c.QuietAfter = map[string]time.Time{"uk-box": p1Created} |
| + }}, |
| + {"chore", func(c *hostclaim.Claim) { |
| + o := c.Obligations[0] |
| + o.ID = "0123456789abcdef0123456789abcdef" |
| + c.Obligations = append(c.Obligations, o) |
| + }}, |
| + {"another box's inventory", func(c *hostclaim.Claim) { |
| + c.Inventory["uk-box"] = hostclaim.InventoryEntry{Layout: "flat", Paths: map[string]uint64{p1Host + ".db": 1}} |
| + }}, |
| + } { |
| + t.Run(e.name, func(t *testing.T) { |
| + h := newRevertHarnessOn(t, newLandingHarness(t, realLaunchEnv).cutHarness) |
| + h.fromStopped(t) |
| + h.arm(t) |
| + rec := revertRecovery(t, h) |
| + probe, wrote := h.cfg.recoveryProbe, false |
| + var foreign *hostclaim.Claim |
| + var before, own hostclaim.Observed |
| + read := func() hostclaim.Observed { |
| + o, err := hostclaim.Read(context.Background(), h.s, p1Host) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + return o |
| + } |
| + h.cfg.recoveryProbe = func(ctx context.Context, s release.Store, r provision.Record, req recoveryProbeRequest) (recoveryProbeOutcome, error) { |
| + before = read() |
| + out, err := probe(ctx, s, r, req) |
| + if out == recoveryProbeAnswered && !wrote { |
| + wrote = true |
| + own = read() |
| + rewriteP1Claim(t, h.s, e.edit) |
| + foreign = h.claim(t) |
| + } |
| + return out, err |
| + } |
| + err := h.w().revertSource(context.Background(), h.s, h.read(t, p1Host), nil) |
| + if !wrote || rec.wakes != 1 { |
| + t.Fatalf("the probe did not answer once: wrote %v, %d starts", wrote, rec.wakes) |
| + } |
| + if !errors.Is(err, hostclaim.ErrConflict) { |
| + t.Fatalf("the revert's probed took a foreign write: %v", err) |
| + } |
| + // The same change in place of the probe's own last write (the |
| + // gen alone cannot tell them apart) is refused too, and the |
| + // probe's own writes alone pass. |
| + box := h.w().cfg.Box |
| + if err := revertProbeWrites(box, h.cfg.DataDir, before, own); err != nil || own.Claim.Gen != before.Claim.Gen+2 { |
| + t.Fatalf("the probe's own writes (gen %d to %d): %v", before.Claim.Gen, own.Claim.Gen, err) |
| + } |
| + swapped, err := revertClaimCopy(own.Claim) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + e.edit(swapped) |
| + if err := revertProbeWrites(box, h.cfg.DataDir, before, hostclaim.Observed{Host: own.Host, ETag: own.ETag + "-foreign", Claim: swapped}); !errors.Is(err, hostclaim.ErrConflict) { |
| + t.Fatalf("a foreign change at the probe's own gen was taken: %v", err) |
| + } |
| + c := h.claim(t) |
| + if c.Op.Phase != hostclaim.PhaseProbing || c.Op.Journal[hostclaim.JournalRevPhase] != hostclaim.PhaseProbing || c.Gen != foreign.Gen { |
| + t.Fatalf("the claim after the refusal: gen %d (foreign %d), %+v", c.Gen, foreign.Gen, c.Op) |
| + } |
| + if m := h.move(t); m.ReopenedAt != nil || h.route.HoldOp != p1ID { |
| + t.Fatalf("the refusal reopened: %+v, row %+v", m, h.route) |
| + } |
| + }) |
| + } |
| +} |
| + |
| // TestRevertReplays: a crash after any of the revert's side effects, then a |
| // restarted agent, still reaches the close, with the evidence asked once it |
| // is journalled, the hold only ever restoring under the move id, and the |
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go| index 87d4e3da6..ba4764854 100644 |
| --- a/internal/provision/plan14_root_reads_test.go |
| +++ b/internal/provision/plan14_root_reads_test.go |
| @@ -779,12 +779,14 @@ func TestPlan14RootReadCensus(t *testing.T) { |
| // eleventh. Task 5's move door (a home target under host claims) |
| // re-freezes Server.handleAPIInstanceMoveRequest and |
| // Server.startClaimModeMove at main's dispositions, read sets |
| - // unchanged, two more. Ruling owed: the Plan 14 owner has not yet |
| - // approved it. |
| + // unchanged, two more. Ruled 2026-10-16 as census-ruling-home-part2 |
| + // (the overlay's header), on the condition that revertRun.probing's |
| + // re-read accepts only the probe wake's own writes; that check |
| + // re-pinned revertRun.probing at the same disposition, so the seal moved. |
| if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-home-part2.tsv"), &inv, true); err != nil { |
| t.Fatal(err) |
| } |
| - seal = "efe40983d60e48a0743f4ca8ca3f1fe504f48f737d8ad8c7c1a549ab707bca12" |
| + seal = "5d1b9b6addf6c9964ed18b417ee5b3d0d49fd43e1acabb775e054bba4b6e314e" |
| errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...) |
| errs = append(errs, checkRootReadDeclarations(got, inv)...) |
| if len(errs) > 0 { |
diff --git a/internal/provision/plan9_b8_census_test.go b/internal/provision/plan9_b8_census_test.go| index 96a2995fe..8af5ccb11 100644 |
| --- a/internal/provision/plan9_b8_census_test.go |
| +++ b/internal/provision/plan9_b8_census_test.go |
| @@ -617,7 +617,7 @@ var plan9B8CensusInventory = []censusEntry{ |
| {"internal/host/movesrecover.go", "recoverRun.stuck", "record-writer", "key:SetRecordMove", "d634bbd18794aedf081c51f6e3c16b62a9b6b82b21b24f636b949c840c8e5776"}, |
| // Records revert inputs through SetRecordMove; stuck uses the reviewed record helper and journals are local. Plan 6 fix round 2: its journal write is a Commit through moves.UpdateJournal, and its two step removals are Unmarks. |
| // Re-reviewed for home part 2, Task 4 (ruling owed): after the probe answers it reads the host claim again for its probed CAS; its record writes are unchanged. |
| - {"internal/host/movesrevert.go", "revertRun.probing", "record-writer", "key:SetRecordMove", "2eb21ddc4f49a08292f687f6f6e22b1c70facaf3a71a2d7a118583fbb278e5e3"}, |
| + {"internal/host/movesrevert.go", "revertRun.probing", "record-writer", "key:SetRecordMove", "9a4e1f09ea6af0ab98f0253765d544a4419b4daa98066753fa8b2bf1e5aa618a"}, |
| // Applies revert record mutations only through SetRecordMove and its sequenced writer. |
| {"internal/host/movesrevert.go", "revertRun.record", "record-writer", "key:SetRecordMove", "f42100f118eb62ceece23b49423bda2ef357f1aa065760b288bf20340e36a920"}, |
| // Starts rollback through SetRecordMove; the companion claim update targets the separate claim namespace. |
diff --git a/internal/provision/testdata/plan14-root-reads-home-part2.tsv b/internal/provision/testdata/plan14-root-reads-home-part2.tsv| index 453897c35..90bc3312b 100644 |
| --- a/internal/provision/testdata/plan14-root-reads-home-part2.tsv |
| +++ b/internal/provision/testdata/plan14-root-reads-home-part2.tsv |
| @@ -1,4 +1,4 @@ |
| -# Plan 14 census overlay home-part2 (amadan #236, moves into home part 2, Tasks 1, 2b (with its fix rounds 2 to 5) and 4; loaded last, after census-ruling-seed, which loads after moves-overcap-cut-236). RULING OWED: the Plan 14 owner has not yet approved this overlay. |
| +# Plan 14 census overlay home-part2 (amadan #236, moves into home part 2, Tasks 1, 2b (with its fix rounds 2 to 5) and 4; loaded last, after census-ruling-seed, which loads after moves-overcap-cut-236). RULED by the Plan 14 controller 2026-10-16 as census-ruling-home-part2: every unit at main's disposition; LaunchLayers takes LaunchEnvironment's (J1, root-route); movesReadLaunch follows merge-ruling-2 like its siblings; revertRun.probing is accepted-legacy;rev3=J4;basis=root-input, on the condition that the post-probe re-read accepts only the probe wake's own writes. |
| # Task 1: moves.CompareEnv takes targetHome, and T3 (internal/host checkEnv) passes rec.Move.To == "" for it: a home target must set no CARLOS_REGION and fails region-present when it does. Task 2b: internal/moves owns the move-portable env rule once: a name is left out of the pin only when it is box-owned (AgentOwnedVars, BoxVars) and the instance's own layers did not supply its running value (Pinned); ReportLaunch and PortableDigest make the v2 report and pin (EnvReportVersion, "env2:sha256:"), and LocalDigest the whole env for a check made on the box that recorded it. The composer reports the instance's layer names (activator ExecBackend.LaunchLayers, LaunchEnvironment's composition moved into its own unit), never a name the agent decides (agentOwnedNames). The moves launch source (claimMoveLaunchSource) makes the env report from that one composition and returns it with the launch; the probe owners take its digest as the launch's pin (movesReadLaunch passes it through) and keep it in memory for Materialize, bound to the exact env. No layer names cross into the claim: ClaimServingLaunch, ClaimLaunchInputs and ClaimLaunchDigest are main's. T3 refuses a v1 source report and the probe owners a v1 pin, fail closed. The recovery records recoveryInputs and checks localEnvDigest, the whole local env. |
| # Env digests, by where they are compared. Across boxes, the move-portable v2 pin: the source pin writeSourceReport sr.Env.Digest (host/movesphase1.go) copied to the target report's Inputs.EnvReport (host/movesreport.go) and checked on the target against the launch's own pin, the digest of the env report claimMoveLaunchSource makes from the same composition as the launch's env (host/claimmoveaccess.go, passed through by movesReadLaunch, host/moveswakesource.go): the probe owners' Select and the committed wake's selectCommitted compare it, and Materialize and materializeCommitted check the pin Select captured in memory for exactly that env (host/movesprobeowners.go captureEnvPin); the cut's currentInputs (host/movescut.go), compared on the source with that pin. Same box, the whole local env: the recovery's recoveryInputs (host/movescut.go, recorded through host/movesrecoveryprobe.go:139-146 on the recovering box) checked by the recovery owners' Select and Materialize (host/movesrecoveryowners.go localEnvDigest). Same box, main's full composed env, unchanged: ClaimLaunchDigest (host/claimoperations.go, byte for byte main's; it is root's LaunchSpec hash), compared only with the attempt's own box, boot and controller (host/movesprobeattempts.go, host/relocationattempts.go, host/claimlaunch_authority.go, host/claimwakestop.go, activator/servingretry.go, activator/rolelaunch_linux.go, activator/rolelaunch_root_linux.go); snapshot.Identity.Config (host/claimmoveaccess.go), which the moves owners never read (they set their own identity from the pins). |
| # Re-frozen units, every one at main's disposition (class, task and basis unchanged; kinds and digest refreshed); these rows replace this overlay's earlier, unruled rows. Authority rows, read set unchanged (only source offsets moved): host/claimmoveaccess.go claimMoveLaunchSource (authority C,G,J1; it calls LaunchLayers and reports the env from that composition) and host/movespass.go armPhase1 (authority B2,C; its LaunchEnv hook returns the layer names). Legacy rows: activator ExecBackend.LaunchEnvironment (a wrapper of LaunchLayers) and the new ExecBackend.LaunchLayers (LaunchEnvironment's composition and reads, moved; LaunchEnvironment's disposition); host movesWorker.writeSourceReport (ReportLaunch with the layer names); movesProbeOwners.Select, Materialize, selectCommitted and materializeCommitted (the captured report's pin, v2 only); movesRecoveryOwners.Select and Materialize (localEnvDigest); moveswakesource.go movesReadLaunch (passes the access's report through). No new read and no root decision. Ten frozen legacy declarations are added. |
| @@ -6,6 +6,7 @@ |
| # Task 4 (a defect its revert witness found): the revert's probing step reads the host claim again after its probe answers, because a real recovery probe wake writes the claim (its admission and attempt reservation) and the step's own read went stale, so its probed CAS lost every pass. revertRun.probing gains that one read (file:Read, hostclaim.Read of the control store) and is new to the census; it is frozen at the disposition main gives the same read in its sibling units (revertRun.step and recoverRun.claim: legacy, accepted-legacy;rev3=J4;basis=root-input). One frozen declaration is added; the seal moves. |
| # Task 5: the Phase 1 move door admits a home target under host claims. Server.handleAPIInstanceMoveRequest gains its home refusals before any write (MV-N510 without host claims, MV-N511 for a home-named box, read from the same edges-file listing through movePlacements) and its sentences name the home; Server.startClaimModeMove's sentences name the home. Read sets unchanged (the same file:Exec and file:Read kinds), only offsets moved: both re-frozen at main's disposition (class, task and basis unchanged; kinds and digest refreshed), they add two frozen declarations; the seal moves. internal/console's source envelope is refreshed. |
| # Merge of main at ee35dee (2026-10-16): this overlay now loads after census-ruling-seed (FS-R6, FS-R10) and moves-overcap-cut-236. No unit of this overlay is in either; every row still matches its scan unchanged. Only the internal/activator and internal/host envelopes are re-pinned to the merged tree, and the seal moves with the merged freeze. |
| +# The ruling's condition (2026-10-16): revertRun.probing takes the re-read claim only when it is the step's own read plus the probe wake's own writes, replayed with the claim's own transitions (hostclaim.AdmitWriter's cold admission for this box's writer, with its origin, then the restore's re-tag through FS-R11's claimRestoreObservation.retag) and compared exactly; anything else is a conflict, as the stale CAS was. A reverting op's probe attempts are journalled on the box, never reserved in the claim. The check reads nothing, so the read set is the same one file:Read: revertRun.probing is re-pinned at the same disposition (only its digest and the read's offset moved), its frozen declaration replaced, the internal/host envelope re-pinned, and the seal moves. |
| @legacy 849ce111c61c3dcedd80595348d0a26c67fb47462d497fba63d52d2c94e1d5d0 internal/activator/backend_exec.go ExecBackend.LaunchEnvironment legacy accepted-legacy accepted-legacy;rev3=J1;basis=root-route whole:Route@139 e8853e15b427b80a701b5e10b565f7d4f0b778e0c8185da63266e2408f6b4113 |
| internal/activator/backend_exec.go ExecBackend.LaunchEnvironment legacy accepted-legacy accepted-legacy;rev3=J1;basis=root-route whole:Route@139 e8853e15b427b80a701b5e10b565f7d4f0b778e0c8185da63266e2408f6b4113 |
| @legacy 1680e7269c40013e72c2f0a39abc13ee75cecadf2b3b8aa3d3a0278c060458ce internal/activator/backend_exec.go ExecBackend.LaunchLayers legacy accepted-legacy accepted-legacy;rev3=J1;basis=root-route registry:Socket@751,whole:Route@179,whole:Route@747 66a14ac6d0a4c2c9333adf45a792407d8cc4fa389416feb31fcf58616fc5b611 |
| @@ -28,13 +29,13 @@ internal/host/movesrecoveryowners.go movesRecoveryOwners.Materialize legacy acce |
| internal/host/movesrecoveryowners.go movesRecoveryOwners.Select legacy accepted-legacy accepted-legacy;rev3=J1;basis=root-route registry:DBPath@1756,registry:DBPath@792,whole:Route@1011,whole:Route@514 746a4c5f4a87e6b5210ae12e986af05ea8bf3849d940a4fec6598000b555cd46 |
| @legacy 1a77e1c538d55076eb81dbe54e0c110803dbafd56a79990246430473fca9a16d internal/host/moveswakesource.go movesReadLaunch legacy accepted-legacy accepted-legacy;rev3=merge-ruling-2;basis=Moves launch-input adapter selects by host through existing read interface; generic launch payload selection remains legacy. registry:Host@258 780a60ccf1035f2c7d64fdc804a867b64d380b05a51b151155ca6efe70b01767 |
| internal/host/moveswakesource.go movesReadLaunch legacy accepted-legacy accepted-legacy;rev3=merge-ruling-2;basis=Moves launch-input adapter selects by host through existing read interface; generic launch payload selection remains legacy. registry:Host@258 780a60ccf1035f2c7d64fdc804a867b64d380b05a51b151155ca6efe70b01767 |
| -@legacy d0a0ae52a0459fe62264eeb3b0cf906548b4afe6a8f23a4d1bae562b3ca286c0 internal/host/movesrevert.go revertRun.probing legacy accepted-legacy accepted-legacy;rev3=J4;basis=root-input file:Read@2524 2eb21ddc4f49a08292f687f6f6e22b1c70facaf3a71a2d7a118583fbb278e5e3 |
| -internal/host/movesrevert.go revertRun.probing legacy accepted-legacy accepted-legacy;rev3=J4;basis=root-input file:Read@2524 2eb21ddc4f49a08292f687f6f6e22b1c70facaf3a71a2d7a118583fbb278e5e3 |
| +@legacy 2640176488fffc20bc14ecfdfe3b53b40a3acfa53079b266829f1b33fe1d123e internal/host/movesrevert.go revertRun.probing legacy accepted-legacy accepted-legacy;rev3=J4;basis=root-input file:Read@2645 9a4e1f09ea6af0ab98f0253765d544a4419b4daa98066753fa8b2bf1e5aa618a |
| +internal/host/movesrevert.go revertRun.probing legacy accepted-legacy accepted-legacy;rev3=J4;basis=root-input file:Read@2645 9a4e1f09ea6af0ab98f0253765d544a4419b4daa98066753fa8b2bf1e5aa618a |
| @legacy 5923daef3bc024b71d20d654c5afd9e78c93f9ccd00508507cc39f1cf083e14f internal/console/apiinstancemove.go Server.handleAPIInstanceMoveRequest legacy accepted-legacy accepted-legacy;rev3=census-ruling-seeded-instances;basis=the console (cmd/console serve under console.service, user carlos-console): main's body with one more refusal before any write: a seed that may not move yet (refuseSeedMove); read sites unchanged, offsets moved file:Exec@10698,file:Exec@8291,file:Exec@8379,file:Exec@9370,file:Exec@9547,file:Exec@9698,file:Read@6777,file:Read@9089 79483d9603c096a269042d7b250e328761f9de7f365c272b82d34b16579b0417 |
| internal/console/apiinstancemove.go Server.handleAPIInstanceMoveRequest legacy accepted-legacy accepted-legacy;rev3=census-ruling-seeded-instances;basis=the console (cmd/console serve under console.service, user carlos-console): main's body with one more refusal before any write: a seed that may not move yet (refuseSeedMove); read sites unchanged, offsets moved file:Exec@10698,file:Exec@8291,file:Exec@8379,file:Exec@9370,file:Exec@9547,file:Exec@9698,file:Read@6777,file:Read@9089 79483d9603c096a269042d7b250e328761f9de7f365c272b82d34b16579b0417 |
| @legacy 3bbe043ae854375a257b0425b0ee82ce9c2146b9bcbcbc3f88aa3ed6c4fe5bfc internal/console/apiinstancemove.go Server.startClaimModeMove legacy accepted-legacy accepted-legacy;rev3=J4;basis=root-input file:Exec@3526,file:Exec@565 c4e47ebd1e84c89d72ed8bbb25e65af8e365f4993827682ca55de9fd2d2d90bb |
| internal/console/apiinstancemove.go Server.startClaimModeMove legacy accepted-legacy accepted-legacy;rev3=J4;basis=root-input file:Exec@3526,file:Exec@565 c4e47ebd1e84c89d72ed8bbb25e65af8e365f4993827682ca55de9fd2d2d90bb |
| @coverage internal/activator 9511daaa77f912b86ab84d8e30b7e6c6b1b6d0b2989e79ec6bda2c0471daf1d4 |
| @coverage internal/console a565401e44e7797120ad52136146d84c8e0d430ceeb6a6f1c5e1ea0ed7a78c7e |
| -@coverage internal/host 152dee4aa0b7e48a140ee06b0ad8c9f342f3381c2327fcc6dfbb7f2c72f4b61c |
| +@coverage internal/host 585d33e2b501102e08b0fc9fd9cc074720ee9800d262270b59dfeb90ee50d0ce |
| @coverage internal/moves 51ae19338bf9f92d45cc166f9fb71cdd3280c617892f71bb9ce67b2b00a7ae27 |