carlos / platform Public

Region HA plan 1b-next: Astra round 3 findings (10 resolved, 3 partly, 1 not, 6 new; not ready), unedited

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 15d94dfa9b13e03f51ec9048862835a2ceb936c7 parent d93c94d
1 file changed, +50 −0
  • docs/superpowers/plans/CODEX-PLAN-REGION-HA-1BN-R3.md +50 −0
diff --git a/docs/superpowers/plans/CODEX-PLAN-REGION-HA-1BN-R3.md b/docs/superpowers/plans/CODEX-PLAN-REGION-HA-1BN-R3.md
new file mode 100644
index 000000000..17f8aced8
--- /dev/null
+++ b/docs/superpowers/plans/CODEX-PLAN-REGION-HA-1BN-R3.md
@@ -0,0 +1,50 @@
+Reviewed `d93c94d0`. **P** below refers to the [implementation plan](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/region-ha-1b-next/docs/superpowers/plans/2026-10-07-region-ha-1b-next-relocation.md). **6b** refers to the declared plan on `origin/design-replica-ownership`.
+
+All 11 embedded patches pass `git apply --check`. Compilation and tests were not reproduced in this read-only sandbox.
+
+| Prior finding | Disposition | Assessment against rev 3 |
+|---|---|---|
+| R1-2 — build authority | Resolved | Each builder write now checks claim and installation identity; lease loss cancels the build. |
+| R1-4 — former writer’s loss evidence | Resolved | Retained evidence includes the former writer’s last admission and its consequences. |
+| R1-5 — immutable files pin | **Partly** | Copying uses retained bytes, but `PreparedFromInstallManifest.Verify` still reads the mutable source manifest at **P:7825–7834**. A late PUT still prevents progress. Verify against the retained control-bucket copy too. |
+| R1-9 — release replay after restart | **Partly** | **P:7103** restores the old controller identity without setting `Recover`. 6b’s stop coordinator rejects that identity mismatch before replay (**6b:11975–11976**). Use its authenticated recovery path and preserve the recorded owner’s directory revision. |
+| R1-13 — later empty probe | Resolved | Later attempts handle `genselect.ErrEmpty` and use ordinary empty-generation authority. |
+| R1-14 — row-case adapter test | Resolved | The incorrect deletion/status expectations are corrected. The replacement test has a separate compilation defect below. |
+| R1-16 — exhaustion before selection | Resolved | Both paths check exhaustion before choosing a spare. A different exhaustion boundary fails below. |
+| R2-N1 — prepared retry ordering | Resolved | Inherited flags are reset; ordinary retries leave `Prepared` false. |
+| R2-N2 — forbidden deletion expectation | Resolved | Existing unexplained rows are retained; assertions use `hold_op`. |
+| R2-N3 — descriptor privileges | Resolved as a gated contract | Agent capture is process-local; R3 explicitly requires root’s complete scan. |
+| R2-N4 — completed target remains held | **Not resolved** | The clear at **P:8537–8542** is inside the write callback. Before reaching it, serving `Policy.Check` rejects any nonempty hold ([wakepolicy.go:63](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/region-ha-1b-next/internal/hostbox/wakepolicy.go:63)). Provide narrowly scoped authority to clear an obsolete hold before that rejection. |
+| R2-N5 — retained controller lifetime | **Partly** | Sweeping is added, but a timed-out close loses the reference needed to finish cleanup; see new finding 2. |
+| R2-N6 — three-attempt limit | Resolved | The claim durably counts attempts before invoking R1’s single-attempt probe contract. |
+| R2-N7 — discovery completion | Resolved | Qualified discharge now uses `CompleteObligation` semantics. |
+
+New findings, ranked:
+
+1. **P1 — Task 9, Steps 1 and 4: identifiers are missing from the declared upstream.**
+ **P:8529** uses `observed.Claim` at the insertion after `write(owner)`. In 6b, `observed` is declared only inside the earlier `sql.ErrNoRows` block (**6b:7891–7902**), so it is undefined at that insertion. **P:8373** also calls `adapterFixture`, attributed to a 6b fixture that the declared plan does not supply.
+ **Fix:** read and validate the claim under captured authority at the insertion point, and include the actual test fixture and its methods.
+
+2. **P2 — Task 7, Step 4: `Finish` forgets a controller before closing it successfully.**
+ **P:6933–6942** removes the map entry before calling `Close(ctx)`. Main explicitly retains the lifetime lock when closing times out and requires another `Close` call ([controller.go:478](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/region-ha-1b-next/internal/hostexec/controller.go:478)). If the pass expires during cleanup, later sweeps cannot find that controller; another relocation cannot acquire the host’s lock.
+ **Fix:** retain the entry until closing completes, then conditionally remove that same controller. Test cancellation followed by a successful later sweep and another relocation.
+
+3. **P2 — Tasks 3/9/10: production construction never supplies the new policy and fence observer.**
+ `Policy.Relocation` is mandatory for relocation actions (**P:3221–3238**), but its assignments are confined to tests. 6b constructs the policy with only `Inputs`, `Moves` and `Held` (**6b:10887**), then captures `a.Policy.Check` (**6b:10520**). Task 10 supplies neither the relocation policy nor `Reconciler.FenceClosed`, whose default is false (**P:8499**). Executor reconciliation and execution therefore remain refused, and source-fence closure is invisible to row decisions.
+ **Fix:** define and wire both providers before assembly construction captures the policy, gated on the feature flags. Test production assembly with HA enabled and disabled.
+
+4. **P2 — Task 9, Step 3: a fenced source cannot write its required hold while it remains executor.**
+ **P:8485** chooses `holdOnly` only when `op.exec != box`. During `releasing`, the source remains executor after its admission fence closes. It therefore takes the guarded installation path, where the active fence or receipt prevents the callback ([guard.go:144](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/region-ha-1b-next/internal/hostguard/guard.go:144)). This contradicts the `WithLocal` behavior promised by **P:8363**.
+ **Fix:** use the conditional hold-only path for a source whose admission fence is closed, even while it remains executor. Test with a real durable fence.
+
+5. **P2 — Tasks 1/5, Step 3: exhaustion cannot stop a terminated target after local installation.**
+ After two replacements, let the final target journal `local_install` in `installed`, then terminate before probing. **P:5197–5198** calls `MarkStuck`, but **P:1968–1969** refuses precisely that state. Every pass repeats the refusal; the dead target cannot probe, and its fence is never consumed.
+ **Fix:** define a fence-consuming terminal transition that retains loss evidence, revokes the dead installation’s authority and reaches a safe stuck state without selecting a third replacement. Extend the exhaustion test beyond `starting`.
+
+6. **P2 — Task 1, Step 3 / Task 5, Step 3: replacement acknowledgement is skipped when leaving `releasing`.**
+ If the target terminates after the source journals its release, replacement adds a new participant while retaining `releasing` (**P:1891–1892, 1935–1936**). The next worker pass calls `PinAfterRelease`, which advances to `pinning` without checking acknowledgements (**P:1543–1556**). Spec §5.8 requires the replacement to acknowledge before leaving that phase.
+ **Fix:** require the acknowledgement barrier in the `releasing → pinning` CAS. Test a replacement with its acknowledgement withheld after source release publication.
+
+I accept the author’s remaining rulings as scoped choices. Rulings **5/21, 9, 17 and 20** remain incompletely implemented for the reasons above. R3’s privilege split and R5’s retained consequences address the earlier requests; the missing production policy wiring needs an explicit implementation or upstream request.
+
+No files changed. **Verdict: not ready.**
\ No newline at end of file