# Moves Phase 1 Implementation Plans: Index

> **For agentic workers:** this file is the map. Each plan it names is a
> self-contained implementation plan with the superpowers header; execute
> them in the order below with superpowers:subagent-driven-development or
> superpowers:executing-plans. Do not start a plan whose dependencies are
> not merged on `main`.

**Goal:** Build the move-any-app Phase 1 design (spec rev 7.22, e1d7c3f1;
claim rev 6.30, 73b1b9dc) on `main` at d649d8d4 or later (claim Plan 6, the
box wiring for C1 to C4, merged 2026-10-06), as an ordered
series of plans that each produce working, tested, mergeable software.
(The series began against spec rev 7.18 on `main` 67547f5; the revisions
since are in the spec's header and the dispositions below.)

**Spec:** `docs/superpowers/specs/2026-10-02-move-any-app-design.md` (rev
7.20: P7 cold-only, region HA L13 to L17 folded in with claim rev 6.28's
fence reference and pre-flip conversion; L18 stated as a limit; one-box
regions only). The executable model is `internal/sim/hostlife/` at 9c417aaa on
the same branch; its witness names are cited in the tests the plans port.

**Claim library on `main` (built on, never re-implemented):**
`internal/hostclaim` (E, rev 6.27 semantics; 989ee349 §1.1 records the
#236 rulings), `internal/genselect` (F), `internal/hostguard` and
`internal/stopevidence` (claim Plan 3), `internal/park` (claim Plan 4),
`internal/console/apidbclaim.go` (claim Plan 7).

## The series

| # | plan | produces | needs from claim plans 5, 6, 8 |
|---|---|---|---|
| 1 | [`2026-10-06-moves-phase1-1-copy-and-staging.md`](2026-10-06-moves-phase1-1-copy-and-staging.md) | op ids, the record's move journal, the `moving` claim CAS at the request door (operator-only), the per-op staging layout with `move.json`, seal-ready copy rounds, the files archive, the timers snapshot, the launch and readiness reports (T1 to T12, S1, S2), the T11 budget, the protocol floor and `moves_protocol`, the `hold_op` column, the copy gate | nothing |
| 2 | [`2026-10-06-moves-phase1-2-cut-flip-landing.md`](2026-10-06-moves-phase1-2-cut-flip-landing.md) | the journaled cut (requested to flipped), `stopevidence.Reconstruct`, the seal by version, the archive publication, the flip CAS, hold rows and reconcile's participant rules, timers frozen and drained, `RenderHeld`, `MaterializeHeld`, the landing's `checking` to `restored` phases including the generation build (snapshot, anchor, build intent, install manifest), the park queue helpers and `Engine.Parked` | C3 and C8 for two sub-steps of `local`; everything else nothing |
| 3 | [`2026-10-06-moves-phase1-3-probe-commit-open.md`](2026-10-06-moves-phase1-3-probe-commit-open.md) | the probe policy (attempt windows, deadline checks, budgeted attempts), `ProbeWake` as a cold admission into the prepared generation, the commit record CAS and its claim half (obligations, `CoverRegionPredecessors`), the conditional open, the source's finalization park | Tasks 5 to 7 BLOCKED-ON-CLAIM (C1, C2); Tasks 1 to 4 and 8 are not |
| 4 | [`2026-10-06-moves-phase1-4-rollback-return-declare-lost.md`](2026-10-06-moves-phase1-4-rollback-return-declare-lost.md) | `reverting` (pre-flip recovery), the rollback decision, the target's release with the clean/dirty rule (`src_covered`), the dirty return lane, the source's `claimed` CAS (`holder.stamp = source_stamp`, clean `holder.generation = src_gen`), the dirty install on the source, the target's `cleaning`, `stuck`, `--retry`, the record side of declare-lost | Task 9 BLOCKED-ON-CLAIM (C1, C2); Task 10 BLOCKED-ON-CLAIM (C6, C7); the lost-target branch of Task 5 with it |
| 5 | [`2026-10-06-moves-phase1-5-cleanup-and-parks.md`](2026-10-06-moves-phase1-5-cleanup-and-parks.md) | the paging primitive, abort with the target-owned staging park, the staging horizon as a persisted `ParkScope`, the finalization parks and the release to `serving` (K8), the `moves/<host>` sweep scope, the db door's refusal on move kinds, the console's claim-half recovery pass and deadline sweeps | the acknowledgement after the horizon of every chore is C4's runner; the immediate half is built here; the console pass can later be called from claim Plan 5's `Recovery.Pass` |
| 6 | [`2026-10-06-moves-phase1-6-cli-status-observability.md`](2026-10-06-moves-phase1-6-cli-status-observability.md) | `carlos move` and its four sub-verbs, `POST …/move/resolve`, `GET …/move?detail=1`, exit codes 0/3/4/5, the move log, the member stream events, paging rules, the copy gate, the `docs/site/cli.md` heading | nothing (the `--declare-lost` flag calls Plan 4 Task 10's door and is built with it) |
| 7 | [`2026-10-06-moves-phase1-7-rehearsal-runbook.md`](2026-10-06-moves-phase1-7-rehearsal-runbook.md) | the `moveprobe` test app, the cross-version tests, the rollout steps and the rehearsal runbook in M12's order | every plan above, merged and rolled with Paul's go |
| home part 2 | [`2026-10-15-moves-home-region-part2.md`](2026-10-15-moves-home-region-part2.md) | moves into the home region under host claims (#236): T3's home rule, the commit, revert, rollback, abort and loss with a home target, the door's home target (MV-N510, MV-N511), and the rehearsal's Run 1b | claim Plan 13's home naming and the signed designation; nothing new from the claim |

Plans 1 and 2 have no unmerged dependency and are built first, in order.
Plan 3's pure tasks (1 to 4, 8) and Plan 4's pure tasks (1 to 8) can be
built before claim Plans 6 and 8 land; the index marks every task that
cannot. Plans 5 and 6 depend only on Plans 1 to 4 for code; Plan 5 notes
the runner it hands its chores to.

## Dependency order

```
main (67547f5+) ─► Plan 1 ─► Plan 2 ─► Plan 3 (1-4, 8) ─► Plan 4 (1-8) ─► Plan 5 ─► Plan 6 ─► Plan 7
                                        │                   │
          claim Plan 6 (feat/hostclaim-box) ─► Plan 3 (5-7) ─► Plan 4 (9)
          claim Plan 8 (feat/hostclaim-leases) ─────────────► Plan 4 (10)
          claim Plan 5 (feat/hostclaim-console) ─────────────► Plan 5 (console pass joins Recovery.Pass; adoption of legacy hosts before any rehearsal)
```

## Exact interfaces needed from claim Plans 5, 6 and 8 (for platform-1e)

These are the names the moves plans call. Each is quoted from the plan
that defines it; if a plan changes a name, the moves plan that uses it
changes with it.

**From claim Plan 6 (`feat/hostclaim-box`, package `internal/hostbox`,
`internal/hostwrite`, activator and schedule adapters):**

- `hostbox.Writer{Claims release.Store; Data genselect.Reader; Bucket, Region, Box string; AdmissionEvidence func(context.Context, hostclaim.Observed, hostclaim.Admission, *genselect.Plan) error; PublicationEvidence func(context.Context, hostclaim.Observed) error; NewID func() string}` with
  `Baseline(ctx, observed) (*genselect.Plan, error)`,
  `Admit(ctx, observed, in hostclaim.Admission, baseline *genselect.Plan) (hostclaim.Observed, error)`,
  `Published(ctx, observed) (hostclaim.Observed, error)`. Plan 3 Task 5
  (`ProbeWake`) admits the probe through `Admit` and publishes through
  `Published`; it supplies `AdmissionEvidence` that checks the landing's
  journal (the prepared generation's install manifest) and
  `PublicationEvidence` that checks `generation.json` by `If-Match`.
- `hostbox.GenerationPrefix(holder hostclaim.Holder) (string, error)`.
- The claim-aware start sequence Plan 6 Task 9 wires into
  `Service.wake` (purge, restore of the admitted baseline, start,
  readiness, first upload, marker, `PublishGeneration`, open). Plan 3
  Task 6 requires that sequence to be callable for a held row with a
  probe policy: moves asks Plan 6 for one exported entry,
  `(a *Service) startAdmitted(ctx, e *actEntry, admitted hostclaim.Observed, policy startPolicy) error`, or equivalent, where `startPolicy{MaxStarts int; Window time.Duration; Deadline time.Time; Probe bool}` is the only new input. Until that exists, Plan 3 Tasks 5 to 7 cannot be built.
- `hostbox.Regional.Run` runs `park-replica` chores whose scope is a
  `moves/<host>/<op id>` lane or a whole location, and `park-generation`
  chores, under a move or rollback op id (platform-1e work order item 1:
  "Regional.Run for park-replica moves scopes"); no new kind (D6).
- `spawnClaimed(ctx, enabled bool, manager *cgroup.Manager, host string, limit cgroup.Limit, selected hostwrite.Work, identity hostwrite.Identity, build func() *exec.Cmd) (*exec.Cmd, *cgroup.Prepared, error)` (claim Plan 6 line 878): every spawn under a move is strict (§5.3); `ProbeWake` spawns through it with `CARLOS_MOVE_PROBE=1`, keeps the returned `*cgroup.Prepared` for the process's whole life, and on every exit path (a failed start included) runs `Prepared.KillAndEmpty` then `Prepared.Close` before the attempt is counted.
- The wake admission of Plan 6 Task 9 must honour moves' K6: `doWake` reads the claim fresh and, when `op.exec` does not name this box, kills the host's process tree (`ReconstructStopEvidence`'s step 1, Plan 2 Task 3) before anything else; it admits a wake under `landing/committed` for the target (§7.14 row "landing, committed, not yet finalized") and under `reverting`/`restoring` phases `probed` and `opened` for the source. Its restore writes `<dbPath>.gen` and retires (`park.Local.RetireArtifacts`) a warm database whose `.gen` names another generation before restoring cold (§5.1 start check rule 3, §9.3).
- `schedule.SelectedTimers` (the timer-control adapter): Plan 2 Task 9's
  `ReplaceTimers` call on the target runs under the landing's own
  selection, so the adapter must accept a held row's write when the
  selection names the landing op.

**From claim Plan 8 (`feat/hostclaim-leases`, package `internal/hostclaim`
additions, `internal/hostexec`, `internal/hostquarantine`,
`internal/hostfence`):**

- `hostclaim.LostDeclaration{LossID string; ExpectedClaimGen uint64; ExpectedClaimETag string; HolderIncarnation string; HolderStamp, HolderSession uint64; OpID, OpKind, OpPhase, Box string; QuarantineRevision uint64; Enrollment string; FencedAt time.Time; Consequences LossConsequences}`.
- `hostclaim.LossOperationOwner interface{ ValidateDeclareLost(Claim, LostDeclaration) error }`: Plan 4 Task 10 implements it in `internal/moves` with §7.10's legality table (target owes evidence; target released dirty and not returned; destroyed box owes only local chores; refused after the record's commit, for the source, for a non-participant).
- `hostclaim.DeclareLost(ctx, s release.Store, pub ed25519.PublicKey, observed Observed, lossID string, owner LossOperationOwner) (Observed, error)`.
- `hostclaim.LossRecord`, `CreateLossRecord(ctx, s, sg release.Signer, record LossRecord) (ObservedLossRecord, error)`, `ReadLossRecord`, `ReplaceLossRecord`, `LossCloudObservation`, `LossOperatorAttestation`, `TimerLoss`, `LossConsequences`, `ObligationRef`.
- `hostclaim.LossRecoveryOwner interface{ ValidateLossRecovery(Claim, LossRecord, Admission) error }` and `AdmitWriterWithLoss(ctx, s, pub, observed, in Admission, lossID string, validated []ValidatedWriteSource, owner LossRecoveryOwner) (Observed, error)`: the source's re-admission after a declared-lost target (the `writes_lost` for every target session since the cut and the source's cut session when `src_covered` is false; witnesses `wTwoSessionDeclaration`, `wNoDuplicateDeclarationEarly`, `wNoDuplicateDeclarationLate`).
- `hostclaim.Readmit(box string, quarantineRevision uint64) func(*Claim) error` and the `hostquarantine` retirement a returning box runs before `--readmit` (§7.10).
- `hostexec.Open(ctx, cfg hostexec.Config) (*Controller, error)` with `Ready/Lost/Close`: the lease controller every admitted writer runs under; `ProbeWake` runs its spawn under it once Plan 8 lands (Plan 3 Task 6 notes the seam).
- `declareLostTransition`'s effects (claim Plan 8 line 1444): it journals `loss.<id>` (the canonical recovery intent), sets `op.exec = "none"` (the literal string, not `""`) when the lost box was the executor, removes the box from `op.participants`, reassigns its regional chores to `region:<r>`, discharges its local chores, and keeps the phase. Moves' `ReturnToSource` (Plan 4 Task 2) accepts that state: exec `""` or `"none"`, and either `evidence_target` or a `loss.<id>` journal entry.
- **Activation prerequisites are claim Plan 8b's, not Plan 8's** (Plan 8 "Rollout runbook", line 5678): the authenticated release identity behind the floor, the root-signed actor directory and census, and the live quarantine, declare-lost and readmission wiring. Plan 4 Task 10 can be built against Plan 8's library and tests, but `carlos move resolve --declare-lost` stays refused in production (the door answers "declare-lost is not enabled in this deployment yet") until Plan 8b lands and Paul rules on #236's P1 to P3. A direct `park.Local.RetireInstallation` is the build-time stand-in for the returning box's quarantine in tests only; it is not an activation path.

**From claim Plan 5 (`feat/hostclaim-console`, package `internal/hostops`):**

- `hostops.Controller.Adopt(ctx, r hostops.Record) (hostclaim.Observed, error)`: a legacy host has no claim until adoption; the move door refuses a claimless host ("the claim not `serving`", §7.1), so every rehearsal host is adopted first. No moves code calls it.
- `Recovery.Pass(ctx) error` and `(s *Server) StartClaimRecoveryPass(ctx)`: Plan 5 Task 7's `movesRecoveryPass` is written to be called from `Recovery.Pass` once it exists; until then the console runs it on its own ticker.
- `hostops.RunOwnedRemote(ctx, e *park.Engine, w park.Work, scope ParkScopeProvider) error` and `ParkScopeProvider func(context.Context, hostclaim.Observed, hostclaim.Obligation) (hostclaim.ParkScope, error)`: the regional runner's scope provider for moves' chores returns the persisted scope the chore carries (Plan 5 Task 3 binds it at the queuing CAS), never a computed one.

## BLOCKED-ON-CLAIM contracts (rev 3)

Platform-1e's work order (2026-10-06): (1) claim Plan 6 Tasks 6 to 11
built to the signatures below: `startAdmitted`, K6 in the wake
admission, `SelectedTimers` for held rows, `Regional.Run` for
`park-replica` moves scopes; unknown kinds stay refused. (2) Plan 8b:
roster, census, release identity and guard, the termination fence, the
real declare-lost and readmit. (3) Plan 5 Tasks 5 to 9: the move, flip,
commit, rollback and delete transitions, plus the delete's two parks.
Claim rev 6.27 (989ee349) §1.1 records Paul's #236 rulings; P7
(cold-only) binds every plan here.

Every task marked **BLOCKED-ON-CLAIM** names one of these contracts and
is not built until the contract is merged on `main`; the moves plans do
not guess the API beyond what is written here.

Owners (the coordinator, 2026-10-06): **C1 to C4 are claim Plan 6 rev 2, merged to `main` as d649d8d4 on 2026-10-06** (claim Plan 6b, the production assembly, is being drafted; Plan 8b, floor and roster for C6 and C7, is heading to CI),
**C5 is claim Plan 5**, **C6 and C7 are claim Plans 8 and 8b**, **C8 is
region HA's** (plan 1a). Claim rev 6.28 (4ffe8a0d) adopts region HA's L1
to L20; spec rev 7.20 aligns with it (the fence reference, the pre-flip
source conversion, L13, the one-box scope).

| id | contract moves needs | from | blocks |
|---|---|---|---|
| C1 | **On `main` at d649d8d4 (claim Plan 6, merged 2026-10-06; inert while `CARLOS_HOST_CLAIM` is unset).** `(a *Service) startAdmitted(ctx, e *actEntry, admitted hostclaim.Observed, policy startPolicy) error` with `startPolicy{MaxStarts int; Window time.Duration; Deadline time.Time; Probe bool; Prepared *moves.InstallManifest; Inputs *provision.MoveInputs}`: the admitted writer's start of claim rev 6.10 §6.4a (purge, restore of the admitted baseline or the prepared generation's pinned snapshot, start, readiness, first upload, `generation.json` by If-Match or If-None-Match, `PublishGeneration`, open), callable for a **held** row, returning `ErrAttemptOver` when the policy's window or deadline passes before readiness and never marking the host failed when `Probe` is true. Cold only (P7): the sequence never consults a certificate while P7 stands. | claim Plan 6 Tasks 7, 9 (work order 1) | Plan 3 Tasks 5, 6, 7; Plan 4 Task 9 |
| C2 | **On `main` at d649d8d4 (claim Plan 6, merged 2026-10-06; inert while `CARLOS_HOST_CLAIM` is unset).** K6 in `doWake`: a fresh claim read; when `op.exec` does not name this box the host's process tree is killed (`stopevidence.Reconstruct` step 1) before anything else; a wake is admitted under `landing/committed` for the target and under `reverting`/`restoring` phases `probed` and `opened` for the source; the restore writes `<dbPath>.gen` and retires a warm database whose `.gen` names another generation. | claim Plan 6 Task 9 | Plan 3 Task 7 (serving after the open); Plan 4 Tasks 5, 6 (serving after `opened`) |
| C3 | **On `main` at d649d8d4 (claim Plan 6, merged 2026-10-06; inert while `CARLOS_HOST_CLAIM` is unset).** `schedule.SelectedTimers` accepts a held row's write when the selection names the hold's op, and gains `ReplaceTimers(host string, rows []registry.Timer) error` delegating to `registry.ReplaceTimers` under that selection. | claim Plan 6 Task 7 | Plan 2 Task 10 `local` (until merged, `local` calls `registry.ReplaceTimers` directly behind the switch D13) |
| C4 | **On `main` at d649d8d4 (claim Plan 6, merged 2026-10-06; inert while `CARLOS_HOST_CLAIM` is unset).** `hostbox.Regional.Run` runs `park-replica` chores scoped to `moves/<host>/<op id>` and to `instances/<host>/<inc>`, and `park-generation` chores, under move and rollback op ids; its `Leases.Acquire(bucket, location)` **shares the activator's held lease for a location this box itself serves** (an aggregate lease adapter, claim Plan 5 D3's wording), else a landing's residue chores and the at-rest sweep on a serving host can never acknowledge (`park/remote.go:404` acquires the location's lease). | claim Plan 6 Task 10, Plan 5 Task 3 | Plan 5 Tasks 1, 3, 5 acknowledgement after the horizon (the immediate half runs without it) |
| C5 | Production callers for `Controller.ReconcileClaims` inside the frozen, signed #232 ownership backfill and for `(s *Server) StartClaimRecoveryPass` (started from serve, callable from the moves console pass, stamps `TargetParkedAt`): a legacy host gets its claim on the next reconcile pass of that backfill, never lazily from the door or the recovery pass (claim §1.1 P4, Plan 5 rev 2 at 2d67ec47). | claim Plan 5 integration (work order 3) | the rehearsal (Plan 7 P-5); the request door's "no host claim yet" refusal is correct meanwhile |
| C6 | The loss sequence, end to end: `CreateLossRecord` of a `prepared` record with `WriteSources` composed as **the latest target session as reason `target` and every earlier target session since the cut and, when `src_covered` is false, the source's cut session as further sources** (`lost.go:149-191` requires the frozen set to equal the admission's primary `WritesLost` plus `AdditionalWritesLost`, unique by `(box, generation, session)`, each with a validated baseline); the transition `prepared → committed` with `ResultClaimGen` after `DeclareLost` (`lost.go:119` admits only a `committed` record); `DestroyedStorage` attestations for `DischargeLocal`; `Enrollment` and `QuarantineRevision` as the roster supplies them (Plan 8b), and a fence form of the declaration with neither (region HA L2 to L4). Moves supplies `moves.LossOwner` (`ValidateDeclareLost`, `ValidateLossRecovery`) and the record-side `lost` phase; the claim owns the record's lifecycle and the exact `LossRecord` composition. | claim Plan 8 + 8b (work order 2) | Plan 4 Task 10; the lost-target branch of Plan 4 Task 5; Plan 6 Task 1 `--declare-lost`; Plan 7 Run 4 |
| C7 | **Settled by claim rev 6.28 §7.3 (spec rev 7.20 §7.10):** `op.journal["fence.<box>"] = {"enrollment","key","sha256"}` names `control/box-fences/<enrollment>.json` and the digest of the bytes verified; a `loss.<id>` intent cites the same reference; consumers accept a fence only through it, re-verifying the record; an automatic declaration needs no quarantine revision and no operator. Still owed by Plans 8/8b: the fence record's writer and the verification helper (`hostclaim.VerifyFence(ctx, s, ref) error` or its real name) and the one-CAS conversion of a pre-flip `moving` op to `relocating` at `pinning` (`hostclaim.ConvertMovingToRelocating(opID, fence FenceRef) func(*Claim) error` or its real name: exec `"none"`, stamp and generation preserved, obligations untouched, `relocating`/`pinning` in the source region; refused once the record is `landing`). Moves' `ReturnToSource` (Plan 4 Task 2) and the console pass (Plan 5 Task 7) consume both. | claim Plans 8 and 8b | Plan 4 Task 2's fence form; Plan 5 Task 7's conversion call; Plan 6 Task 2's `waiting for a box in <region>` state |
| C8 | Region HA's files publisher API in two forms (spec rev 7.22 §7.6, L17, R18-1, R19-1; claim rev 6.30 73b1b9dc). The **prepared-manifest build**, `files.BuildPrepared(ctx, store lease.VersionedStore, location, generation, opID string, stamp uint64, dir, envPath string, wake WakeState) (manifestVersion string, err error)` or its real name: before any session exists, under `op.exec`'s installation authority, create-only content objects plus one create-only manifest bound to `{location, generation, op id, installation stamp}` where `generation` is the installation's **prepared destination generation** (`g-<move id>`, or `g-<rollback id>` for a dirty return), never `holder.generation`, which stays the source's until the probe's admission (R19-1); exact versions, sequence 0, never replaced, retired with its generation. The **session-bound publisher**, `files.Publish(...)` or its real name (claim C3), which starts after the first admission, takes the prepared manifest by exact version as its baseline and publishes from sequence 1, writing origin-qualified references (P6-D3); and the reader a cold admission uses to establish its files manifest from the admitted predecessor chain by exact version. | region HA plan 1a | Plan 2 Task 10 `local`'s manifest step (6) (`.files-gen` alone until then); Plan 3 Task 5's admitted start (files from the manifest once C8 exists) |

### Shared contract types (coordination with claim Plan 6 Task 6)

Claim Plan 6 (merged to `main` as d649d8d4, 2026-10-06) implements C1 to C4 and owns two files our plans also name. **Settled:** the types are on `main`; Plan 1 imports `provision.MoveInputs`, Plan 2 imports `moves.InstallManifest`, Plan 3 imports `moves.ErrAttemptOver`; none of our tasks defines them. Shapes verified identical against bf63e250 and d649d8d4.

| type | claim Plan 6 file | our definition | shape check |
|---|---|---|---|
| `moves.InstallManifest` | `internal/moves/install_contract.go` | Plan 2 Task 8 | identical: `V int; Op, Host string; N uint64; SnapshotKey, SnapshotVersion string; SnapshotCRC64 uint64; AnchorKey, AnchorVersion string; AnchorCRC64 uint64; IntegrityCheck string; DBSHA256 string; BuiltAt time.Time`; snake_case json tags (final): `v`, `op`, `host`, `snapshot_txid` (Go field `N`), `snapshot_key`, `snapshot_version`, `snapshot_crc64`, `anchor_key`, `anchor_version`, `anchor_crc64`, `integrity_check`, `db_sha256`, `built_at`; Plan 2 Task 8 expects and tests them; spec rev 7.22 §7.6 names them |
| `moves.ErrAttemptOver` | `internal/moves/install_contract.go` | Plan 3 Task 1 | identity only (`errors.Is`); Plan 3 now uses claim Plan 6's message text |
| `provision.MoveInputs` | `internal/provision/moveinputs_contract.go` | Plan 1 Task 2 imports it (its own definition removed in rev 6) | identical fields and json tags: `bundle_etags`, `overlay_etag`, `release`, `files_plain`, `env_report`, all `omitempty` |

Plans 1, 2 and 3 each carry the import rule at the definition site.

## Decisions locked across the series

- **D1 Journals.** The record's `move` field carries the move's phase
  journal (§7.2). The claim's `op.journal` carries what the claim half
  must read with the same fresh read as an admission: `seal` (the JSON of
  `moves.Seal`), `src_gen`, `source_stamp`, `cut_session`, `rev_phase`,
  `evidence_source`, `evidence_target` (deployment-bucket keys, never the
  evidence inlined; K13).
- **D2 zstd.** The archive is tar then zstd (§8.1). The repository pins no
  zstd library; Plan 1 adds `github.com/klauspost/compress` (pure Go) as
  the one new module dependency of the series.
- **D3 Staging-work lease.** `lease.AcquireTTL(ctx, deploymentBucket, "control/staging-leases/"+targetRegion+"/"+host, deploymentRegion, moves.StagingLeaseTTL)` is the claim's shared staging-work lease (§7.3), with `StagingLeaseTTL = 45 * time.Minute` (the ordinary 15-minute `LeaseTTL` cannot leave the signature's 15-minute life plus skew in front of `SafeUntil`). Every staging request goes through `moves.Guarded.Before(fn func(signAt time.Time) error)`, which checks `SafeUntil()` once and hands `fn` the very timestamp it checked; the staging mirror (`lease.NewStagingMirror`, hand-built SigV4 requests, no SDK retries, a 120 s request timeout, no clock of its own) signs each request exactly once at that `signAt`, so a pause of any length between the check and the signing yields a signature dated at the check, which S3 refuses once that date is 15 minutes old; the lane's own retry loop makes a new check for each new signature (Plan 1 rev 5, round-4 finding 3). The residual gap, a pause between the signing and the socket write, is the pause assumption the spec names (§7.3, model 14), bounded by the skew allowance and the request timeout.
- **D4 Seal versions from the copy responses.** `lease.MirrorPrefix` records each copy's destination `versionId` in the receipts through an optional `lease.VersionedCopier` interface that **only the guarded staging adapter implements** (`guardedMirror` over `*lease.StagingMirror`, Plan 1 Tasks 6 and 14; the ordinary `s3Mirror` does not, so the legacy path stays plain, rev 5), so the source never needs to read the target's `moves/*` (M17). The seal is the receipts' versions for the current source keys, and a receipt is trusted only when it matches the current source ETag and size **and** the current destination ETag; anything else is recopied, which yields a fresh version id.
- **D5 The sealed restore is litestream's own planner.** `moves.SealedClient` implements `litestream.ReplicaClient` over the target bucket's versioned store and serves exactly the sealed `(key, versionId)` pairs; `litestream.NewReplicaWithClient(nil, client).Restore` builds the database (the pattern `internal/lease/lease.go:404` uses). Nothing reads the current listing.
- **D6 Park kinds and destinations (rev 3, after platform-1e's interface check).** No new obligation kind and no new destination. A staging lane is a `park-replica` scoped to `moves/<host>/<op id>` (the grammar claim rev 6.23 §2 allows). The source's finalization park and a rolled-back target's cleaning park are `park-replica` scoped to the whole location `instances/<host>/<inc>`: in that region the location is no longer authoritative, so `park.Engine`'s live-set rule (`remote.go:346`) selects every version. A landing's residue from an earlier stay in the target region is parked as one `park-generation` chore per residue generation (`@root` for the origin), the same decision `park.Decide` makes for the at-rest sweep, because a whole-location park there could never acknowledge once the landing's generation is live. The destination is the engine's `parked/<incarnation>/<host>/<retirement id>/`, and F3 selects a move's retirements by the manifest's `Op`, not by path. Spec §6.4's `superseded/<op id>/...` wording is therefore a spec amendment for rev 7.20 ("the destination is the claim engine's, the op is in the manifest"); the invariants it served, one manifest per retirement event (D5), nothing live overwritten, INV-10, hold. Reason no new kind is needed: nothing in a move reads superseded data back (a dirty return seals the target's live installation before `cleaning` parks; a clean return restores from the source's live replica; F3 deletes after M7's hold, by manifest), so the engine's existing kinds and paths carry every property the spec asked of `superseded/`.
- **D7 Reconcile learns holds from the host package.** `provision.ReconcileConfig` gains `Hold func(Record) (status, op string, ok bool)` and `KeepParticipant func(host string) bool`; the host package supplies both from its claim reads. Reconcile itself never reads a claim.
- **D8 The console's move pass.** `movesRecoveryPass` (Plan 5) writes the claim half the record decided (§7.5) and runs the deadline sweeps. It is a plain function so claim Plan 5's `Recovery.Pass` can call it.
- **D9 `provision.MoveStart` joins `provision.MoveRequest`** in Plan 1 (rev 4, under D13): a move without an id, an incarnation and a cut mode has no journal to run, so the new path starts with `MoveStart`; `MoveFlip` and `MoveClose` join `MoveComplete` in Plan 2. The legacy transitions, the legacy worker and the old staging-wins landing (`promoteStagedReplica`) stay reachable and unchanged while the switch is off and are retired together in Plan 6 Task 8; no earlier task deletes or renames them.
- **D10 Flat landing only.** Phase 1 lands flat (§6.2, §21); a nested source is refused at the door and at every box step.
- **D11 The terminal outcome survives the close.** Every close CAS (`done`, `rolled_back`, `aborted`) writes `Record.LastMove = &MoveOutcome{ID, Outcome, At, OfflineS}` as it clears `Move`, so a CLI that missed the intermediate states reads the outcome of the move id it requested from `GET …/move` (`{"state":"none","last":{…}}`) and exits with the right code.
- **D12 Order within the series.** The copy gate (the record file and the ship check) is Plan 1 Task 15, so every later plan's strings are checked from the day they are written; the paging primitive `host.PageOperator` is Plan 5 Task 0, before the console pass that needs it; `carlos move`'s dispatch and its `docs/site/cli.md` heading land in one task (Plan 6 Task 1), so no commit fails the docsite gate. `registry.DeleteIfHold` is Plan 1 Task 12's (the `hold_op` task, built before the floor task that reads it), beside `ReleaseHold`; `hostclaim.ReleaseMove` is Plan 1 Task 3's (Plan 4 widens its legal states); `MoveGuard` is Plan 1 Task 2's; `RenderHeld`, `MaterializeHeld` and the strict spawn are Plan 1 Task 7b's; Plan 1 Task 11's host-side report pass is built inside Task 14 (it needs that task's harness).
- **D13 The operator-off switch.** Every Phase 1 entry point (the new request door and its siblings, the Phase 1 worker, the console's move pass, `carlos move`) is behind `CARLOS_MOVES_PHASE1=on`, read by the console and the agent, **off by default** (Plan 1 Task 0 lands it first). **Off means `main` behaves exactly as today**: the legacy move door (Instance role, `MoveRequest`), the legacy worker (preparing, ready, the staging-wins cut, aborting) and `promoteStagedReplica` stay reachable and unchanged, and every existing test keeps passing unchanged; the new code is unreachable. On: the Phase 1 path handles id'd records and refuses legacy ones. The legacy path is retired in one final task (Plan 6 Task 8) after Paul approves the copy record and the default flips. The switch is spec §15 step 4's dormancy made concrete: the series merges to `main` task by task without exposing a half-move, and the default stays off until every plan is merged and Paul's rehearsal order begins (Plan 7 P-1 turns it on per deployment). The copy gate ties to it: `TestPendingCopyIsNotWired` becomes `TestPendingCopyKeepsTheSwitchOff`: while any row of the copy record is `pending`, `moves.DefaultEnabled` must be `false`, and no task may flip it. Pending strings may therefore be wired as the tasks are built; nothing ships to an operator until Paul approves the record and the switch default changes in one commit. **One exception, by design: the protocol floor (§11) is armed by publishing `control/moves-protocol.json`, an operator action, not by the switch; unpublished, it changes nothing, and once armed it stays armed with the switch off (Plan 1 Task 13, rev 6).**
- **D15 One-box regions only (spec rev 7.20 §7.1, the coordinator's scope ruling).** The request door refuses a move into or out of a region with more than one eligible box (M-C21, pending Paul), computed with main's `eligibleBoxes` and `chooseBox` (`internal/console/boxchoice.go`), which main's tests exercise but no production door calls yet: Plan 1 Task 4's `selectMoveBoxes` is their first production caller and composes every input itself (`liveEdges`, `regionCands` for the AWS region, `boxid.Verifier` over `boxid.Load` with the new `Config.AWSAccount`, `expectedSecretsFP`, `lost` nil, `running` nil); `choose()` therefore names the region's one box, `chooseBox` is unchanged, and its tie-break is unreachable in Phase 1 (region HA owns the hash form, L15). The model stays at one box per region (spec §14.2.6); the two-box model is not built in this series. Plan 1 Task 4 carries the refusal; Plan 7's rehearsals run only in one-box regions.
- **D16 A source terminated before the flip (spec rev 7.20 §7.10).** The fence converts the `moving` op to `relocating` at `pinning` in one claim CAS (C7's claim-side helper); the moves side writes the record's `move.state = "relocating"` with `reason` and, in a one-box region, shows `waiting for a box in <region>` (M-C22) and pages once (Plan 5 Task 7, Plan 6 Tasks 2 and 5); the lane's `park-replica` runs under the target region as for an abort; the move's record clears when the relocation releases to `serving` (the console pass, on `serving` with `rec.Move.ID == op id`). Nothing in Phase 1 builds the relocation itself: that is region HA's.
- **D14 Cold only (P7, claim rev 6.27 §1.1).** No task passes `Mode: "warm"` or `PredecessorCertified: true` to `AdmitWriter`; no task writes `closed/<session>.json`; `KeepStamp` follows the library's rule unchanged. The budget already prices every attempt as a cold admission (Plan 1 Task 10); the per-wake `park-generation` chore is outside the cut window.

## Global constraints (every plan repeats these)

- Gate before every commit: `go build ./...`, `go vet ./...`, `go test ./... -count=1`, `gofmt -l .` clean, `make test`; cross-compile `GOOS=darwin`, `GOOS=windows`, `GOOS=linux GOARCH=arm64` (AGENTS.md "Build & test"). Go uses its default build cache (`~/.cache/go-build`, writable inside the sandbox; never a cache under `/tmp`, the machine's rule); `GOFLAGS=-buildvcs=false` when the worktree's VCS metadata is unreadable from the sandbox.
- Cold only (P7, D14) and the operator-off switch (D13) in every plan.
- CPU policy: tests serial (`-p 1` when the whole tree runs), niced, no deep simulations. No new sweep of `internal/sim/hostlife` runs in these plans.
- Copy: every user-facing string goes through the copy-review gate before it ships; no em dashes anywhere (a full stop and a new sentence instead). Plan 6 Task 7 is the gate.
- Nothing rolls to production without Paul's go. Rehearsal order is M12's: the probe app, a disposable instance, kpmg (Plan 7).
- A new `carlos` command is not done until `docs/site/cli.md` names it (the `internal/docsite` gate).
- Wire shapes are additive: every new record field is `omitempty`, every new registry column has a default, every new claim journal key is a string.

## Review round 1 (Astra, codex, on 616f2499): dispositions

The findings are committed unedited as `CODEX-PLAN-MOVES-PHASE1-R1.md`
beside this index. Verdict then: PLAN: NOT READY. Every finding was
verified against the pinned main checkout and accepted; where each is
fixed:

| # | finding | fixed in |
|---|---|---|
| 1 | `MoveLanding` type and constant collide | Plan 1 Task 2: the journal type is `MoveLandingJournal` |
| 2 | `activator` importing `stopevidence` is a cycle; `a.cgroups` is no field | Plan 2 Task 3: `stopevidence.Reconstruct(ctx, ReconstructInputs{Cgroups: eb.Cgroups, …})` |
| 3 | `spawnClaimed` returns `(*exec.Cmd, *cgroup.Prepared, error)` | Index interfaces, Plan 3 Task 5: the handle is kept and cleaned on every exit path |
| 4 | a receipt's version trusted on ETag equality alone | Plan 1 Task 5 and D4: a receipt vouches only when it matches source and destination completely; else recopy |
| 5 | a 15-minute lease never passes the guard | Plan 1 Task 6 and D3: `moves.StagingLeaseTTL` 45 min, `lease.AcquireTTL`, a test on real settings |
| 6 | the SDK signs at send and re-signs retries | Plan 1 Task 6 and D3: `lease.NewStagingMirror` with no SDK retries and a 120 s timeout; one request per `Before`; lane retries re-check |
| 7 | `move.json` replay needed a forbidden read | Plan 1 Task 14, Review Focus 2: ledger intent first; `ErrExists` with a pending intent is our own PUT; without, refuse; the target validates |
| 8 | an application file named `env` collides with the overlay | Plan 1 Task 7: `files/` and `env` namespaces, duplicate destinations refused, round-trip test |
| 9 | T9 needs the target box in `touched` before the flip | Plan 1 Task 14: the first `Touch` adds the target region and box |
| 10 | `Put(..., "")` is create-only; reports could not refresh | Plan 1 Task 11 `moves.PutReplace`, Task 12, Plan 6 Task 3 corrected |
| 11 | the floor failed open on a read error; the door ignored completeness | Plan 1 Task 12: the floor is armed locally; unknown refuses a participant; the door needs `readEdgesSnapshot(...).complete()` |
| 12 | stop evidence published before its final proof | Plan 2 Task 5: `stopping` keeps steps 1 to 4 local; the complete report is written at `stopped` before `ExecToNone` |
| 13 | `archived` persisted before the archive was in the seal | Plan 2 Task 5: publication and the seal complete before the phase CAS; the flip checks `seal.Files` |
| 14 | a paused tick could slip the drain | Plan 2 Task 6: the hold check runs after `claim`; `TestDrainRaceCannotAdvanceBookkeeping` |
| 15 | the LTX encoder refuses the lock page | Plan 2 Task 8: `ltx.LockPgno` skipped, `Commit` kept, a >1 GiB sparse test |
| 16 | a replayed build restored over an existing file | Plan 2 Task 8: the scratch database is removed before every restore |
| 17 | `RetireArtifacts` without a persisted chore does nothing | Plan 2 Tasks 9 and 10: `QueueLocalPark` and `RecordInventory` at `checking`; the test asserts the filesystem |
| 18 | the first probe's start check compared against the source generation | Plan 3 Tasks 4 and 5: `PreparedCheck` before admission; `StartCheck` after the CAS inside the admitted sequence |
| 19 | the claim never reached `probing`/`probed` | Plan 2 Task 10 and Plan 3 Task 6: every landing phase mirrors into the claim with `SetMovePhase`; the commit test drives it |
| 20 | declare-lost could override a winning commit | Plan 4 Task 10: the record decides first (`MoveRollBack`, `MoveRollbackTargetLost`), the claim follows |
| 21 | the loss payload did not match claim Plan 8 | Plan 4 Task 10: the exact `LossRecord`, `LossCloudObservation`, `LossOperatorAttestation`, `LossConsequences` fields |
| 22 | recovery could not consume the declaration result | Plan 4 Task 2: `ReturnToSource` accepts exec `"none"` and a `loss.<id>` journal; Task 10 runs the source pass against a real declaration |
| 23 | orphan claims were never visited | Plan 5 Task 7: claim discovery from `control/hosts/`, `ReleaseOrphanMove` |
| 24 | the CLI inferred the outcome from the last state seen | D11 and Plans 1, 3, 4, 5, 6: `Record.LastMove`, `GET …/move` returns `last`, the CLI reads it |
| 25 | the probe app's launch contract differed from the backend | Plan 7 Task 1: `--socket`, `--db`, `TestMoveprobeLaunchContract` |
| 26 | activation depends on claim Plan 8b | Index interfaces: 8b's prerequisites named; declare-lost stays refused until 8b lands |
| 27 | forbidden transitions requested | Plan 4 Task 1: `MoveStuck` from `restoring`/`verified`, `MoveRollbackTargetLost` |
| 28 | reverting had no staging cleanup producer | Plan 2 Task 2 `Revert` queues the park; Plan 4 Task 6 waits; Plan 5 Task 1 dispatches |
| 29 | the MinIO test would never run in CI | Plan 1 Task 11: added to `30-minio-contract`; failing cases for the stubbed hooks |
| 30 | task-order cycle and a failing docsite commit | D12: `PageOperator` is Plan 5 Task 0; dispatch and heading in Plan 6 Task 1; the copy gate is Plan 1 Task 15 |
| 31 | the copy check needed an independent inventory | Plan 1 Task 15: `movesCopyIDs` pinned, negative fixtures |
| 32 | the clean-rollback rehearsal wrote before refusing | Plan 7 Tasks 1 and 3: the before-write mode suppresses every mutation |

## Review round 2 (Astra, on da2832aa): dispositions (plan rev 3)

Findings file: `~/.cache/nearest-region/CODEX-PLAN-MOVES-PHASE1-R2.md`
(the coordinator's record; not re-reviewed, per the coordinator). The
locked design stands; the work split into local fixes (rev 3) and
contracts (the BLOCKED-ON-CLAIM table above).

| # | finding | rev 3 |
|---|---|---|
| 1 | `complete()` needs `BoxesComplete`, which `readEdgesSnapshot` never sets | Plan 1 Task 4: the door checks `snap.Complete` (every edges file read), the evidence the floor needs; liveness is not the floor's question |
| 2 | `MoveStuck` constant and function collide; a two-argument `Revert` call remained | Plan 4 Task 1: the transition is `MoveMarkStuck`; Plan 2 Task 5's call takes the five arguments |
| 3 | `Claim.Validate` rejects the new kinds | D6: no new kinds; `park-replica` scopes and `park-generation` chores; `Engine.Parked` stays |
| 4 | the dependency graph understated the integration | the BLOCKED-ON-CLAIM table and platform-1e's work order; `DeleteIfHold` moved to Plan 1 Task 13 |
| 5 | intermediate merges exposed half-moves | D13: the operator-off switch `CARLOS_MOVES_PHASE1`; the interim abort releases the claim (`ReleaseMove` in Plan 1 Task 3) |
| 6 | return-lane writes outside the lease and signing contract | Plan 4 Task 4: the lane is written under the source region's staging lease through the guarded staging mirror; the bound is the return seal's |
| 7 | `pending` before the PUT defeats the foreign refusal | Plan 1 Task 14: the target validates `move.json` (its own bucket) in T9 and at `checking`; the source's ledger decides only its own replay; a foreign object is caught by the target, never by the source guessing |
| 8 | inventory registration against the real path/stamp contract | Plan 2 Task 10: relative paths; the old stay's entries retired by their own tag before the new installation registers under F; `.gen` included |
| 9 | the landing held the lease the engine acquires | Plan 2 Task 10: the location lease is taken after `parking`; the acknowledgement half is C4 |
| 10 | a whole-location park could not finish once the new generation was live | D6: residue is parked per generation (`park-generation`) |
| 11 | record/claim phase mirroring wedged on a half write; `ExecToNone` replay | Plan 2 Task 10 `syncClaimPhase` catch-up; Plan 2 Task 2 `ExecToNone` idempotent on a matching journal |
| 12 | the finalization stamp could be lost | Plan 3 Task 8: `row-deleted` in the op journal before the delete; the entry condition reads it |
| 13 | dirty-return probes rejected their prepared generation | Plan 3 Task 5: `Op`, `HoldOp` and `PreparedGeneration` distinguished; `PreparedCheck` for every prepared install |
| 14 | the launch-input pin was never enforced at spawn | Plan 3 Task 5 step 2: render, then compare with `move.inputs` (landing) or record `recovery_inputs` (recovery) |
| 15 | declare-lost rewrote the state its legality check read | Plan 4 Tasks 1, 10: a distinct `lost` target phase the ordinary recovery cannot consume |
| 16 | the loss flow cannot pass the real Plan 8 API | C6; Plan 4 Task 10 BLOCKED-ON-CLAIM |
| 17 | case (c) performed regional retirement | Plan 4 Task 10: a separate consequence path |
| 18 | `TargetParkedAt` had no surviving producer after a loss | Plan 4 Task 10 and Plan 5 Task 7: the console pass stamps it when no chore owned by the lost box or its region remains under the op |
| 19 | the readiness window started before the restore | Plan 3 Tasks 1, 6: `Opened` is the first app start, written by the probe; attempts are accounted separately |
| 20 | randomized sealing broke the replay identity | Plan 2 Task 5: the archive identity (manifest, sealed key, temp file) is journaled before any PUT |
| 21 | the converger could write after the barrier | Plan 2 Task 6: a shared `schedule.HostGate` the runner, the converger and the cut all take |
| 22 | T11 used example rates | Plan 1 Task 11: measured rates with a one-hour freshness; missing measurements fail T11 |
| 23 | log keys gave no sequencing guarantee | Plan 6 Task 3: transition-identity keys, create-only |
| 24 | copy approval and prerequisites after their consumers | D13 (the switch ties approval to enablement); `hostguard.Capture` returns `(Captured, error)`; the lease fixture is `leasetest.NewVersionedBucket` + `NewS3Server`; `ProtocolVersion` stub stays in Task 4 |
| 25 | the loss rehearsal lacked its prerequisites | Plan 7 P-6: Plan 8b, C5, the switch |
| R1-6 | the SDK signs at send | Plan 1 Task 6: the staging mirror signs each S3 REST request by hand with the SDK's SigV4 `Signer.SignHTTP` at the check's clock (`signingTime`), no presigner (S3 v1.105.0 has none for copies), and sends once with no retry |
| R1-8 | `Extract` lacked duplicate-destination rejection | Plan 1 Task 7 |
| R1-11 | unknown-floor participation missed preparing participants | Plan 1 Task 12: a participant is a held row **or** a record whose move names this box |
| R1-27 | Task 7's retry contradicted Task 1 | Plan 4 Task 7 resumes at `StuckAt` |

## Plan 1 review (Astra, on d5716bf4): dispositions (Plan 1 rev 4)

Findings file: `~/.cache/nearest-region/CODEX-PLAN1-REVIEW.md` (the
coordinator's record). All fourteen fixed in Plan 1 rev 4:

| # | finding | rev 4 |
|---|---|---|
| 1 | the switch did not protect intermediate commits; off changed main's behaviour | Task 0 lands the switch first with `moves.DefaultEnabled`, `Config.MovesEnabled`, `moveCfg.Enabled`; D13 redefined: off = today's legacy path unchanged and reachable; the legacy path is retired in Plan 6 Task 8 |
| 2 | deleted consumers and a duplicate `servingClaim` | Task 2 keeps `MoveRequest` and `MoveComplete` and their tests; the hostclaim fixture is `movingTestClaim` |
| 3 | no copy presigner in S3 v1.105.0 | Task 6 signs each REST request with `v4.Signer.SignHTTP` at the check's clock |
| 4 | Task 5 broke plain mirroring | the plain `MirrorStore` path is unchanged; the receipt rule applies only to a `VersionedCopier` |
| 5 | the source copy was not bound to the claim | Task 14: `moves.ReplicaSource(holder)` and `lease.MirrorPrefixKeep`; legacy root handling; a multi-incarnation test |
| 6 | staging writes bypassed the guard; revalidation before the lease | Task 14: one guarded path for every write class including `move.json` and the `probe/` object; lease first, then the claim read |
| 7 | worker transitions lacked op identity | Task 2's `MoveGuard` and the guarded promote, cut, abort and abort-complete; `TestRoundNeverAdvancesAReplacementMove` |
| 8 | red-after-fix tests | the legacy fixture uses `MoveRequest`; `putSized` and `Changed`; the lease test is arithmetic plus `RenewMargin`, `AcquireTTL` under lease's own S3-gated tests; the signing test uses `leasetest`'s server and the `X-Amz-Date` header; the legacy API test stays, a Phase 1 twin releases the claim between requests |
| 9 | launch composition differed from `Start`'s | Task 9 composes through `agentOwnedVars(rt, fileEnv, haveControl)` and tests removals; `CompareEnv` reports a missing region and layout names absent from both |
| 10 | T5 did not verify the archive | Task 7's `archive.Verify` and its negative tests |
| 11 | extraction changed modes | Task 7 restores exact file and directory modes, deepest directory last |
| 12 | forward dependency and the rejected completeness check | the `hold_op` task is Task 12, before the floor task 13; `snap.Complete` only |
| 13 | target selection ignored rev 7.19 | Task 4 uses `eligibleBoxes` and `chooseBox` with the approved SHA-256 tie-break, refuses multi-box regions (D15), binds T8 and T9 to `op.intent.target` |
| 14 | contradictory Task 14 tests | the source-refusal test is gone; the target validates the lane; the self-review note matches |

Also from the review: T7 is `release.MaterializeHeld` and S2 is the strict next spawn, both real in Task 7b; S1 wakes a hibernating source through the box's own edge on loopback.

## Plan 1 review round 4 (Astra, on d0907747): dispositions (Plan 1 rev 5)

`CODEX-PLAN1-REVIEW-R4.md` beside this file: PLAN 1 NOT READY TO BUILD, 6 P1, 8 P2, 1 P3. Every finding fixed in Plan 1 rev 5; no second review was run (the coordinator's instruction: fix once and stop).

| # | finding | disposition |
|---|---|---|
| 1 | P1 Task 4's selection not buildable: `snapshotFor` undefined, `eligibleBoxes` takes seven arguments, no production caller on main, `chooseBox` has no host argument | **Fixed.** `selectMoveBoxes` is a complete adapter: `liveEdges` (the live snapshot `complete()` needs), `boxid.Load` + `boxid.Verifier{Account: cfg.AWSAccount, Certs}` per box, `regionCands` for the AWS region, `HomeRegion`, `expectedSecretsFP`, `lost` nil (C6), `running` nil; `chooseBox` unchanged, its tie-break unreachable under the one-box refusal; tests with real signed `boxid` records. `Config.AWSAccount` (`CARLOS_AWS_ACCOUNT`) is new and named for the roll notes. D15 updated |
| 2 | P1 Task 7b consumes `moves.Check` before Task 11; `Cgroups` is concrete | **Fixed.** `moves.Check` is Task 1's (`check.go`); the strict spawn goes through an unexported `prepareStrict` seam over a `strictProc` interface `*cgroup.Prepared` satisfies; `Cgroups` stays `*cgroup.Manager` |
| 3 | P1 signing time later than the lease check | **Fixed.** `Guarded.Before(fn func(signAt time.Time) error)` hands fn the checked timestamp; every `StagingMirror` mutation is an `At` method taking it; the mirror has no clock; the signing test pauses past the lease between check and signing and asserts the check's `X-Amz-Date`, then a refused third `Before` |
| 4 | P1 generation-source prefix wrong for valid locations | **Fixed.** `ReplicaSource` normalizes `TrimSuffix(Location, "/") + "/"` as `genselect.requestLocation` does; the test's holder comes through `hostclaim.Update`; every fixture location lost its trailing slash |
| 5 | P1 the floor misses the units' direct `carlos.prev` exec | **Fixed.** The agent's hold-prev marker `/data/.moves/hold-prev` (participant and no floor, or `.prev` below the floor); both units' `ExecStart` conditions skip `.prev` under it; unit assertions and a shell-level test |
| 6 | P1 T7 and the floor file write `/opt` from the sandboxed agent | **Fixed.** `MaterializeHeld` returns `ErrStageBlocked`; the pass relays through the existing `stage-requested` marker and `carlos adopt --stage-only` runs `release.ServeHeldStageRequests`; T7 passes on `LiveVersion` only; the floor file moved to `<DataDir>/.moves/floor.json` |
| 7 | P2 `s3Mirror` as `VersionedCopier` changes the legacy path; Task 13 undefined dormancy | **Fixed.** `s3Mirror` implements neither interface (pinned by test); the only producer is the staging mirror through `guardedMirror`; console record and floor arming run only with the switch on, pinned by tests |
| 8 | P2 `Verify` hashes the compressed stream | **Fixed.** `h` hashes the decoded tar, trailer drained; sealed digest checked; pipe drained to EOF so `Open`'s terminal authentication is the verdict; tamper tests for a flipped last byte and a cut final chunk |
| 9 | P2 fixtures fail correct implementations | **Fixed.** `AddBucket("data-uk")`; leasetest's server stores a plain PUT as a new version (its own test); `writeServingClaim` and `movingTestClaim` build valid claims and assert `Validate()` |
| 10 | P2 Task 9 misstates control availability and `CARLOS_PURGE` | **Fixed.** Control is always attempted (`haveControl` true pre-spawn, documented); `CARLOS_PURGE` is an advisory class whose presence difference is a note, never a failure; fixtures corrected; the test builds its `ExecBackend` explicitly |
| 11 | P2 S1 can pass from the cache | **Fixed.** `(*edge.Edge).ServeUncached` with a context marker that disables the cache lookup and store while the hibernating branch still wakes; `ReadyProbe` set from `RunWithActivatorReady`'s callback; the cached-200-beside-failing-wake test |
| 12 | P2 readiness input versions lack producers | **Fixed.** `appconfig.LoadMergedHostVersions` returns the etags of the exact objects merged; `SourceReport.Archive` publishes the archive keys and versions the source's PUT responses returned; T5 reads by those versions |
| 13 | P2 private-storage refusal checks the policy | **Fixed.** The door and the round check the instance's store binding (`objstore.GetIn` under `rec.Environment` or `rec.Slug`, as the create door stamps it); both manifest and store reads fail closed with 503 |
| 14 | P2 inventory rejects M-C21/M-C22; record absent from main | **Fixed.** Inventory `M-C1` to `M-C22`; Task 15's first step copies the record from the design branch by sha |
| 15 | P3 D9, Task 2 and Task 14 contradict D13 | **Fixed.** D9 rewritten; Task 2's file list and commit text keep every legacy caller; Task 14 names only the guarded transitions |

## Plan 1 review round 5 (Astra, on 80647eeb): dispositions (Plan 1 rev 6)

`CODEX-PLAN1-REVIEW-R5.md` beside this file: PLAN 1 NOT READY TO BUILD, 3 P1, 10 P2, 1 P3. Every finding fixed in Plan 1 rev 6 and verified against `origin/main` at 1c43c2ce.

| # | finding | disposition |
|---|---|---|
| 1 | P1 build failures in the task sequence: undefined `testHolderKey`; `Check` declared twice; `MoveGuardedCut(g, now)` against a one-argument definition; `instanceStoreEnv` console-local but used from `host` | **Fixed.** The fixture uses the record key grammar `create_test.go:14` uses; Task 11's duplicate removed; Task 14 calls `MoveGuardedCut(g)`; `provision.InstanceStoreEnv(*Record)` in Task 2 serves both packages. Also found and fixed: Task 4 read `moves.ProtocolVersion`/`ReadFloor` before Task 13 defined them; `floor.go` is now Task 1's |
| 2 | P1 the root staging relay has no operation-authority check | **Fixed.** `ServeHeldStageRequests(ctx, s, optDir, dataDir, authorize)`: every request component validated before any path join (`Host == file name`, `ValidHostName`, `ValidAccount`, `ValidApp`, `ValidVersion`); `heldStageAuthority` in `host` checks `hold_op`, the claim's op id, kind, target and this box, and the record's pinned release; stale and traversal tests; no `pub` parameter |
| 3 | P1 the hold-prev marker left recovery free to run an old `.prev` after the console committed | **Fixed, redesigned.** The marker is withdrawn. Root writes `/data/.moves/floor.{json,min-release}` before staging and `decidePrev` writes `carlos.prev.version` before the journal; both units select `.prev` only when `prev_ok` (a `sort -V` comparison in the `ExecStart` line) holds; the door admits only when every participant's edges file shows the armed floor and a `prev_release` at or above it, so nothing asynchronous follows admission; shell-level and crash-boundary tests |
| 4 | P2 default-off system apply contradicted the dormant boundary | **Fixed.** The boundary is the floor object's existence, not the switch (D13 names the exception): `floorAllows` reads only the local file, the registry is consulted only by `rollback`'s hold rule, an armed floor stays armed with the switch off; `TestFloorIsDormantWithoutTheObject` covers both cases |
| 5 | P2 `fs.ErrPermission` misses `EROFS` | **Fixed.** Main's `stageBlocked` (`adopt.go:250`) exported and reused; the test injects `EROFS` through the `materializeHeldStage` seam; the `chmod` fixture dropped |
| 6 | P2 Task 4's HTTP fixtures cannot reach their outcomes | **Fixed.** `fleetFixture` writes production-shaped edges files, fresh status snapshots and `boxidtest`-signed identity records, sets `SecretsPub`, `AWSAccount` and `boxCerts`; shared by the door tests and the selector test |
| 7 | P2 selection permits an absent source | **Fixed.** `selectMoveBoxes` returns `Source`; the door requires it and requires `Op.Exec == Source`; the floor requires files in both regions; missing-source and different-executor tests |
| 8 | P2 the multipart receipt-match branch unspecified | **Fixed.** When versioned, the receipt match requires `DstVersionID` and records it; a two-round multipart fixture and an old receipt without a version |
| 9 | P2 `Ready` mixes input generations; T3 loader contradiction; shadow reporting | **Fixed.** `Ready` requires `target.Inputs.FilesPlain == source.FilesPlain` and `Inputs.EnvReport == source.Env.Digest`; T3 uses `LoadMergedHostVersions`; `MergedVersions.Shadowed` → `TargetReport.Notes`, asserted |
| 10 | P2 interim abort's claim-release crash boundary | **Fixed.** `ReleaseMove` leaves `Journal["released_move"]` on the serving op; `RequireReleasedMove(id)` is the replay rule; crash-and-retry test |
| 11 | P2 dir-store lease fallback cannot satisfy the guard | **Fixed.** No deployment bucket → Phase 1 rounds refuse with an S1 reason; tests use the lease fake |
| 12 | P2 `Verify` leaks its producer | **Fixed.** Deferred `pr.CloseWithError` and a wait on the producer's `done`; goroutine-count test on a multi-chunk early mismatch |
| 13 | P2 commit commands omit files | **Fixed.** Tasks 1, 4, 6, 13 and 14 stage every directory they touch |
| 14 | P3 competing instructions | **Fixed.** The goal names spec rev 7.22, claim 6.30 and `main` 1c43c2ce; the door's order paragraph matches the code; Task 9's interface prose lists the advisory class; Task 13 names the live read; `release.LiveVersion` is marked as existing (`versions.go:290`) |

## Plan 1 review round 6 (Astra, on 296807a2): dispositions (Plan 1 rev 7)

`CODEX-PLAN1-REVIEW-R6.md` beside this file: PLAN 1 NOT READY TO BUILD, 4 P1, 4 P2, 1 P3. Every finding fixed in Plan 1 rev 7. This was the one authorised round after rev 6; per the coordinator's instruction the fixes were made once and no further round was run.

| # | finding | disposition |
|---|---|---|
| 1 | P1 fields consumed before their task: the console reader fields, `hold_op` before Task 12; `ErrOpMismatch`; `fleetBox.FloorArmed` | **Fixed.** Task 4 adds the four reader fields to `edgesFilePlacementView`; the build order (header) puts Task 12 before Task 7b; `ErrStaleOp` (main `ops.go:9`); `fleetBox` declares `FloorUnarmed` |
| 2 | P1 `boxidtest` import cycle | **Fixed.** `boxid_test.go` is left as it is; `boxidtest` is an exported twin, duplication accepted |
| 3 | P1 T7's relay needs state that exists only after readiness | **Fixed.** `heldStageAuthority` accepts an absent registry row during `preparing` and takes the pinned release from `SourceReport.Release` (new: the source's `LiveVersion`, the identity producer; Plan 2's admission copies it into `Inputs.Release`); `TestPreparingRelaysHeldStageOnAHardenedBox` runs the whole cycle from a `MoveStart` record with no target row |
| 4 | P1 `%s` in `ExecStart`, `sort -V` semantics, a test that bypassed systemd | **Fixed, redesigned.** Root writes a verdict file `carlos.prev.allowed` (the floor `.prev` was judged at or above, by `ReleaseAtOrAbove`, with no hold row); the units compare it to the current floor with `cmp -s`, no `%`, no `$`; the test asserts the absence of both expansions, then runs the body |
| 5 | P2 the downgrade prohibition covered only `rollback` | **Fixed.** `activationAllowed` applies both halves of §11 in `install`, `rollback` and `restoreFiles`; the verdict is withheld under a hold; `TestHeldParticipantRefusesEveryDowngrade` |
| 6 | P2 `Open` accepts trailing ciphertext | **Fixed.** The final chunk must end the stream (`ReadFull` must return `io.EOF`); appended-byte and concatenated-archive tests |
| 7 | P2 shadowing reported at the wrong layer | **Fixed.** `LaunchEnvironment` returns the names the carried `<host>.env` overrides in the rendered configuration (`instanceEnvParts`); `MergedVersions.Shadowed` dropped; the test uses no instance overlay |
| 8 | P2 commit commands | **Fixed.** Task 11 commits the pure package alone; Task 14 creates and commits the host report files, the MinIO test and the `30-minio-contract` amendment, and stages `internal/lease` |
| 9 | P3 superseded instructions | **Fixed.** D3 and D4 rewritten; the Go cache line follows the machine's rule (default cache) |

## The model's limit (rev 7.20)

`internal/sim/hostlife` stays at one box per region plus replacement
boxes. It gains rule `fence-converts-moving` and witness
`wFenceConvertsMoving` (a source terminated before the flip in a one-box
region: conversion to `relocating`/`pinning`, no churn and one alert
while no box exists, completion on the replacement box). It does not
model `choose()`, two target replacements, `stuck` after them, or the
files publisher (it takes the fenced source's files directory as
published). The two-box model is region HA's, on its own branch.

## What the model proved and the plans port

Witnesses that become real tests, by plan: Plan 2 ports `wSealByVersion`,
`wMarkersLast`, `wBuildCrashes`, `wEvidenceWholeTree`, `wTimerFreeze`;
Plan 3 ports `wProbeWindowAt` (`TestProbeWindow`), `wCommitBeforeOpen`,
`wCommitCoverage`, `wCommitInterrupted`, `wCommitReplayed`; Plan 4 ports
`wSealReturn`, `wCleanReturnIdentity`, `wRollbackJournal`,
`wRecoveryInputs`, `wDirtyReturn`, `wReturnLostRecord`,
`wRollbackBaselinePreserved`, `wTwoSessionDeclaration`,
`wNoDuplicateDeclarationEarly`, `wNoDuplicateDeclarationLate`,
`wDeclareLostCompute`, `wLostQuarantine`, `wLostReturnPark`; Plan 5 ports
`wStagingScopeDeadline`, `wParkScopeSurvivesRestart`,
`wSweepScopeMembership`, `wStagingQuiet`, `wFreeSweepVsCreate`. Each
cites its witness in the test's doc comment.
