carlos / platform Public

plan: home part 2 rev 2, Astra round 1 dispositions

Four P2s from Astra's review of be29ccd0, each fixed:
- witnesses split supplied arguments (@home) from retained claim state
  (either spelling); replays assert no duplicate and no rewrite; Touch
  covers ie already held (Run 1b);
- a fixture-only task (Task 2) adds a production probe mode, and every
  into-home witness runs through movesProbeOwners, movesRecoveryOwners
  and C8, with a restarted probe and the pre-flip revert;
- the door normalises MV-N5, MV-N30 and MV-N177 fills too, with refusal
  tests for each;
- MV-N511's check moves ahead of the staging capability check, which
  would otherwise answer a misleading MV-N20.
Ruling R2 now treats the one-box finding as a blocker independent of
part 2, with options (a) to (d); stopping the agent alone is not enough
because the edges file outlives the box. Asks name their witnesses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev c139b80b77447eaf044efb8c10d84d0e9a4b4aad parent be29ccd
1 file changed, +156 −105
  • docs/superpowers/plans/2026-10-15-moves-home-region-part2.md +156 −105

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.012s
?   	amadan.net/carlos/platform.git/launchctx/controlname	[no test files]
?   	amadan.net/carlos/platform.git/launchctx/wireid	[no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok  	github.com/carlosframework/platform	302.783s
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	2.946s
ok  	github.com/carlosframework/platform/cmd/moveprobe	93.267s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.707s
ok  	github.com/carlosframework/platform/internal/activator	249.360s
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.484s
ok  	github.com/carlosframework/platform/internal/apperr	0.071s
ok  	github.com/carlosframework/platform/internal/applog	0.121s
ok  	github.com/carlosframework/platform/internal/awsinv	0.004s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.005s
ok  	github.com/carlosframework/platform/internal/boxid	0.861s
?   	github.com/carlosframework/platform/internal/boxid/boxidtest	[no test files]
?   	github.com/carlosframework/platform/internal/boxid/internal/signtest	[no test files]
ok  	github.com/carlosframework/platform/internal/boxvolumes	0.008s
ok  	github.com/carlosframework/platform/internal/caps	0.012s
ok  	github.com/carlosframework/platform/internal/certwarm	0.002s
ok  	github.com/carlosframework/platform/internal/cgroup	0.721s
ok  	github.com/carlosframework/platform/internal/changelog	0.303s
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/docs/superpowers/plans/2026-10-15-moves-home-region-part2.md b/docs/superpowers/plans/2026-10-15-moves-home-region-part2.md
index 56c311d6f..d893cef34 100644
--- a/docs/superpowers/plans/2026-10-15-moves-home-region-part2.md
+++ b/docs/superpowers/plans/2026-10-15-moves-home-region-part2.md
@@ -2,31 +2,32 @@
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
-Rev 1, 2026-10-15. Paul approved starting part 2 on 2026-10-15. Written against `main` at a5003aba. Tracking thread: amadan #236.
+Rev 2, 2026-10-15. Paul approved starting part 2 on 2026-10-15. Written against `main` at a5003aba. Tracking thread: amadan #236. Rev 1 (be29ccd0) was reviewed by Astra: NOT READY, four P2s; the dispositions are at the end.
-**Goal:** Let a Phase 1 move name the home region as its target, under host claims, with the same guarantees a move between two named regions has. That means the commit, a clean or dirty rollback, abort, declare-lost, and the parks in home after the commit. Today the door refuses every such move (MV-N21: "moves to the home region are not supported yet. the home publishes no staging lane").
+**Goal:** Let a Phase 1 move name the home region as its target, under host claims, with the same guarantees a move between two named regions has. That means the commit, a clean or dirty rollback, a pre-flip revert, abort, declare-lost, and the parks in home after the commit. Today the door refuses every such move (MV-N21: "moves to the home region are not supported yet. the home publishes no staging lane").
-**Architecture:** Almost all of the box and claim machinery already handles a home target. Part 1 (359e8ac2) maps every record code to an engine region once (`park.PlacementRegion`: the home's `""` is `@home`). Claim Plan 13 (a0806e4c, 2ad5412c) keeps the home box's roster name (for example `ie`) as its claim engine region, grants the `@home` alias only when the signed roster entry equals the signed fleet home designation, and compares the two names through one rule (`regionname.SameRegion` under `op.intent.home_region`). `BeginAdmittedMove` already start-guards both spellings for a home target, `ClaimMoveRosterPlaces` already maps a home roster box to `@home`, and region HA's `ConvertCommittedLanding` already accepts a landing whose `to` is `@home`. What is missing is narrow:
+**Architecture:** Almost all of the box and claim machinery already handles a home target. Part 1 (359e8ac2) maps every record code to an engine region once (`park.PlacementRegion`: the home's `""` is `@home`). Claim Plan 13 (a0806e4c, 2ad5412c) keeps the home box's roster name (for example `ie`) as its claim engine region. It grants the `@home` alias only when the signed roster entry equals the signed fleet home designation. It compares the two names through one rule (`regionname.SameRegion` under `op.intent.home_region`), and it keeps whichever spelling claim state already holds rather than rewriting it. `BeginAdmittedMove` already start-guards both spellings for a home target, `ClaimMoveRosterPlaces` already maps a home roster box to `@home`, and region HA's `ConvertCommittedLanding` already accepts a landing whose `to` is `@home`. What is missing is narrow:
-1. **The console door.** It refuses `to == ""` outright. Behind that refusal, its staging capability (`placements()`) skips every edges file without a `placement`, which is exactly the home box, so the home never reads as stageable. Three of its sentences would print the raw code (`""`) for the home. And nothing stops a box whose `CARLOS_REGION` equals the home's name from being picked as a home target, which that box cannot serve.
+1. **The console door.** It refuses `to == ""` outright. Behind that refusal, its staging capability (`placements()`) skips every edges file without a `placement`, which is exactly the home box, so the home never reads as stageable. Six of its sentences would print the raw code (`""`) for the home. And nothing stops a box whose `CARLOS_REGION` equals the home's name from being picked as a home target, which that box cannot serve.
2. **T3 on the home box.** `moves.CompareEnv` reports `region-missing` whenever the source sets `CARLOS_REGION` and the target does not. A home box never sets it (`agentOwnedVars` adds it only when the box has a region). So every move into home fails T3 and waits at `preparing` for ever. Moves out of home pass only because the loop runs over the source's variables.
-3. **Proof.** No test runs a move into home through the real box paths. The harness hard-codes the target as `uk`, and its held-env composer sets `CARLOS_REGION=uk`, which would hide gap 2.
+3. **Proof through the production owners.** No test runs a move into home through the real box paths. The close harness's probe is a fake (`probeLog.wake`, `movesland_probe_test.go:73`) that calls `AdmitWriter` with a hard-coded `uk-box` and `uk`. It bypasses the production probe owners, their admission evidence and the C8 files publication, which is exactly where the home box's roster name `ie` meets the move's `@home`. The harness's held-env composer also sets `CARLOS_REGION=uk`, which would hide gap 2.
-Everything else (the staging lane, the lease key, Touch, Commit, the rollback halves, the parks, the loss path) is already written region-agnostically and needs witnesses, not code.
+Everything else (the staging lane, the lease key, Touch, Commit, Revert, the rollback halves, the parks, the loss path) is already written region-agnostically and needs executable witnesses, not code.
**Tech Stack:** Go; `internal/console`, `internal/moves`, `internal/host` (tests), `internal/hostclaim` and `internal/hostops` (tests only).
-**Spec:** move-any-app design rev 7.22 (`docs/superpowers/specs/2026-10-02-move-any-app-design.md`): §2 and §7.1 (the refusal of "a target of home", F2), §7.3 (staging lane and lease, T1 to T13), §7.6 (landing), §7.9 and §7.10 (rollback, return lane, declare-lost), §16 F2 ("Moves into home": `CARLOS_HOME_PLACEMENT`, home as a target, `carlos move back`). Task 0 amends §2, §7.1 and §16. Claim rev 6.33 (Plan 13's Task I) for the home alias. Index: `2026-10-06-moves-phase1-0-index.md`; D6, D13, D14 and D15 bind every task here. Part 1: `git show 359e8ac2`. Plan 13: `docs/superpowers/plans/2026-10-12-host-lifecycle-claim-13-home-region.md` on `design-replica-ownership`.
+**Spec:** move-any-app design rev 7.22 (`docs/superpowers/specs/2026-10-02-move-any-app-design.md`): §2 and §7.1 (the refusal of "a target of home", F2), §7.3 (staging lane and lease, T1 to T13), §7.6 (landing), §7.9 and §7.10 (revert, rollback, return lane, declare-lost), §16 F2 ("Moves into home": `CARLOS_HOME_PLACEMENT`, home as a target, `carlos move back`). Task 0 amends §2, §7.1 and §16. Claim rev 6.33 (Plan 13's Task I) for the home alias. Index: `2026-10-06-moves-phase1-0-index.md`; D6, D13, D14 and D15 bind every task here. Part 1: `git show 359e8ac2`. Plan 13: `docs/superpowers/plans/2026-10-12-host-lifecycle-claim-13-home-region.md` on `design-replica-ownership`.
## Global Constraints
- **Off means off.** Every change is behind `CARLOS_MOVES_PHASE1` (D13). The legacy door (switch off) keeps its own refusal, em dash and all, unchanged. With the switch on, a home target is admitted **only on a host-claim console** (`s.cfg.HostClaim` with a claim runtime). The plain Phase 1 path keeps refusing it before any write (Ruling R1).
-- **One region rule.** A record code becomes an engine region only through `park.PlacementRegion`. Two engine regions are compared only through `park.SameRegion` / `regionname.SameRegion` under the operation's `home_region`, or `park.PlacementMatches` for a code against an engine region. No new `==` between a placement code and an engine region: `TestPlan13NoDirectPlacementEngineComparison` (`home_region_comparison_test.go`) must stay green. No moves call site may pass the home box's roster name where a placement-derived region is meant (Review Focus 1).
+- **One region rule.** A record code becomes an engine region only through `park.PlacementRegion`. Two engine regions are compared only through `park.SameRegion` / `regionname.SameRegion` under the operation's `home_region`, or `park.PlacementMatches` for a code against an engine region. No new `==` between a placement code and an engine region: `TestPlan13NoDirectPlacementEngineComparison` (`home_region_comparison_test.go`) must stay green.
+- **Supplied arguments versus retained state.** Every region a moves call site newly hands the claim for a home target is the placement-derived `@home`, never the roster name. Claim state the move finds already written (`touched.regions`, `quiet_after` keys, earlier chores, admissions, predecessor regions) may hold either spelling, and nothing in this plan rewrites, duplicates or canonicalizes it. Witnesses assert the first rule on arguments and the second on state, and test both spellings of retained state (Review Focus 1).
- **Empty is unset.** `""` stays unset at every engine and claim boundary. Do not make `Validate`, `parkQueueGuard`, `AdmitWriter`, `PublishGeneration` or `Cover*Predecessors` accept it (Plan 13's constraint, kept).
-- **No claim or region HA code changes.** This plan writes tests in `internal/hostclaim` and `internal/hostops` only where they drive moves' own call sites. Anything that would change claim or region HA behaviour is an ask (below), not a task.
-- **Cold only (P7, D14); one-box regions only (D15).** Nothing here relaxes either. T8 and S1 keep counting every edges file that publishes the placement (Ruling R2).
-- **Copy.** Every new operator-facing string is a `pending` row in `2026-10-06-moves-phase1-copy.md` in the commit that writes it, and in `movesCopyIDs` (`approved_moves_copy_test.go`). Ids MV-N510 to MV-N529 are reserved for this plan (checked free on every branch on 2026-10-15). No em dashes anywhere: a full stop and a new sentence, lower case where the door's sentences are lower case.
-- **Gate.** While iterating, targeted runs only: `go test ./internal/moves ./internal/console -run 'Home|MoveDoor|CompareEnv' -count=1` and `go test ./internal/host -run 'Home|Plan13' -count=1`. Never run the whole `internal/host` suite locally; CI's `make test` is the full run (Paul, 2026-10-12). Before handing off, once: `go build ./...`, `go vet ./...`, `gofmt -l .`, the copy gate (`go test . -run 'Moves|Copy' -count=1`) and the cross-compile (`GOOS=darwin`, `GOOS=windows`, `GOOS=linux GOARCH=arm64` `go build ./...`). The Go build cache stays at its default. Use `GOFLAGS=-buildvcs=false` if the worktree's VCS metadata is unreadable from the sandbox. Tests serial and niced; no `internal/sim/hostlife` sweep.
+- **No claim or region HA production code changes.** This plan writes tests in `internal/hostclaim` and `internal/hostops` only where they drive moves' own call sites. Anything that would change claim or region HA behaviour is an ask (below), not a task.
+- **Cold only (P7, D14); one-box regions only (D15).** Nothing here relaxes either. T8 and S1 keep counting every edges file that publishes the placement. Ruling R2 decides how dev lives with that.
+- **Copy.** Every new operator-facing string is a `pending` row in `2026-10-06-moves-phase1-copy.md` in the commit that writes it, and in `movesCopyIDs` (`approved_moves_copy_test.go`). Ids MV-N510 to MV-N529 are reserved for this plan (checked free on every branch on 2026-10-15). An approved template whose fill changes keeps its text and its row; only the argument passed in changes. No em dashes anywhere: a full stop and a new sentence, lower case where the door's sentences are lower case.
+- **Gate.** While iterating, targeted runs only: `go test ./internal/moves ./internal/console -run 'Home|MoveDoor|CompareEnv' -count=1` and `go test ./internal/host -run 'Home|Plan13|ProbeOwners|Revert' -count=1`. Never run the whole `internal/host` suite locally; CI's `make test` is the full run (Paul, 2026-10-12). Before handing off, once: `go build ./...`, `go vet ./...`, `gofmt -l .`, the copy gate (`go test . -run 'Moves|Copy' -count=1`) and the cross-compile (`GOOS=darwin`, `GOOS=windows`, `GOOS=linux GOARCH=arm64` `go build ./...`). The Go build cache stays at its default. Use `GOFLAGS=-buildvcs=false` if the worktree's VCS metadata is unreadable from the sandbox. Tests serial and niced; no `internal/sim/hostlife` sweep.
- **Nothing rolls without Paul's go.** Merge only on amadan with `amadan branch merge -squash`, after re-merging `main` and a green CI on the tip.
## What already exists (do not rebuild)
@@ -35,12 +36,15 @@ Everything else (the staging lane, the lease key, Touch, Commit, the rollback ha
|---|---|---|
| home's engine region | `internal/regionname`, `park.HomeRegion`, `park.PlacementRegion` | `""` maps to `@home`; `@home` is outside the placement alphabet |
| the alias | `regionname.SameRegion`, `claimRegionBinding` (`internal/host/claimregion.go`), `Roster.MatchesHome` | the roster name and `@home` are one region only under a verified designation |
+| alias-aware replay | `QueueResidueParks`, `queueReplicaPark` (`TestPlan13ResidueAliasQueue`, `TestPlan13ReplicaAliasQueue`, `internal/hostclaim/plan13_followups_test.go`) | an `@home` replay of a chore already queued under `ie` changes nothing and adds nothing |
| the start CAS | `hostops.BeginClaimMove`, `ClaimMoveRosterPlaces`, `hostclaim.BeginAdmittedMove` | maps a home roster box to `@home`; writes `op.intent.to = @home` and `home_region`; start-guards both spellings (CC11 over `ie` and `@home`) |
| the home lane's lease | `stagingLeaseKey` (`internal/host/movespass.go`) | `control/staging-leases/@home/<host>` in the deployment bucket, for every caller that passes a record code |
| the source finds home's bucket | `loadMoveFleet` (`internal/host/movesreport.go`) | keeps the placementless box under `""`, so `fleet.targets[""]` is the home box's `data_bucket` |
+| Touch | `sourceRound` (`internal/host/movesphase1.go:321-331`) | skips the write when `touched` already holds either spelling of home and the target box; otherwise adds `@home` |
| T8, T9, T12 | `checkOneOwner`, `checkClaim`, `checkQuiet` | name home by the operation's home name; T9 accepts either spelling in `touched`; T12 reads the later of both quiet keys (`TestPlan13HomeTargetReadiness`, `TestPlan13MoveQuietReadsLegacyAndHomeToken`) |
| arming on the home box | `armMovesParks` | arms only when the claim grants the `@home` alias; T13 fails closed otherwise |
| parks under either name | `park.Engine.OwnsRegion`, `hostbox.Regional.Run`, the moves filters (`PlacementMatches(cfg.ParkHomeRegion, cfg.Region, o.Region)`) | `region:@home` chores run on the home box's moves engine and its regional runner |
+| production probe owners | `movesProbeOwners` (`internal/host/movesprobeowners.go`) | compare the admission's region with `op.intent.to` and the predecessor's through `SameRegion`; project home history for the baseline (`HolderRegionView`) |
| predecessor cover | `hostclaim.CoverRegionPredecessors` | compares through `SameRegion` under the op's home binding |
| fence and loss | `MoveFenceRegions`, `VerifyMoveTargetFence`, `move_loss_owner.go` | a home target's fence binding maps to `@home` (`TestPlan13FenceRecoveryHomeAndNamed`, "target-home") |
| committed landing on a dead target | `hostclaim.ConvertCommittedLanding` (region HA) | accepts `to = @home` under the op's binding; the relocation's `intent.region` is the roster name |
@@ -50,16 +54,16 @@ Everything else (the staging lane, the lease key, Touch, Commit, the rollback ha
**Q1. What is missing for a move whose target is home.**
-- **Staging lane.** Bucket: the home box's own data bucket, `CARLOS_HIBERNATE_BUCKET`, which its edges file publishes as `data_bucket` with no `placement`. Lease: `control/staging-leases/@home/<host>` in the deployment bucket (part 1's `stagingLeaseKey`). Lane keys: `moves/<host>/<move id>/...` in the home bucket, written by the named source under its `sibling_staging_arns` grant (M17). Who publishes it: the home box's edges-file pass already publishes the coordinates; the console just does not read them (`placements()` skips a file with no `placement`). Task 4 adds a move-only fold. Nothing new is published.
-- **How the source finds the bucket and the ARNs.** It already does: `loadMoveFleet` keys the home box under `""`, and every source step looks up `fleet.targets[rec.Move.To]`. IAM is infra: every named region's `sibling_staging_arns` must list the home data bucket's ARN (the source pushes the lane into it), and the home box's must list every named bucket (the home target's T10 probe and its dirty return lane). M17's mesh ("every region lists every other region's data bucket") already says this; P-2 of the runbook has to check that the home bucket is in it (Task 5).
+- **Staging lane.** Bucket: the home box's own data bucket, `CARLOS_HIBERNATE_BUCKET`, which its edges file publishes as `data_bucket` with no `placement`. Lease: `control/staging-leases/@home/<host>` in the deployment bucket (part 1's `stagingLeaseKey`). Lane keys: `moves/<host>/<move id>/...` in the home bucket, written by the named source under its `sibling_staging_arns` grant (M17). Who publishes it: the home box's edges-file pass already publishes the coordinates; the console just does not read them (`placements()` skips a file with no `placement`). Task 5 adds a move-only fold. Nothing new is published.
+- **How the source finds the bucket and the ARNs.** It already does: `loadMoveFleet` keys the home box under `""`, and every source step looks up `fleet.targets[rec.Move.To]`. IAM is infra: every named region's `sibling_staging_arns` must list the home data bucket's ARN (the source pushes the lane into it), and the home box's must list every named bucket (the home target's T10 probe and its dirty return lane). M17's mesh ("every region lists every other region's data bucket") already says this; P-2 of the runbook has to check that the home bucket is in it (Task 6).
- **T1 to T13 on a home box.** All but T3 already work (table above). T3 is gap 2: `CompareEnv` must accept a home target that sets no `CARLOS_REGION` (Task 1). T8 has a deployment precondition, not a code gap: exactly one edges file in the deployment may lack a `placement` (Ruling R2).
-- **Landing on a home box.** Already written: the landing runs on the box the claim names, queues its earlier-stay residue as `park-generation` chores under `@home` (D6), and commits with `Commit(id, PlacementRegion(From), source, @home, ...)`. The probe's admission records the home box's engine region, its roster name. Task 2 proves it end to end.
-- **Rollback and return lanes (source named, target home).** The home box releases with `TargetReleased(..., @home, ...)`, which matches `op.intent.to` exactly; its cleaning park and the move's lane park are queued under `@home`; a dirty return lane is written by the home box into the named source's bucket under `control/staging-leases/<source>/<host>`, and `QueueReturnLanePark` names the source region. Task 3 proves each.
-- **Parks in home after the commit.** Two kinds, both under `@home` and owner `region:@home`: the staging lane's `park-replica` (queued by `Commit`) and the residue `park-generation` chores of an earlier stay (queued at the landing's `parking` step, including the legacy root generation `@root` of a host that was created in home). The home box's moves engine runs the immediate half; `hostbox.Regional` on the home box acknowledges them (Plan 13 Review Focus 2). The source's location park runs in the named source region.
-- **The `@home` alias in Touch, Commit and Release.** Touch: the source adds `PlacementRegion(To)`, which is `@home`, and the target box; the start CAS already guarded both spellings. Commit: `targetRegion` is the home box's `parkRegion()`, `@home`, which matches `op.intent.to` exactly; the source's quiet key is the named region. Release: `TargetReleased` passes `@home`; `ReleaseMove` and the K8 release name no region. Every one of these comparisons is exact on the claim side, and that is safe only because moves always passes `@home`, never the roster name. Task 2's `TestHomeTargetObligationsUseTheHomeToken` pins it (Review Focus 1, Ask A1).
+- **Landing on a home box.** Already written: the landing runs on the box the claim names, queues its earlier-stay residue as `park-generation` chores (D6), and commits with `Commit(id, PlacementRegion(From), source, @home, ...)`. The probe's admission, through the production probe owners, records the home box's engine region, its roster name. Task 3 proves it end to end through those owners.
+- **Rollback, revert and return lanes (source named, target home).** The home box releases with `TargetReleased(..., @home, ...)`, which matches `op.intent.to` exactly; its cleaning park and the move's lane park are queued with `@home`. A dirty return lane is written by the home box into the named source's bucket under `control/staging-leases/<source>/<host>`, and `QueueReturnLanePark` names the source region. A cut that fails before the flip reverts on the source with `Revert(id, source, @home, &laneScope, ...)`, and the home box parks the lane. Task 4 proves each.
+- **Parks in home after the commit.** Two kinds, both owned by home's regional runner: the staging lane's `park-replica` (queued by `Commit`) and the residue `park-generation` chores of an earlier stay (queued at the landing's `parking` step, including the legacy root generation `@root` of a host that was created in home). A new chore is queued with `@home` and owner `region:@home`. A chore the claim already owes under `ie` (from an earlier stay or a sweep) is kept as it is, and the `@home` replay neither duplicates nor rewrites it (Plan 13's alias-aware queue). The home box's moves engine runs the immediate half; `hostbox.Regional` on the home box acknowledges. The source's location park runs in the named source region.
+- **The `@home` alias in Touch, Commit and Release.** Touch: when `touched` lacks every spelling of home or lacks the target box, the source adds `PlacementRegion(To)`, which is `@home`, and the target box; when it already holds `ie` (Run 1b, after Run 1 moved the host out of home under the same claim), it writes nothing. Commit: `targetRegion` is the home box's `parkRegion()`, `@home`, which matches `op.intent.to` exactly; the source's quiet key is the named region. Release: `TargetReleased` passes `@home`; `ReleaseMove` and the K8 release name no region. The claim compares `op.intent.to` exactly, and that is safe because `op.intent.to` is itself written as `@home` by the start CAS and moves always supplies `@home`. Task 3 pins the supplied arguments (Review Focus 1, Ask A1).
- **How the door resolves `--to`.** As today: `homeRegionIn` maps the console's home name (`CONSOLE_HOME_REGION`, or the claim-mode fallback `dbClaimHomeRegion`) to `""`. No `home` keyword (Ruling R3): a named region called `home` stays a named region, as Plan 13 rules. With no home name configured the home cannot be named, and the request is `invalid region` as today.
-**Q2. Claim-side and region HA changes.** None required. Four confirmations and one optional hardening are listed under Asks. The design keeps the claim's exact comparisons as they are and pins moves' side of the contract with tests.
+**Q2. Claim-side and region HA changes.** None required in production code. The asks are each backed by an executable witness in this plan, plus one optional hardening (A1).
**Q3. What is shared with part 1, and the failure modes.** Shared: `park.PlacementRegion`, `stagingLeaseKey`, `cfg.parkRegion()`, `moveFleet`'s home key, the engine bindings (`ParkHomeRegion`), the predecessor mapping and the park view's J2/F4 rule. Part 2 adds no new mapping. The failure modes are listed in Review Focus and in "Failure modes" below.
@@ -67,11 +71,14 @@ Everything else (the staging lane, the lease key, Touch, Commit, the rollback ha
## Review Focus
-1. **One region, two names (Plan 13's dedupe rule).** The claim compares `op.intent.to` exactly in `Abort`, `Revert`, `TargetReleased`, `QueueStagingPark`, the loss and fence paths, and in `requireRetiredRegion` (`internal/hostclaim/move.go:549`). For a home target `to` is `@home`. If any moves call site passed the home box's roster name (`ie`) instead, most of these refuse (fail closed), but `requireRetiredRegion`'s `region != to` would read `ie` as "not the target" at `landing/committed` and allow a whole-location park in the live home region. The park view still protects the live generation there (`movesAuthoritative` answers `@home`, compared through `SameRegion`), so it is defence in depth, but the reviewer should check every moves call site that hands the claim a region on the home box, and the test that pins them (Task 2, `TestHomeTargetObligationsUseTheHomeToken`). The staging lease is the other place: a key spelled with the roster name would split the lane's lease from the source's `@home` key and let two writers stage at once. `TestHomeLaneStagingLeaseKey` already pins the key for a record code; Task 2 adds the into-home direction.
+1. **One region, two names (Plan 13's dedupe rule).** Two separate properties, each with its own witness:
+ - **Supplied arguments are `@home`.** The claim compares `op.intent.to` exactly in `Abort`, `Revert`, `TargetReleased`, `QueueStagingPark`, the loss and fence paths, and in `requireRetiredRegion` (`internal/hostclaim/move.go:549`). If a moves call site passed the roster name `ie` instead, most of these refuse (fail closed), but `requireRetiredRegion`'s `region != to` would read `ie` as "not the target" at `landing/committed` and allow a whole-location park in the live home region. The park view still protects the live generation (`movesAuthoritative` answers `@home`, compared through `SameRegion`), so that is defence in depth. The staging lease is the other place: a key spelled with the roster name would split the lane's lease from the source's `@home` key and let two writers stage at once.
+ - **Retained state keeps its spelling.** `touched`, `quiet_after`, earlier chores and admissions may already name home as `ie`. A replay must neither add a second chore for the same work under `@home` nor rewrite the `ie` one, and Touch must not write when `ie` is already there. Witnesses seed both spellings.
2. **T3's home rule.** A home target must set no `CARLOS_REGION`; a named target must still set its own, different from the source's. A target in home that does set one is a box that is not the home box, and T3 must fail it (`region-present`), not wave it through (Task 1).
-3. **The door's home capability must not leak into create.** `placements()` feeds the create gate's `published` flag and `armedPlacements`; adding `""` there would change create. Task 4 folds the home only in a move-only reader, and `TestCreatePlacementsUnchangedByHomeFold` pins create.
-4. **A home-named box is not a home box.** A box with `CARLOS_REGION=<home name>` is home to the console (`homeRegionIn`) but not to itself (no alias; its `cfg.Region` is `ie`, so it never writes a target report, and the source's `fleet.targets[""]` never finds it). The door must refuse it as a home target before any claim write (MV-N511).
-5. **Parks after the commit, from both engines.** The home box runs `@home` chores from its moves engine (region `@home`) and from `hostbox.Regional` (region = roster name, alias `@home`). Both must acknowledge the same chore once, and a named box must never acknowledge either (`TestPlan13NamedBoxCannotAcknowledgeHomeDelete` covers the named side; Task 3 covers the lane park on the home side).
+3. **Production owners, not fakes.** The landing, the restarted probe, the C8 files publication, the source's recovery after a rollback and the pre-flip revert must run through `movesProbeOwners`, `movesRecoveryOwners` and the claim runtime, with the home box's engine region `ie` and the op's `to` `@home`. A witness that admits through a fake probe with a literal region proves nothing here (Tasks 2 to 4).
+4. **The door's home capability must not leak into create.** `placements()` feeds the create gate's `published` flag and `armedPlacements`; adding `""` there would change create. Task 5 folds the home only in a move-only reader, and `TestCreatePlacementsUnchangedByHomeFold` pins create.
+5. **A home-named box is not a home box.** A box with `CARLOS_REGION=<home name>` is home to the console (`homeRegionIn`) but not to itself (no alias; its `cfg.Region` is `ie`, so it never writes a target report, and the source's `fleet.targets[""]` never finds it). The door must refuse it, and say why, before the capability check would give a misleading staging refusal (MV-N511, Task 5).
+6. **Parks after the commit, from both engines.** The home box runs home chores from its moves engine (region `@home`) and from `hostbox.Regional` (region = roster name, alias `@home`). Both must acknowledge the same chore once, and a named box must never acknowledge either (`TestPlan13NamedBoxCannotAcknowledgeHomeDelete` covers the named side; Task 4 covers the home side).
---
@@ -80,16 +87,21 @@ Everything else (the staging lane, the lease key, Touch, Commit, the rollback ha
| path | responsibility |
|---|---|
| `docs/superpowers/specs/2026-10-02-move-any-app-design.md` | §2, §7.1, §16 F2 amendment (Task 0) |
-| `internal/moves/envreport.go` | T3's home rule in `CompareEnv` (Task 1) |
+| `internal/moves/envreport.go`, `internal/moves/envreport_test.go` | T3's home rule in `CompareEnv` (Task 1) |
| `internal/host/movesreport.go` | `checkEnv` passes the home target to `CompareEnv` (Task 1) |
-| `internal/host/movesphase1_harness_test.go` | the `intoHome` harness option (Task 2) |
-| `internal/host/moveshome_target_test.go` | new: the into-home witnesses (Tasks 2 and 3) |
-| `internal/console/apiinstancemove.go` | the door's home-target rules (Task 4) |
-| `internal/console/instancesplacement.go` | `movePlacements`, the move-only home fold (Task 4) |
-| `internal/console/boxselect.go` | the target's placement, one home name (Task 4) |
-| `internal/console/apiinstancemove_home_test.go` | new: the door's home-target tests (Task 4) |
+| `internal/host/movesphase1_harness_test.go` | the `intoHome` option: the source's code, box and bucket, and the target's, parameterized (Task 2) |
+| `internal/host/movesland_test.go`, `internal/host/movesland_probe_test.go` | `targetCfg` and `probeLog` take the target from the harness instead of `uk`/`uk-box` (Task 2) |
+| `internal/host/movesparks_test.go` | `newCloseHarness` can run its probe through the production owners (Task 2) |
+| `internal/host/movesprobeowners_test.go`, `internal/host/movesrecoveryowners_test.go`, `internal/host/moveswakesource_test.go` | their fixtures accept a home target box (Task 2) |
+| `internal/host/movesrevert_test.go`, `internal/host/movescut_test.go` | the pre-flip revert fixture accepts a home target (Task 2) |
+| `internal/host/moveshome_target_test.go` | new: the into-home witnesses (Tasks 1, 3 and 4) |
+| `internal/hostops/move_home_target_test.go` | new: declare-lost and recovery grant witnesses for a home target (Task 4) |
+| `internal/console/apiinstancemove.go` | the door's home-target rules and display fills (Task 5) |
+| `internal/console/instancesplacement.go` | `movePlacements`, the move-only home fold (Task 5) |
+| `internal/console/boxselect.go` | one home name for selection (Task 5) |
+| `internal/console/apiinstancemove_home_test.go` | new: the door's home-target tests (Task 5) |
| `docs/superpowers/plans/2026-10-06-moves-phase1-copy.md`, `approved_moves_copy_test.go` | rows MV-N510 to MV-N512, MV-N21 withdrawn |
-| `docs/superpowers/plans/2026-10-06-moves-phase1-rehearsal.md` | P-0, P-2, Run 1b, Runs 2 to 4, Run 6 (Task 5) |
+| `docs/superpowers/plans/2026-10-06-moves-phase1-rehearsal.md`, `docs/superpowers/plans/2026-10-06-moves-phase1-0-index.md` | P-0, P-2, Run 1b, Runs 2 to 4, Run 6 (Task 6) |
---
@@ -98,7 +110,7 @@ Everything else (the staging lane, the lease key, Touch, Commit, the rollback ha
**Files:** modify `docs/superpowers/specs/2026-10-02-move-any-app-design.md`.
- [ ] §2: "between two non-home, non-fleet placements or out of home" becomes "between two non-fleet placements, into home only with host claims on".
-- [ ] §7.1's refusal list: "a target of home" becomes "a target of home on a console without host claims, or a home target box that sets a region code of its own".
+- [ ] §7.1's refusal list: "a target of home" becomes "a target of home on a console without host claims, or a home whose box sets a region code of its own".
- [ ] §16 F2: home as a target is delivered by this plan. `CARLOS_HOME_PLACEMENT` is superseded by Plan 13's signed fleet home designation, which names home once for every reader; no environment variable is added. `carlos move back` stays a follow-on (Ruling R3).
- [ ] A spec header line for the revision, citing this plan and #236.
- [ ] Commit: `git commit -m "spec: moves into home (part 2), §2, §7.1, §16 F2"`.
@@ -119,7 +131,7 @@ Everything else (the staging lane, the lease key, Touch, Commit, the rollback ha
**Tests:**
- `TestCompareEnvHomeTargetSetsNoRegion`: source with `CARLOS_REGION=uk`, target without, `targetHome` true: no diff. The same pair with `targetHome` false: `region-missing` (today's rule kept). Target with `CARLOS_REGION=ie` and `targetHome` true: `region-present`. Source without and target without, `targetHome` true: no diff.
-- `TestHomeTargetT3FromTheRealComposition`: build the home box's held environment through the production composer (`agentOwnedVars` with no region) rather than the harness's fixture, so the test fails today and passes after the fix. Assert the target report's T3 is `ok`.
+- `TestHomeTargetT3FromTheRealComposition`: build the home box's held environment through the production composer (`agentOwnedVars` with no region, as the box's `ComposeHeldEnv` calls it), not the harness's fixture, so the test fails today and passes after the fix. Assert the target report's T3 is `ok`.
- `TestNamedTargetT3Unchanged`: the existing T3 tests pass with `targetHome` false at every call.
**Copy:** MV-N512 (below), pending.
@@ -128,108 +140,130 @@ Everything else (the staging lane, the lease key, Touch, Commit, the rollback ha
---
-### Task 2: The into-home harness and the commit witness
+### Task 2: Fixtures for a home target box
-**Files:** modify `internal/host/movesphase1_harness_test.go`; create `internal/host/moveshome_target_test.go` (Task 1 created it; extend).
+Fixture work only. No production code changes, and every existing test passes unchanged at the end of the task.
-**Harness option `intoHome`.** It mirrors `fromHome` (`h.src = ""`) for the target:
+**Files:** `internal/host/movesphase1_harness_test.go`, `internal/host/movesland_test.go`, `internal/host/movesland_probe_test.go`, `internal/host/movesparks_test.go`, `internal/host/movesprobeowners_test.go`, `internal/host/movesrecoveryowners_test.go`, `internal/host/moveswakesource_test.go`, `internal/host/movesrevert_test.go`, `internal/host/movescut_test.go`. Read each fixture's current constructor before editing; the list is what the into-home witnesses touch, and an implementer who finds another hard-coded `uk`/`uk-box`/`data-uk` on the target side parameterizes it the same way and adds it to the commit.
+
+**The `intoHome` option.** It mirrors `fromHome` (`h.src = ""`) for the target:
- the source is placed in a named code that is not the home's name (use `ca`), with box `ca-box` and bucket `data-ca`, so no fixture name suggests home and the home's name never collides with the source (today's fixture hard-codes `ie-box` and `data-ie` in several places; parameterize them as `fromHome` parameterized the source's code);
- the target is `home-box`, placement `""`, bucket `data-home`, AWS region `eu-west-1`; its edges file has `data_bucket` and no `placement`;
- the roster names `home-box` in region `ie` and carries the fleet designation `home_region: ie`; the claim's op carries `home_region: ie`; the target's `moveCfg.ParkHomeRegion` is `ie`, as `armMovesParks` would set it;
- the target's held-env composer sets **no** `CARLOS_REGION` (the current fixture's `env["CARLOS_REGION"] = "uk"` would hide Task 1's gap);
-- the start uses `BeginAdmittedMove(..., PlacementRegion(""), MoveAdmission{HomeRegion: "ie", ...})`, as the claim-mode door does, not the plain `BeginMove`.
-
-**Tests (each cites the path it drives):**
-- `TestMoveIntoHomeStagesTheHomeLane`: the source round writes `move.json` and the replica lane into `data-home` under `moves/<host>/<id>/`; it takes the lease `control/staging-leases/@home/<host>` and no other; `Touch` adds `@home` and `home-box`; `touched.regions` gains neither `""` nor `ie` from moves.
-- `TestMoveIntoHomeTargetChecksPass`: on `home-box`, T1 to T13 are all `ok`. T8 names the home by its name. T10 probes `data-ca`. T12 refuses inside a quiet window recorded under either `ie` or `@home`.
-- `TestMoveIntoHomeCommitsAndCloses` (through the real cut, flip, landing, probe and commit, as `TestMoveFromHomeClosesThroughTheSourcePark` does for part 1):
- - an earlier home stay is seeded in `data-home` (a root generation and one named generation); the landing queues one `park-generation` per residue generation under `@home`, owner `region:@home`, and none for the install's own;
- - the commit queues the source's location park under `ca`, the lane park under `@home`, and covers the source's predecessor; `quiet_after` gains `ca` and never `""`;
- - the probe's admission records `home-box` with the home box's engine region, and its `PredecessorRegion` is `ca`;
+- the start uses `BeginAdmittedMove(..., PlacementRegion(""), MoveAdmission{HomeRegion: "ie", ...})`, as the claim-mode door does, not the plain `BeginMove`;
+- an option to seed retained claim state under either spelling: `touched.regions` holding `ie` or `@home`, a `quiet_after` key under either, and an earlier residue chore owed under `ie`.
+
+**The production probe for the close harness.** `newCloseHarness` (`movesparks_test.go:38`) installs `probeLog.wake`, which admits with a literal `Box: "uk-box"` and `Region: "uk"`. Add a harness mode that wakes the probe through the production `movesProbeOwners` over the claim runtime, as `TestMovesProbeOwnersThroughTheClaimRuntime` (`movesprobeowners_test.go:814`) does, with the target box's identity: `m.Box = home-box`, `m.Region = ie` (the claim engine's name), `m.HomeRegion = ie`, `m.FleetHomeRegion = ie`. The fake stays for the existing tests.
+
+**The recovery owners and revert fixtures.** `movesRecoveryOwners`' fixture and the cut and revert fixtures accept a home target (the source box is `ca-box`, the target `home-box`), so the source's recovery after a rollback and a pre-flip revert can run through the production owners.
+
+**Tests:** `TestIntoHomeFixtureIsAHomeTarget`: the fixture's record has `Move.To == ""`; the target's edges file has no placement; the claim's `intent.to` is `@home` and `home_region` is `ie`; the target's held env has no `CARLOS_REGION`; the production probe mode admits on `home-box` with region `ie`.
+
+- [ ] Parameterize; run the targeted suites to show nothing existing changed; commit: `git commit -m "moves: fixtures for a home target box (home part 2)"`.
+
+---
+
+### Task 3: The commit into home, through the production owners
+
+**Files:** extend `internal/host/moveshome_target_test.go`.
+
+**Tests (each through the production owners from Task 2, never the fake probe):**
+- `TestMoveIntoHomeStagesTheHomeLane`: the source round writes `move.json` and the replica lane into `data-home` under `moves/<host>/<id>/`; it takes the lease `control/staging-leases/@home/<host>` and no other. Touch, with `touched` empty of home: the write adds `@home` and `home-box`. With `touched` already holding `ie` and `home-box` (seeded): no Touch write, and `touched` is byte-identical afterwards. With `touched` holding `ie` but not `home-box`: one write that adds `@home` and `home-box` and leaves `ie` in place.
+- `TestMoveIntoHomeTargetChecksPass`: on `home-box`, T1 to T13 are all `ok`. T8 names the home by its name. T10 probes `data-ca`. T12 refuses inside a quiet window recorded under `ie`, and again under `@home`.
+- `TestMoveIntoHomeCommitsAndCloses` (through the real cut, flip and landing, then the production probe owners and the claim runtime, as `TestMoveFromHomeClosesThroughTheSourcePark` does for part 1 with the fake):
+ - an earlier home stay is seeded in `data-home` (a root generation and one named generation); the landing queues one `park-generation` per residue generation with region `@home`, owner `region:@home`, and none for the install's own;
+ - the probe's admission, written through `AdmitWriter` with the production `AdmissionEvidence`, records `home-box` with region `ie` and `PredecessorRegion` `ca`; the admission evidence compared `ie` with `op.intent.to = @home` through `SameRegion` and passed;
+ - the C8 provider establishes the prepared generation's files manifest under `data-home`'s `<location>/files/g-<id>/` (`moves.GenerationFilesPrefix`), as `TestLandingWakesEstablishThroughC8` does for a named target;
+ - the commit queues the source's location park in `ca`, the lane park with `@home`, and covers the source's predecessor; `quiet_after` gains `ca` and never `""`;
- after the horizon, the home box's engine acknowledges the lane park, the source parks its location, and the record closes with `Region == ""` and `LastMove.Outcome == "done"`;
- the result can move out of home again: `ClaimMoveRosterPlaces` and the source round accept a second move `"" -> ca` (part 1's path).
-- `TestHomeTargetObligationsUseTheHomeToken`: over the whole of the previous test and Task 3's rollback test, every obligation that moves code queued for the target side names `@home` and owner `region:@home`; none names `ie` or `""`; every `op.intent.to` the claim compared was `@home`. This is Review Focus 1's pin.
+- `TestMoveIntoHomeRestartedProbe`: the first probe attempt is admitted and the agent restarts before readiness. The restarted attempt is published fenced (`TestMovesProbeOwnersPublishARestartedAttemptFenced`'s shape) on `home-box`, its admission's predecessor is the first attempt's generation in `ie`, and the commit proceeds as above.
+- `TestHomeTargetSuppliedRegionsAreTheHomeToken`: over the three tests above and Task 4's, every region a moves call site **supplied** to the claim for the target side (`Touch`, `QueueResidueParks`, `Commit`'s `targetRegion`, `QueueStagingPark`, `TargetReleased`, `Abort`, `Revert`) is `@home`, recorded by a wrapper around `hostclaim.Update` in the test. Every `op.intent.to` the claim compared was `@home`. This is the first half of Review Focus 1.
+- `TestHomeTargetRetainedChoresKeepTheirSpelling`: with a residue chore and a lane chore already owed under `ie` (seeded), the landing's `QueueResidueParks(@home)` and the commit's `QueueStagingPark(@home)` leave the obligation list byte-identical (no second chore, no rewrite to `@home`); the same with them owed under `@home`. Both are acknowledged once by the home box's engine. This is the second half of Review Focus 1.
-- [ ] Write the tests; fix only what they prove broken (expected: nothing outside Task 1); targeted runs; commit: `git commit -m "moves: into-home harness and commit witness (home part 2)"`.
+- [ ] Write the tests; fix only what they prove broken (expected: nothing outside Task 1); targeted runs; commit: `git commit -m "moves: commit into home through the production owners (home part 2)"`.
---
-### Task 3: Rollback, abort, loss and conversion with a home target
+### Task 4: Revert, rollback, abort, loss and conversion with a home target
-**Files:** extend `internal/host/moveshome_target_test.go`; where a test must drive a console grant, add it to `internal/hostops/move_home_target_test.go` (create).
+**Files:** extend `internal/host/moveshome_target_test.go`; create `internal/hostops/move_home_target_test.go`.
-**Tests:**
-- `TestMoveIntoHomeCleanRollback`: the probe fails before any write. `TargetReleased` names `@home` and matches `op.intent.to`; the cleaning park of `home-box`'s location and the move's lane park are queued under `@home`; the source reopens on `ca-box` with `holder.generation = src_gen`; the record ends `rolled_back`; nothing is parked in `data-ca` that the source still serves (J2/F4 through `restoring`).
-- `TestMoveIntoHomeDirtyReturn`: the probe writes, then fails. `home-box` seals its installation and writes the return lane into `data-ca` under `control/staging-leases/ca/<host>`; `QueueReturnLanePark` names `ca`; the source installs the return, and the pre-move generations park after the return (D6), not before.
-- `TestMoveIntoHomeAbortParksTheHomeLane`: abort before the flip. `Abort` queues the lane park under `@home`; the home box's moves engine runs it; `hostbox.Regional` on the home box (region `ie`, alias `ie`) acknowledges it after the horizon; a named box's runner refuses it; the record clears only after that (Plan 5 Task 1's rule).
-- `TestMoveIntoHomeDeclareLost` (hostops): a home target stopped mid-landing and declared lost through Plan 4 Task 10's door. The loss intent's `TargetRegion` is `@home`; the source is re-admitted with the loss; the record shows `loss_ref`. It reuses Plan 13's `TestPlan13FenceRecoveryHomeAndNamed` fixture rather than a new one.
-- `TestCommittedLandingIntoHomeConvertsOnTargetFence`: after the commit, `home-box` is fenced as terminated. `hostclaim.ConvertCommittedLanding` turns the claim into `relocating` with `intent.region = ie` (the roster name), `move_to_region = @home`, and keeps the lane park under `@home` and the source's finalization chores. Region HA reviews this test (Ask H1). It changes no region HA code.
+**Tests (production owners throughout):**
+- `TestMoveIntoHomePreFlipRevert`: the cut stops the source on `ca-box` and fails before the flip (the shape of `TestCutDeadlinePassedReverts`). `Revert(id, ca-box, @home, &laneScope, ...)` queues the lane park with `@home`, owner `region:@home`; the source's revert probe re-admits on `ca-box` through `movesRecoveryOwners` (`TestRevertProbeAdmitsUncovered`'s shape) in region `ca`; `home-box`'s moves engine runs the lane park on `data-home`, and the home box's `hostbox.Regional` acknowledges it after the horizon. Nothing in `data-ca` is parked.
+- `TestMoveIntoHomeCleanRollback`: the probe fails before any write. `TargetReleased` names `@home` and matches `op.intent.to`; the cleaning park of `home-box`'s location and the move's lane park are queued with `@home`; the source reopens on `ca-box` through `movesRecoveryOwners` with `holder.generation = src_gen`; the record ends `rolled_back`; nothing is parked in `data-ca` that the source still serves (J2/F4 through `restoring`).
+- `TestMoveIntoHomeDirtyReturn`: the probe writes, then fails. `home-box` seals its installation and writes the return lane into `data-ca` under `control/staging-leases/ca/<host>`; `QueueReturnLanePark` names `ca`; the source installs the return through the recovery owners, establishing its files from the return's manifest (C8), and the pre-move generations park after the return (D6), not before.
+- `TestMoveIntoHomeAbortParksTheHomeLane`: abort before the cut. `Abort` queues the lane park with `@home`; `home-box`'s moves engine runs it; `hostbox.Regional` on `home-box` (region `ie`, alias `ie`) acknowledges it after the horizon; a named box's runner refuses it; the record clears only after that (Plan 5 Task 1's rule). This is Ask A2's witness.
+- `TestHomeRegionalAcknowledgesBothSpellings`: the real `hostbox.Regional` on `home-box` acknowledges a lane `park-replica` and a residue `park-generation` owed under `@home`, and the same owed under `ie`, each exactly once; a `ca` box's runner acknowledges none of the four. Also Ask A2's witness.
+- `TestMoveIntoHomeDeclareLost` (hostops): a home target stopped mid-landing and declared lost through Plan 4 Task 10's door. The loss intent's `TargetRegion` is `@home`; the source is re-admitted with the loss through `AdmitWriterWithLoss`; the record shows `loss_ref`. It reuses Plan 13's `TestPlan13FenceRecoveryHomeAndNamed` fixture rather than a new one.
+- `TestCommittedLandingIntoHomeConvertsOnTargetFence`: after the commit, `home-box` is fenced as terminated. `hostclaim.ConvertCommittedLanding` turns the claim into `relocating` with `intent.region = ie` (the roster name) and `move_to_region = @home`, and keeps the lane park and the source's finalization chores in their retained spelling. This is Ask H1's witness. It changes no region HA code.
-- [ ] Write the tests; any production fix they force is in moves' code only, and a failure that points into claim or region HA code becomes an ask on #236, not a patch; targeted runs; commit: `git commit -m "moves: into-home rollback, abort, loss and conversion witnesses (home part 2)"`.
+- [ ] Write the tests; any production fix they force is in moves' code only, and a failure that points into claim or region HA code becomes an ask on #236, not a patch; targeted runs; commit: `git commit -m "moves: into-home revert, rollback, abort, loss and conversion witnesses (home part 2)"`.
---
-### Task 4: The door admits a home target under host claims
+### Task 5: The door admits a home target under host claims
**Files:** modify `internal/console/apiinstancemove.go`, `internal/console/instancesplacement.go`, `internal/console/boxselect.go`; create `internal/console/apiinstancemove_home_test.go`; the copy record and `approved_moves_copy_test.go`.
**Interfaces:**
-- `movePlacements(ctx, s release.Store) (caps map[string]placementCapability, published bool)`: `placements()` plus the home, folded under `""` from every edges file with no `placement`. Same reachability rule: no `eip`, no capability. Staging is `data_bucket != ""`. `published` keeps `placements()`'s meaning, so a home-only fleet still reads "publishes no armed regions". `placements()` and `armedPlacements()` are unchanged.
+- `movePlacements(ctx, s release.Store, home string) (caps map[string]placementCapability, homeNamed []string, published bool)`: `placements()` plus the home, folded under `""` from every edges file with no `placement`. Same reachability rule: no `eip`, no capability. Staging is `data_bucket != ""`. `homeNamed` is the sorted box ids of edges files whose `placement` equals `home` (when `home != ""`). `published` keeps `placements()`'s meaning, so a home-only fleet still reads "publishes no armed regions". `placements()` and `armedPlacements()` are unchanged.
- `(s *Server) stagingNames(caps) string`: `stagingCodes` with each code passed through `homeRegionOut`, so MV-N20's list names the home by its name.
-- `moveSelection` gains `TargetPlacement string`: the target's edges-file `placement`, as published.
-- `selectMoveBoxes` and `verifiedRoster` take the home name from one helper, `s.moveHomeName()`: `dbClaimHomeRegion()` with host claims, `cfg.HomeRegion` without. That is the name `homeRegionIn` already uses, so `eligibleBoxes` and the door agree when the console's home is the claim-mode fallback.
+- `(s *Server) moveHomeName() string`: `dbClaimHomeRegion()` with host claims, `cfg.HomeRegion` without. `selectMoveBoxes`, `verifiedRoster` and `eligibleBoxes` take the home name from it. That is the name `homeRegionIn` already uses, so selection and the door agree when the console's home is the claim-mode fallback.
**The Phase 1 request door, in order (`handleAPIInstanceMoveRequest`):**
1. `to == ""` and not a host-claim console: refuse with MV-N510 (400) before any read or write. This replaces MV-N21 on this path.
-2. `to == rec.Region`: unchanged (MV-N16, filled through `homeRegionOut`).
+2. `to == rec.Region`: unchanged (MV-N16 already fills through `homeRegionOut`).
3. The manifest rule: a home target is allowed when some entry of `m.Regions` maps to `""` through `homeRegionIn`, or the list is empty. Named targets keep `slices.Contains`. MV-N2 fills `<to>` through `homeRegionOut`. Before writing this step, read the create door's rule (`instanceRegionAllowed` at `apiinstances.go:1268` and `regionchoices.go:23-41`). If create treats a home entry differently, follow `regionchoices.go`'s `homeRegionIn` mapping and record the difference in the task's report as a finding for the create door.
-4. Staging: `movePlacements` in place of `placements`. MV-N20 fills `<to>` and the list through `homeRegionOut` and `stagingNames`.
-5. `selectMoveBoxes` as today. MV-N23 fills `<to>` through `homeRegionOut`.
-6. New: `to == ""` and `sel.TargetPlacement != ""`: refuse with MV-N511 (409), naming the box. Before any claim read.
-7. Everything after (floor, claim, roster, `startClaimModeMove`, `BeginClaimMove`) is unchanged. `MatchesHome` and `ClaimMoveRosterPlaces` already refuse a home target without the designation (MV-N177).
+4. **New, before the capability check:** `to == ""` and `len(homeNamed) > 0`: refuse with MV-N511 (409), naming the first box. Why here and not after selection: the home fold takes placementless files only, so a deployment whose only home box sets `CARLOS_REGION=<home name>` has no `caps[""]` staging, and the capability check would answer MV-N20 ("publishes no staging bucket"). That is false, since the box may well publish a bucket, and it hides the misconfiguration the operator has to fix. Checking the edges files the capability read already loaded costs nothing extra. Because the read is not liveness-joined, a stale file of a home-named box that has since gone also refuses; that fails closed and is fixed by removing the file, which the refusal names. A deployment with both a placementless home box and a home-named box also gets MV-N511, not M-C21, for the same reason.
+5. Staging: `movePlacements` in place of `placements`. MV-N20 fills `<to>` through `homeRegionOut` and the list through `stagingNames`.
+6. `selectMoveBoxes` as today. MV-N23 fills `<to>` through `homeRegionOut`. M-C21's `ManyRegion` is already `homeRegionOut`.
+7. The floor: MV-N5 (`apiinstancemove.go:633`) fills `<from>` and `<to>` through `homeRegionOut` (today `to` is raw).
+8. `startClaimModeMove`: MV-N177 (`:786`, `:797`) fills `<to>` through `homeRegionOut` (today raw); the call to `moveClaimCASFailed` at `:831`, and the plain path's call, pass `homeRegionOut(to)`, so MV-N30 ("the instance has unfinished cleanup in <to>") names the home. `moveClaimCASFailed`'s parameter is renamed `toName` to say it is a display name. Every other step is unchanged: `MatchesHome` and `ClaimMoveRosterPlaces` already refuse a home target without the designation.
-The legacy door (switch off) is not touched. Its em-dash refusal stays until Plan 6 Task 8 retires it.
+The record, the claim and `BeginClaimMove` keep receiving the code `to` (`""`); only display arguments change. The legacy door (switch off) is not touched. Its em-dash refusal stays until Plan 6 Task 8 retires it.
**Tests (`apiinstancemove_home_test.go`, using the claim test fixture of `apiinstancemove_claim_test.go`):**
- `TestMoveDoorClaimModeAcceptsHomeTarget`: `--to ie` on a console whose home is `ie`, a roster with the designation, a placementless home box with a data bucket: 202. The claim is `moving` with `intent.to = @home` and `intent.home_region = ie`; the record's `Move.To` is `""`; the view's `target` is `ie`.
- `TestMoveDoorPlainPathRefusesHomeTarget`: switch on, no host claims: 400 with MV-N510; the claim and the record are byte-identical before and after.
-- `TestMoveDoorHomeStagingFromItsEdgesFile`: home box without `data_bucket`: MV-N20 naming `ie`; with it: passes. A named-only list names no empty code.
-- `TestCreatePlacementsUnchangedByHomeFold`: `placements()` over a fleet with a placementless box returns the same map and `published` as before this task (Review Focus 3).
-- `TestMoveDoorHomeTargetMustPublishNoPlacement`: the only `ie` box sets `CARLOS_REGION=ie`: 409 with MV-N511; no claim write, no registration.
-- `TestMoveDoorHomeTargetNeedsTheDesignation`: the roster has no `home_region`: MV-N177; nothing registered.
-- `TestMoveDoorManifestNamesHome`: `regions: ["ie","uk"]` allows `--to ie`; `regions: ["uk"]` refuses with MV-N2 naming `ie`, never an empty name.
-- `TestMoveDoorFillsNameTheHome`: MV-N2, MV-N16, MV-N20 and MV-N23 never render an empty region name for a home target.
+- `TestMoveDoorHomeStagingFromItsEdgesFile`: home box without `data_bucket`: MV-N20 naming `ie`, with a list that names no empty code; with it: passes.
+- `TestCreatePlacementsUnchangedByHomeFold`: `placements()` over a fleet with a placementless box returns the same map and `published` as before this task (Review Focus 4).
+- `TestMoveDoorHomeTargetMustPublishNoPlacement`: the only `ie` box publishes `placement: ie` and a data bucket: 409 with MV-N511 naming it, not MV-N20; no claim read, no registration. A second case with both a placementless home box and a home-named box: MV-N511 again.
+- `TestMoveDoorHomeTargetNeedsTheDesignation`: the roster has no `home_region`: MV-N177 reading "... active boxes in ca and ie"; nothing registered.
+- `TestMoveDoorManifestNamesHome`: `regions: ["ie","uk"]` allows `--to ie`; `regions: ["uk"]` refuses with MV-N2 naming `ie`.
+- `TestMoveDoorHomeRefusalsNameTheHome`, one case per sentence, each driven to its refusal with a home target: MV-N2, MV-N5 (a home box below the floor: "a box in ca or ie is below the moves protocol floor..."), MV-N16, MV-N20, MV-N23, MV-N30 (a pending chore under `@home` or `ie` makes `BeginAdmittedMove` return `ErrPendingChore`: "the instance has unfinished cleanup in ie..."), MV-N177. Each asserts the exact approved template with `ie` filled, and that no sentence contains `" in "`, `" or "` or `" and "` followed by the end of the string.
- `TestLegacyMoveDoorStillRefusesHome`: switch off: the legacy sentence, byte for byte.
- The lint (`TestPlan13NoDirectPlacementEngineComparison`) stays green.
-**Copy:** MV-N510 and MV-N511 pending. MV-N21 is withdrawn: its row is removed from the record with a note, as MV-N388's was, and from `movesCopyIDs`, in this commit.
+**Copy:** MV-N510 and MV-N511 pending. MV-N21 is withdrawn: its row is removed from the record with a note, as MV-N388's was, and from `movesCopyIDs`, in this commit. MV-N2, MV-N5, MV-N20, MV-N23, MV-N30 and MV-N177 keep their approved text; only their fills change (MV-N16 already names the home).
- [ ] Write the failing tests; implement; targeted runs plus the copy gate; commit: `git commit -m "console: the move door admits a home target under host claims (home part 2)"`.
---
-### Task 5: Rehearsal runbook and index
+### Task 6: Rehearsal runbook and index
**Files:** modify `docs/superpowers/plans/2026-10-06-moves-phase1-rehearsal.md`, `docs/superpowers/plans/2026-10-06-moves-phase1-0-index.md`.
- [ ] **P-0, the home item.** Part 2 merged and rolled (boxes, then consoles) is required for any run that moves into home. The rest of the item is unchanged: part 1, Plan 13, and Plan 13's order (readers, then the designation, then the flag). Replace the quoted MV-N21 sentence with "the door refuses moves into home without host claims (MV-N510)".
-- [ ] **P-0, new: one placementless box.** For each deployment, list `control/edges/` and record that exactly one edges file has no `placement`. S1 and T8 refuse a move into **or out of** home while two do (Ruling R2). On dev, `carlos-dev-ie-b` makes two. This also blocks Run 1 today, so it is not part 2's alone.
+- [ ] **P-0, new: one placementless box.** For each deployment, list `control/edges/` and record that exactly one edges file has no `placement`. S1 and T8 refuse a move into **or out of** home while two do, whether or not the second box is running (an edges file outlives its box). On dev, `carlos-dev-ie-b` makes two; Ruling R2's procedure applies to every dev run that touches home, Run 1 included.
- [ ] **P-2.** Every named region's `sibling_staging_arns` lists the home data bucket's ARN, and the home's lists every named data bucket. Record the home data bucket's name and check it is not the deployment bucket.
-- [ ] **Run 1b (new), dev, `uk` to `ie`, a commit into home.** Run 1's host, moved back after Run 1's quiet window (T12). Same checks as Run 1, with `<pr-ie> = home`. Additional checks: the lane is in the home data bucket under `moves/<host>/<move>/`; the claim's `touched.regions` holds `@home`; after the close, `parked/<inc>/<host>/` in the home bucket holds the residue of Run 1's stay, if any; `quiet_after` names `uk`. Blockers: Run 1 passed; part 2 rolled; the designation published on dev; P-0's one-placementless-box item.
+- [ ] **Run 1b (new), dev, `uk` to `ie`, a commit into home.** Run 1's host, moved back after Run 1's quiet window (T12). Same checks as Run 1, with `<pr-ie> = home`. Additional checks: the lane is in the home data bucket under `moves/<host>/<move>/`; the claim's `touched.regions` holds a spelling of home (`ie` from Run 1, or `@home`), and only one; after the close, `parked/<inc>/<host>/` in the home bucket holds the residue of Run 1's stay, if any; `quiet_after` names `uk`. Blockers: Run 1 passed; part 2 rolled; the designation published on dev; P-0's one-placementless-box item. Note for the operator: where the console shares the home box, the landing runs on the console's own box (Ruling R7).
- [ ] **Runs 2 and 3.** Once part 2 is rolled, they run as written (`uk` to `ie`, into home). The out-of-home arrangement stays as the fallback while part 2 is not rolled.
- [ ] **Run 4.** Unchanged: `ie` to `uk`, with `carlos-dev-uk` stopped. It never stops the home box. Its blocker becomes "Runs 1, 1b, 2 and 3 passed". It does not need moves into home itself.
- [ ] **Run 6.** "Both parts merged and rolled" stays. The "dev rehearsal from home" blocker becomes "dev Runs 1 and 1b passed".
-- [ ] **Sources table.** Replace the MV-N21 line (`apiinstancemove.go:537-540`) and the `moves-home-parks` "parts 1 and 2" line with this plan and its merge commit.
+- [ ] **Sources table.** Replace the MV-N21 line (`apiinstancemove.go:537-540`) and the `moves-home-parks` "parts 1 and 2" line with this plan and its merge commit, and add `internal/host/movesreport.go:113` (`loadMoveFleet` counts every box per placement before the data-bucket check) as the source of the one-placementless-box item.
- [ ] **Index.** Add a row after Plan 7: "Home part 2: `2026-10-15-moves-home-region-part2.md`".
- [ ] Commit: `git commit -m "docs: rehearsal and index for moves into home (home part 2)"`.
---
-### Task 6: Gate and review
+### Task 7: Gate and review
- [ ] The full local gate once (Global Constraints), excluding the whole `internal/host` suite, which CI runs.
-- [ ] Astra (codex, from a committed brief) reviews Review Focus 1 to 5 against the branch. Commit its findings unedited beside this plan as `CODEX-PLAN-MOVES-HOME-PART2-R1.md`; fix and record dispositions here.
-- [ ] Post the branch, the asks and the rulings on #236.
+- [ ] Astra (codex, from a committed brief) reviews the built branch against Review Focus 1 to 6. Commit its findings unedited beside this plan as `CODEX-PLAN-MOVES-HOME-PART2-BUILD-R1.md`; fix and record dispositions here.
+- [ ] Post the branch, the witnesses for A2, H1 and H3, and the rulings on #236.
- [ ] Re-merge `main`, wait for CI, then Paul's go to merge (`amadan branch merge -squash`).
## Copy rows (pending)
@@ -242,22 +276,24 @@ To append to `2026-10-06-moves-phase1-copy.md` under a new heading "Moves into h
| MV-N511 | internal/console/apiinstancemove.go | <box> sets CARLOS_REGION, so it cannot take a move into the home region | pending |
| MV-N512 | internal/moves/envreport.go (a T3 kind inside MV-N129's list) | region-present | pending |
-Withdrawn in Task 4: MV-N21 ("moves to the home region are not supported yet. the home publishes no staging lane"). Fills that change but keep their approved text: MV-N2, MV-N16, MV-N20 and MV-N23 name the home by its name instead of the empty code.
+Withdrawn in Task 5: MV-N21 ("moves to the home region are not supported yet. the home publishes no staging lane"). Fills that change but keep their approved text: MV-N2, MV-N5, MV-N20, MV-N23, MV-N30 and MV-N177 name the home by its name instead of the empty code.
## Failure modes
| # | failure | effect | guard |
|---|---|---|---|
-| F1 | a moves call site hands the claim the home's roster name where `@home` is meant | most exact checks refuse (fail closed); `requireRetiredRegion` would allow a location park in the live home region | Task 2's `TestHomeTargetObligationsUseTheHomeToken`; the park view's live-set rule; Ask A1 hardening |
-| F2 | the staging lease keyed by the roster name | two writers on one lane | `stagingLeaseKey` takes record codes only; Task 2 asserts the key |
-| F3 | a box with `CARLOS_REGION=<home name>` chosen as a home target | the move stalls in `preparing` (no target report; the source finds no `targets[""]`) | MV-N511 at the door (Task 4) |
-| F4 | two placementless boxes (dev's `carlos-dev-ie-b`; any agent box without `CARLOS_REGION`) | the door accepts, then S1 or T8 refuses with M-C21 / MV-N166 | Ruling R2; runbook P-0 check |
-| F5 | T3 `region-missing` on a home target | every move into home waits at `preparing` | Task 1 |
-| F6 | a home box on a build without Task 1 | the same wait, with T3's reason in the target report; the operator aborts | roll boxes before consoles (Ruling R5) |
-| F7 | no designation, or the console's home name differs from it | MV-N177 for every claim-mode move; a home box never arms (T13) | Plan 13's rollout order; nothing new |
-| F8 | the IAM mesh lacks the home bucket | the source's lane round fails `AccessDenied`, logged; or T10 fails on the home box | runbook P-2 |
-| F9 | the home box is also the console's box, and it dies mid-landing | the console's move pass stops with it; box-side recovery and the durable records wait for the console's return | Ruling R7 |
-| F10 | the plain Phase 1 path writes `intent.to = ""` | `BeginMove` refuses with `ErrMalformed`, which the door would answer as a 502 | MV-N510 refuses first (Task 4) |
+| F1 | a moves call site supplies the home's roster name where `@home` is meant | most exact checks refuse (fail closed); `requireRetiredRegion` would allow a location park in the live home region | Task 3's `TestHomeTargetSuppliedRegionsAreTheHomeToken`; the park view's live-set rule; Ask A1 hardening |
+| F2 | the staging lease keyed by the roster name | two writers on one lane | `stagingLeaseKey` takes record codes only; Task 3 asserts the key |
+| F3 | a replay canonicalizes or duplicates a chore already owed under `ie` | a second park of the same work, or a rewritten obligation the regional runner no longer matches | Plan 13's alias-aware queue; Task 3's `TestHomeTargetRetainedChoresKeepTheirSpelling` |
+| F4 | a box with `CARLOS_REGION=<home name>` | as a home target the move would stall in `preparing` (no target report; the source finds no `targets[""]`) | MV-N511 at the door, before the capability check (Task 5) |
+| F5 | two placementless edges files (dev's `carlos-dev-ie-b`; any agent box without `CARLOS_REGION`, running or not) | the door accepts, then S1 or T8 refuses with M-C21 / MV-N166, for moves into **and** out of home | Ruling R2; runbook P-0 check |
+| F6 | T3 `region-missing` on a home target | every move into home waits at `preparing` | Task 1 |
+| F7 | a home box on a build without Task 1 | the same wait, with T3's reason in the target report; the operator aborts | roll boxes before consoles (Ruling R5) |
+| F8 | no designation, or the console's home name differs from it | MV-N177 for every claim-mode move; a home box never arms (T13) | Plan 13's rollout order; nothing new |
+| F9 | the IAM mesh lacks the home bucket | the source's lane round fails `AccessDenied`, logged; or T10 fails on the home box | runbook P-2 |
+| F10 | the home box is also the console's box, and it dies mid-landing | the console's move pass stops with it; box-side recovery and the durable records wait for the console's return | Ruling R7 |
+| F11 | the plain Phase 1 path writes `intent.to = ""` | `BeginMove` refuses with `ErrMalformed`, which the door would answer as a 502 | MV-N510 refuses first (Task 5) |
+| F12 | a door sentence filled with the raw code | an operator reads "in " or "in ca and " | Task 5's fills and `TestMoveDoorHomeRefusalsNameTheHome` |
## Rollout order
@@ -271,7 +307,7 @@ Part 2 adds no switch of its own. A home target is reachable only when the desig
## Rehearsal changes
-- Run 1 (`ie` to `uk`) is unchanged, but **today it is blocked on dev by `carlos-dev-ie-b`'s edges file** (S1's `oneBoxRefusal` counts every placementless file). That is a finding against part 1's runbook, raised here because part 2 meets it first. Ruling R2 resolves both.
+- Run 1 (`ie` to `uk`) is unchanged, but **today it is blocked on dev by `carlos-dev-ie-b`'s edges file** (Ruling R2). That blocker is independent of part 2.
- Run 1b (new): `uk` to `ie`, a commit into home, Run 1's host moved back.
- Runs 2 and 3 return to as written (into home) once part 2 is rolled.
- Run 4 is unchanged and does not move into home. It gates on Runs 1, 1b, 2 and 3. Stopping the home box for a declare-lost would stop dev's console, which is why Run 4 stays out of home (Ruling R6).
@@ -283,11 +319,12 @@ Part 2 adds no switch of its own. A home target is reachable only when the desig
Only Paul can decide these. Each has a recommendation.
- **R1. Home targets only under host claims.** The plain Phase 1 path (switch on, claims off) keeps refusing a home target (MV-N510), because it has no designation to resolve home against and its `BeginMove` would write `to = ""`. *Recommend: yes.* Phase 1 cannot land anything in production without claims anyway (its parks arm only with both flags).
-- **R2. Two placementless boxes.** The door counts eligible boxes (D15), but S1 and T8 count every edges file with that placement. On dev, `carlos-dev-ie-b` publishes no placement, so every move into or out of dev's home is refused at S1 or T8. Options:
- - (a) keep the box rule, and take `carlos-dev-ie-b` out of `control/edges/` for the rehearsal window: stop it, delete its edges file (it rewrites the file when it boots), and record both in the runbook;
- - (b) change S1 and T8 to count eligible boxes, as the door does. That is a moves design change: the box would need the roster and `boxchoice.Eligible`;
- - (c) rehearse moves into home on the flagship only.
- *Recommend (a).* The box rule is the safer one. A second placementless box reconciles every unregioned record, so after a landing into home both boxes would hold a row for the host (amadan #286's hazard), which is what T8 exists to refuse. It also unblocks Run 1 today.
+- **R2. Dev's second home box blocks every dev run that touches home, today, independent of part 2.** `loadMoveFleet` (`internal/host/movesreport.go:113`) adds every edges file's box to `boxes[placement]` before it checks for a data bucket, and with no liveness check. S1's `oneBoxRefusal` counts `boxes[From]` and `boxes[To]`, and T8 counts `boxes[To]`. `carlos-dev-ie-b` publishes no placement, so `boxes[""]` holds two boxes and Run 1 (`ie` to `uk`) is refused at S1 with "the home region has more than one box" before any copy. The door, which counts eligible boxes (D15), accepts it first. Options:
+ - **(a) Stop `carlos-dev-ie-b` during rehearsal runs.** Stopping its agent alone is **not enough**: its `control/edges/carlos-dev-ie-b.json` stays, and `loadMoveFleet` still counts it. The procedure is: stop the box (the agent or the instance), then delete its edges file with Paul's go, run, and start it again afterwards (its edges pass rewrites the file on boot). It also takes #286's two-home-box hazard out of the window, since rehearsal apps then wake on one box only. Cost: an operator step per window, and dev loses its second home box while it lasts.
+ - **(b) Make S1 and T8 count only eligible boxes**, as the door does. That is a box-side rule change: the box would need the roster, the box identity records and `boxchoice.Eligible`'s inputs, and it would loosen the check that stops a second placementless box from reconciling an unregioned record that has just landed in home (#286). It needs its own task, its own review, and a decision on what "eligible" means to a box that cannot read the console's secrets fingerprint.
+ - **(c) Give `carlos-dev-ie-b` a placement of its own** (for example `CARLOS_REGION=ie-b`). That ends the collision for good, but it changes which records the box reconciles: it stops taking unregioned records, so any host woken on it moves back to `carlos-dev-ie`. That is a dev topology change with its own risk, and region HA's two-box testing in dev's home region would need another arrangement.
+ - **(d) Retire `carlos-dev-ie-b`** if region HA's dev testing no longer needs it. Cleanest if true; only Paul and region HA know.
+ *Recommend (a) for the rehearsal windows, now,* because it unblocks Run 1 without a code change and keeps the box rule that protects against #286. Ask region HA whether (d) is possible; if it is, prefer it over repeating (a). Do not take (b) as part of this plan: if Paul wants it, it is a separate moves task with its own review.
- **R3. `--to` vocabulary and `carlos move back`.** The door accepts the home's configured name only, as every other surface prints it; there is no `home` keyword, because a named region may be called `home` (Plan 13). `carlos move back` stays a follow-on, as spec §16 F2 lists it. *Recommend: yes to both.*
- **R4. Copy.** Approve MV-N510 to MV-N512 and the withdrawal of MV-N21 (copy review). *Recommend: review with the branch, before merge.*
- **R5. No protocol floor bump.** Task 1 changes the box's T3. A home box without it fails T3 and waits (fail closed), and the operator aborts. Bumping `moves.ProtocolVersion` to 2 would make the door refuse up front, but it would also refuse every move, to any region, until every box of both regions rolls. *Recommend: no bump; roll boxes before consoles*, as the release recipe already says.
@@ -296,22 +333,36 @@ Only Paul can decide these. Each has a recommendation.
## Asks
-None blocks this plan. Each is a confirmation unless it says otherwise. Post them on #236.
+None blocks this plan. Each names the witness that answers it; please review the witness rather than answer in prose. Post them on #236.
**To platform-1e (the claim):**
-- **A1. The exact-region contract, and one optional hardening.** Moves' contract is: every region moves hands to `Abort`, `Revert`, `Commit`, `TargetReleased`, `QueueStagingPark`, `QueueResidueParks`, `QueueReturnLanePark` and `Touch` is a placement-derived engine region (`park.PlacementRegion` of a record code, so `@home` for home), never a roster name. Task 2 pins it. Please confirm the claim keeps its exact `intent.to` comparisons on that basis. Optional hardening, recommended: compare `requireRetiredRegion` (`internal/hostclaim/move.go:549`) through `regionname.SameRegion` under `op.intent.home_region`, so a roster-name caller can never read the live home as "not the target" (Review Focus 1).
-- **A2. The home regional runner.** Confirm `hostbox.Regional` on a home box acknowledges `region:@home` `park-replica` lane chores and `park-generation` residue chores queued by a landing into home (Plan 13 Review Focus 2). Task 3 runs it against the real runner.
-- **A3. No further claim gate.** Confirm that beyond Plan 13's designation nothing in the claim refuses a `moving` op with `intent.to = @home` (`BeginAdmittedMove` already start-guards both spellings).
+- **A1. The exact-region contract, and one optional hardening.** Moves' contract: every region moves supplies to `Abort`, `Revert`, `Commit`, `TargetReleased`, `QueueStagingPark`, `QueueResidueParks`, `QueueReturnLanePark` and `Touch` is a placement-derived engine region (`park.PlacementRegion` of a record code, so `@home` for home), never a roster name; retained claim state keeps whatever spelling it has. Witnesses: `TestHomeTargetSuppliedRegionsAreTheHomeToken` and `TestHomeTargetRetainedChoresKeepTheirSpelling` (Task 3). Please confirm the claim keeps its exact `intent.to` comparisons on that basis. Optional hardening, recommended: compare `requireRetiredRegion` (`internal/hostclaim/move.go:549`) through `regionname.SameRegion` under `op.intent.home_region`, so a roster-name caller can never read the live home as "not the target" (Review Focus 1).
+- **A2. The home regional runner.** Witnesses: `TestMoveIntoHomeAbortParksTheHomeLane`, `TestMoveIntoHomePreFlipRevert` and `TestHomeRegionalAcknowledgesBothSpellings` (Task 4) run the real `hostbox.Regional` on a home box against `@home` and `ie` lane and residue chores. Please review them.
+- **A3. No further claim gate.** Witness: `TestMoveDoorClaimModeAcceptsHomeTarget` (Task 5) and `TestMoveIntoHomeCommitsAndCloses` (Task 3) drive a `moving` op with `intent.to = @home` from the start CAS to the close. Please confirm nothing else in the claim should refuse it.
**To region HA (relocation):**
-- **H1. The committed-landing conversion into home.** Review Task 3's `TestCommittedLandingIntoHomeConvertsOnTargetFence`: `intent.region` is the roster name, `move_to_region` is `@home`, and the lane parks and the source's finalization chores are kept. No code change is expected.
-- **H2. The two-box model.** Moves into home stay one-box (D15, T8 and Ruling R2), so the two-box `internal/sim/hostlife` model needs no new scenario. Confirm, or name the scenario.
-- **H3. C8 on a home landing.** A landing into home publishes its prepared files manifest under the home bucket's `<location>/files/g-<op>/` (`moves.GenerationFilesPrefix`), and INV-30 protects the source's files until the commit. Confirm that relocation's env-by-row lookup and the prepared-authority reader make no placement assumption a placementless home box breaks.
+- **H1. The committed-landing conversion into home.** Witness: `TestCommittedLandingIntoHomeConvertsOnTargetFence` (Task 4): `intent.region` is the roster name, `move_to_region` is `@home`, and the lane parks and the source's finalization chores are kept in their spelling. Please review it. No code change is expected.
+- **H2. The two-box model.** Moves into home stay one-box (D15, T8 and Ruling R2), so the two-box `internal/sim/hostlife` model needs no new scenario. Confirm, or name the scenario. Separately, for Ruling R2: does region HA still need `carlos-dev-ie-b` in dev's home region (option d)?
+- **H3. C8 on a home landing.** Witnesses: `TestMoveIntoHomeCommitsAndCloses` and `TestMoveIntoHomeRestartedProbe` (Task 3) establish the prepared files manifest under `data-home`'s `<location>/files/g-<op>/` through the production provider on a placementless home box, and `TestMoveIntoHomeDirtyReturn` (Task 4) establishes the source's files from the return. Please review them against relocation's env-by-row lookup and prepared-authority reader.
## Self-review
-- Every gap named in the brief has a task or a ruling: the lane (Q1, Task 4 fold, P-2), the ARNs (P-2), T1 to T13 (Task 1, Task 2), the landing (Task 2), rollback and return (Task 3), parks after the commit (Tasks 2 and 3), the alias in Touch, Commit and Release (Task 2's pin), `--to` (R3).
+- Every gap named in the brief has a task or a ruling: the lane (Q1, Task 5 fold, P-2), the ARNs (P-2), T1 to T13 (Tasks 1 and 3), the landing (Task 3), revert, rollback and return (Task 4), parks after the commit (Tasks 3 and 4), the alias in Touch, Commit and Release (Task 3's two pins), `--to` (R3).
- Nothing changes claim or region HA code. Anything a test finds there becomes an ask.
+- Every witness on a path where `ie` meets `@home` runs through the production owners.
- Copy rows are pending, have no em dashes, and are in the commit that writes them.
+
+## Review round 1 (Astra, on be29ccd0): dispositions (rev 2)
+
+Verdict NOT READY, four P2. Astra confirmed the diagnosis (T3 `region-missing`, the door and capability gaps, the one-box finding at `loadMoveFleet:113`) and that no claim or region HA production change is needed; it supported R1, R3, R5 and R6, called R2(a) and R7 reasonable, and asked for executable witnesses behind A2, H1 and H3.
+
+| # | finding | rev 2 |
+|---|---|---|
+| 1 | P2 the witnesses demanded canonical `@home` where Plan 13 keeps existing spellings (`sourceRound:323` skips Touch when `touched` holds `ie`; an `@home` replay keeps an owed `ie` chore, `plan13_followups_test.go:28`) | Global Constraints and Review Focus 1 split supplied arguments (`@home`) from retained state (either spelling). Task 3's Touch test covers empty, `ie` held, and `ie` without the box; `TestHomeTargetSuppliedRegionsAreTheHomeToken` asserts arguments only; new `TestHomeTargetRetainedChoresKeepTheirSpelling` seeds both spellings and asserts no duplicate and no rewrite. Run 1b's check accepts either spelling. |
+| 2 | P2 the close harness's fake probe (`movesparks_test.go:38`, `movesland_probe_test.go:73`) bypasses the production owners, admission evidence and files publication | New Task 2 is fixture work only, with the full file list, and adds a production-probe mode over the claim runtime. Tasks 3 and 4 run every witness through `movesProbeOwners`, `movesRecoveryOwners` and the C8 provider, add `TestMoveIntoHomeRestartedProbe` and `TestMoveIntoHomePreFlipRevert`, and the dirty return establishes files through C8. Review Focus 3 is new. |
+| 3 | P2 more empty home names in the door: MV-N5 (`:633`), MV-N177 (`:786`), MV-N30 (`:752` via `:831`) | Task 5 steps 7 and 8 pass `homeRegionOut` for every display argument, keep the approved templates and rename `moveClaimCASFailed`'s parameter `toName`; `TestMoveDoorHomeRefusalsNameTheHome` drives each of the six sentences, and MV-N16, to its refusal. |
+| 4 | P2 MV-N511's test could not reach its refusal: the home fold takes placementless files only, so MV-N20 fires first at `:599` | The placement-mismatch check moves ahead of the capability check (Task 5 step 4), from `movePlacements`' new `homeNamed` list. Chosen because MV-N20 would tell the operator the region publishes no staging bucket, which is false and hides the real misconfiguration. The post-selection `TargetPlacement` check is dropped as redundant. The test now covers the home-named-only case and the mixed case. |
+| also | the one-box finding as a rehearsal blocker independent of part 2 | Ruling R2 rewritten: options (a) to (d), with the correction that stopping the agent alone does not clear the count because the edges file outlives the box; recommendation (a) now, ask region HA about (d), (b) only as its own task. |
+| also | A2, H1, H3 need executable witnesses | Every ask now names its witness tests; A2 gains `TestHomeRegionalAcknowledgesBothSpellings`. |