carlos / platform Public

provision: census for the F6 host half

Re-pins movesWorker.writeReturnReport (record-write inventory; it answers
only a request naming the box's BoxID, its Put unchanged) and the
internal/host root-read envelope for a2abea21. The box's answerReturn,
which deleted the return index entry, is gone: the inventory comment and
the census note no longer say the box deletes it. No root read, class or
task changes; the seal stands.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 52b940eef8126268c18cb50880e46983b422befb parent a2abea2
3 files changed, +12 −8
  • docs/superpowers/notes/census-proposal-self-serve-moves-2.md +2 −2
  • internal/provision/self_serve_moves2_census_test.go +8 −4
  • internal/provision/testdata/plan14-root-reads-self-serve-moves-2.tsv +2 −2

CI failed — run details

CI log
=== gate ===
.amadan/test-gate.sh
gate: 2026-10-24T00:11:07Z on aarch64, 32 cpus, 61366 MB available, go1.26.8 linux/arm64
gate: +0s start launchctx
gate: +0s start vet
gate: +0s start gofmt
gate: +1s start rest
gate: +20s start internal_console@0
gate: +20s start internal_console@1
gate: +20s start internal_console@2
gate: +20s start internal_console@3
gate: +20s start internal_console@4
gate: +20s start internal_console@5
gate: +20s start internal_console@6
gate: +20s start internal_console@7
gate: +22s start root@0
gate: +22s start root@1
gate: +22s start root@2

amadan: step gate failed: signal: terminated

amadan: job failed at step gate — its output is above, not at the tail of this log
diff --git a/docs/superpowers/notes/census-proposal-self-serve-moves-2.md b/docs/superpowers/notes/census-proposal-self-serve-moves-2.md
index 85df8a005..de44d2883 100644
--- a/docs/superpowers/notes/census-proposal-self-serve-moves-2.md
+++ b/docs/superpowers/notes/census-proposal-self-serve-moves-2.md
@@ -38,7 +38,7 @@ Task 8 (the cut, the abort, the finaliser, paging and the operator return) adds
Also on Task 8's branch: `targetDataPaths` names the raw nested directory through `internal/layout`'s `RawInstanceDir` (the same path; `TestRawInstancesDirectoryIsNamedOnlyHere` failed on CI at `c9822492`), re-pinned in place with its frozen declaration, which moved the seal once before Task 8's own rows.
-Task 8 fix round 1 adds no root read, class or task and no record write. The operator return's discovery is now the return index: the return door writes `control/offline-moves/@returns/<region>/<host>/<forward id>.json` (`offlineReturnIndex`, nonrecord, create-only, moved to the newest request if-match), and a box lists only its own region's index once a minute (one LIST per region per box, whatever the number of hosts), reads an indexed forward's open record once (`readOpen`, a key-site; a record it may not answer for cached as a negative), writes its report (`writeReturnReport`, nonrecord, renamed from `answerReturn`'s write) and deletes the index entry if-match; `scanReturns`'s listing of every host's move prefixes is gone. `FinishSelfServeMove`'s statement moved to `finishSelfServeMove` (shared with `YieldSelfServeMove`, the move an operator took over), so its row is removed under a proposed H3 disposition and `finishSelfServeMove` is frozen accepted-legacy; `probeTarget` is re-pinned in place (one transport per target address); the console, host and moves envelopes are re-pinned in place and the seal moves to `7351b869…`. Open records of aborted moves (written before an open that never committed) are left in the bucket: their cleanup is plan 4's.
+Task 8 fix round 1 adds no root read, class or task and no record write. The operator return's discovery is now the return index: the return door writes `control/offline-moves/@returns/<region>/<host>/<forward id>.json` (`offlineReturnIndex`, nonrecord, create-only, moved to the newest request if-match), and a box lists only its own region's index once a minute (one LIST per region per box, whatever the number of hosts), reads an indexed forward's open record once (`readOpen`, a key-site; a record it may not answer for cached as a negative), writes its report (`writeReturnReport`, nonrecord, renamed from `answerReturn`'s write) and deleted the index entry if-match (no longer: since the final review's F6 the box only reads the index and the console deletes its entries, below); `scanReturns`'s listing of every host's move prefixes is gone. `FinishSelfServeMove`'s statement moved to `finishSelfServeMove` (shared with `YieldSelfServeMove`, the move an operator took over), so its row is removed under a proposed H3 disposition and `finishSelfServeMove` is frozen accepted-legacy; `probeTarget` is re-pinned in place (one transport per target address); the console, host and moves envelopes are re-pinned in place and the seal moves to `7351b869…`. Open records of aborted moves (written before an open that never committed) are left in the bucket: their cleanup is plan 4's.
Task 9 (the estimate, the timings and the events) adds no root read, class or task, and no record write. In the root-read overlay twelve accepted-legacy units are added under `rev3=PROPOSED-self-serve-moves-2`, each frozen with a one-line basis, and none reads a file: the timing marks and `move_timings` (`ssMarkTx`, `Store.MarkSelfServeTiming`, `Store.SelfServeTimings`, `Store.InsertMoveTiming`, `Store.MoveTimings`), the estimate's medians (`Server.estimateRules`, whose sites are the `Open` term, a struct field), the events (`ssRowEventTx`, `Store.SelfServeEvents` and the dead-letter `Store.selfServeEventDeliveryFailed`), `Store.RecordSelfServeHealth` and `Store.SetSelfServeReason` (now transactions with their events, where they used `setSelfServe` before) and the operator status door `Server.handleAPIInstanceOfflineStatus` (its site is the request URL's `Query`, the `?log=1` switch). `Server.estimate`, `Store.AbortSelfServe`, `Store.AdvanceSelfServeToStep`, `Store.ConsumeSelfServeRequest` and `Store.migrateSelfServe` are re-pinned in place with their bases extended (the medians; the events and marks in the change's own transaction; the events' `attempts` column, added idempotently). The internal/console envelope is re-pinned in place, and the seal moves to `16987f09…`. In the record-write inventory nothing changes: the marks, `move_timings`, the events and their delivery are console SQLite only, and the one new bucket access is a read (the advance into landed reads the move's reports through `readOfflineEvidence`, a key-site, for the seal's sizes). `claimWriterReasons` already names `self_serve_events` and `self_serve_timings` as the claims-off pass's writers; `move_timings` is `NonApp` in the store gate and its one writer, the finaliser's timings effect, runs only in that pass, so `TestClaimConsoleMutationInvariant` reaches no new writer.
@@ -50,4 +50,4 @@ Task 10b (the fourteen named end-to-end witnesses) adds no root read, class or t
Task 10b fix round 1 changes how the end-to-end bridge arms a box's instance id. Each box now arms it as `armMovesPass` arms the agent's: a per-process cache of the metadata answer, shaped like `cachedInstanceID`, read once at arm and then lazily through `throttledInstanceID`. `E2E.status` publishes the cached id in the status block, as the agent's status tick does. In the root-read overlay `E2E.status` is re-pinned in place (tests only, no new read site) and the internal/host envelope is re-pinned in place. The seal moves to `9193bc80…`. The record-write inventory and the writers list are unchanged.
-The final review's fix round (console half) adds no root read, class or task and no record write, and adds one bucket writer. In the root-read overlay `Store.issueSelfServeRequest` (it also stores a queue request's staging coordinates, F10), `Store.migrateSelfServe` (three additive columns, `announce_seq`, `request_stage_bucket` and `request_stage_region`, F9 and F10) and `Store.selfServeRequest` (the coordinates read back) are re-pinned in place with their bases extended. `ConfirmSelfServeRequest`'s statement moved to `ConfirmSelfServeRequestLanded`, which also records the landed record_seq as owed its serial bump (F9), frozen accepted-legacy; `ConfirmSelfServeRequest` stays declared with no direct read, so its row is removed under a proposed H3 disposition. The internal/console, internal/moves and internal/alerting (`alerting.Armed`, F13) envelopes are re-pinned, and the internal/host envelope for the host implementer's commits (036528e6..e43e415e: F5 host half, F7, F17's clock comment). The seal moves to `1b87d203…`. In the record-write inventory five units are re-pinned in place with their writes unchanged: `Server.selfServeRun` (the owed announcement), `Server.reserveBoxes` (two lost races with no holder observed answer unreadable, F14), `Server.offlineOpenRecord` (a guard immediately before its create-only Put, the job's lease-bound context, F11), `Server.handleAPIInstanceOfflineReturn` (the request carries `target_box_id`, F6) and the host's `movesWorker.writeReturnReport` (an unreadable request is the scan's error, F5). The new writer, PROPOSED: `Server.deleteReturnIndexEntry` in `offlinereturn.go` deletes a return index entry (`control/offline-moves/@returns/<region>/<host>/<forward id>.json`) with `DeleteIfMatch` on the ETag it read, called when a return is staged or started (`offlineReturnIndexDone`) and by the pass's sweep of entries whose request is past its deadline (`selfServeReturnSweep`, once per tick under the lease, at most 32 entries). It never touches a record. The record-write census scans the Put family only (`censusMethods`), so the deleter has no inventory row; it is recorded here and in the inventory's comment. The writers list is otherwise unchanged.
+The final review's fix round (console half) adds no root read, class or task and no record write, and adds one bucket writer. In the root-read overlay `Store.issueSelfServeRequest` (it also stores a queue request's staging coordinates, F10), `Store.migrateSelfServe` (three additive columns, `announce_seq`, `request_stage_bucket` and `request_stage_region`, F9 and F10) and `Store.selfServeRequest` (the coordinates read back) are re-pinned in place with their bases extended. `ConfirmSelfServeRequest`'s statement moved to `ConfirmSelfServeRequestLanded`, which also records the landed record_seq as owed its serial bump (F9), frozen accepted-legacy; `ConfirmSelfServeRequest` stays declared with no direct read, so its row is removed under a proposed H3 disposition. The internal/console, internal/moves and internal/alerting (`alerting.Armed`, F13) envelopes are re-pinned, and the internal/host envelope for the host implementer's commits (036528e6..e43e415e: F5 host half, F7, F17's clock comment; then a2abea21: F6 host half). The seal moves to `1b87d203…`. In the record-write inventory five units are re-pinned in place with their writes unchanged: `Server.selfServeRun` (the owed announcement), `Server.reserveBoxes` (two lost races with no holder observed answer unreadable, F14), `Server.offlineOpenRecord` (a guard immediately before its create-only Put, the job's lease-bound context, F11), `Server.handleAPIInstanceOfflineReturn` (the request carries `target_box_id`, F6) and the host's `movesWorker.writeReturnReport` (an unreadable request is the scan's error, F5; it answers only a request whose `target_box_id` is the box's own BoxID, F6). The host's `answerReturn`, which deleted the index entry with `DeleteIfMatch` after the report, is removed (F6): no box deletes an index entry. The new writer, PROPOSED: `Server.deleteReturnIndexEntry` in `offlinereturn.go` deletes a return index entry (`control/offline-moves/@returns/<region>/<host>/<forward id>.json`) with `DeleteIfMatch` on the ETag it read, called when a return is staged or started (`offlineReturnIndexDone`) and by the pass's sweep of entries whose request is past its deadline (`selfServeReturnSweep`, once per tick under the lease, at most 32 entries). It never touches a record. The record-write census scans the Put family only (`censusMethods`), so the deleter has no inventory row; it is recorded here and in the inventory's comment. The writers list is otherwise unchanged.
diff --git a/internal/provision/self_serve_moves2_census_test.go b/internal/provision/self_serve_moves2_census_test.go
index 50e9e3537..4a12847a6 100644
--- a/internal/provision/self_serve_moves2_census_test.go
+++ b/internal/provision/self_serve_moves2_census_test.go
@@ -152,8 +152,9 @@ package provision
// - offlinereturn.go:Server.offlineReturnIndex is added, nonrecord
// (sel:Put; fix round 1): the return index entry,
// control/offline-moves/@returns/<region>/<host>/<forward id>.json,
-// create-only, moved to the newest request key if-match; the box
-// deletes it if-match once it has answered (release.DeleteIfMatch).
+// create-only, moved to the newest request key if-match. (Fix round 1
+// had the box delete it once answered; since the final review's F6 the
+// box only reads it and the console deletes it, below.)
// - movesoffline_return.go:movesWorker.writeReturnReport is added,
// nonrecord (sel:Put): the box's return-ready report,
// control/offline-moves/<host>/<forward id>/reports/return-ready-<box
@@ -190,7 +191,10 @@ package provision
// selects (F6); its key and create-only Put are unchanged.
// - movesoffline_return.go:movesWorker.writeReturnReport is re-pinned for
// the host implementer (F5 host half): a request it cannot read is the
-// scan's error; its Put is unchanged.
+// scan's error; and again (F6 host half): it answers only a request
+// whose target_box_id is the box's own BoxID. Its Put is unchanged. The
+// box's answerReturn, which deleted the index entry (DeleteIfMatch,
+// outside the census's Put family), is removed: no box deletes it.
// - offlinereturn.go:Server.deleteReturnIndexEntry is a new bucket writer,
// PROPOSED: the console deletes the return index entry
// (control/offline-moves/@returns/<region>/<host>/<forward id>.json),
@@ -250,7 +254,7 @@ func plan14SelfServeMoves2WriteCensusInventory() []censusEntry {
{"internal/console/offlinereturn.go", "Server.handleAPIInstanceOfflineReturn", "nonrecord", "sel:Put", "371ac237cfeaa2df7daa9c9c5d12ed3be3a5c81092d627f66998073824811446"},
{"internal/console/offlinereturn.go", "Server.offlineReturnIndex", "nonrecord", "sel:Put", "38f5e6d1f0781f11491d7b116a43dd8fb42d5fb3e60c66419513ec1cfb646792"},
{"internal/console/offlinereturn.go", "Server.readOpenRecord", "key-site", "key:OpenRecordKey", "809cf07d965d8bd136a1d463e5df3c435c086598359432630fa9d5fea6cd8950"},
- {"internal/host/movesoffline_return.go", "movesWorker.writeReturnReport", "nonrecord", "sel:Put", "c69060efd9642f9841b67bbb5ca7ebd63cf8dbe7fa104bdeed906b1f00377701"},
+ {"internal/host/movesoffline_return.go", "movesWorker.writeReturnReport", "nonrecord", "sel:Put", "f35746ab1716c0b1ff303d6d763ff016ab4524dfb312490c94534d8fabdf8c70"},
{"internal/host/movesoffline_return.go", "movesWorker.readOpen", "key-site", "key:OpenRecordKey", "712d3bd4ab0850b208e9feb61c9c76ccd8eba9878711982f640b0d78910cdb47"},
{"internal/console/authcli.go", "Server.registerCLIRoutes", "key-site", "lit:instances,lit:sidecars", "0af692569c69daef180738395057594bd8413fdf6d9e1c4cc91f0a924ab3ef7b"},
}
diff --git a/internal/provision/testdata/plan14-root-reads-self-serve-moves-2.tsv b/internal/provision/testdata/plan14-root-reads-self-serve-moves-2.tsv
index 5b690949e..f42c3f609 100644
--- a/internal/provision/testdata/plan14-root-reads-self-serve-moves-2.tsv
+++ b/internal/provision/testdata/plan14-root-reads-self-serve-moves-2.tsv
@@ -122,7 +122,7 @@ internal/host/movespass.go armOffline legacy accepted-legacy accepted-legacy;rev
internal/host/probes.go stagePathActive legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=root-input (J4, as configPathActive): systemctl is-active carlos-stage.path for the precheck's and the ready report's stage-path fact; self-serve moves 2 Task 6 file:Command@42 f7cbe0e2ba971d201f8fc7111eb4cebac9c9bae14cc826366c06b9e8bd3eecb8
@legacy 6febdffd5ed3a46ca7c8296e2bf12d8249561e26ab6479eb672f40a96b13f0f4 internal/status/snapshot.go CollectBox legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=root-route; self-serve moves 2 Task 6: carries Probes.BoxID, Lanes and Hosts into the private BoxMetrics only (never the public feed); read sites unchanged, offsets moved registry:Alias@4465,registry:CertExpiresAt@3160,registry:CertExpiresAt@3396,registry:CertExpiresAt@4580,registry:DBPath@2495,registry:DBPath@2553,registry:DNSCheckedAt@3365,registry:DNSCheckedAt@4549,registry:DNSStatus@3139,registry:DNSStatus@3334,registry:DNSStatus@4519,registry:Hibernate@3853,registry:Host@2295,registry:Host@2437,registry:Host@2666,registry:Host@3280,registry:Host@3315,registry:Host@4500,registry:Kind@2473,registry:Kind@3807,registry:Platform@2152,registry:ServingVersion@2324,registry:ServingVersion@2447,registry:Socket@3988,registry:Version@2186,registry:Version@2305,whole:Route@1581,whole:RouteAlias@4362 e874e3166d22d437b4d4daf2b1dc8b2d56bc853076cc132b9b2c1d30f9a2b1be
internal/status/snapshot.go CollectBox legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=root-route; self-serve moves 2 Task 6: carries Probes.BoxID, Lanes and Hosts into the private BoxMetrics only (never the public feed); read sites unchanged, offsets moved registry:Alias@4465,registry:CertExpiresAt@3160,registry:CertExpiresAt@3396,registry:CertExpiresAt@4580,registry:DBPath@2495,registry:DBPath@2553,registry:DNSCheckedAt@3365,registry:DNSCheckedAt@4549,registry:DNSStatus@3139,registry:DNSStatus@3334,registry:DNSStatus@4519,registry:Hibernate@3853,registry:Host@2295,registry:Host@2437,registry:Host@2666,registry:Host@3280,registry:Host@3315,registry:Host@4500,registry:Kind@2473,registry:Kind@3807,registry:Platform@2152,registry:ServingVersion@2324,registry:ServingVersion@2447,registry:Socket@3988,registry:Version@2186,registry:Version@2305,whole:Route@1581,whole:RouteAlias@4362 e874e3166d22d437b4d4daf2b1dc8b2d56bc853076cc132b9b2c1d30f9a2b1be
-@coverage internal/host 9deb3a7abf24f654c3aa660c41ad60ad0eea23cf002e11663e7d7b33f59277bb
+@coverage internal/host 6042287c1ad54c9be41f60df0505ba60494372c6c600ad5f8688e6f6de10b1d2
@coverage internal/lease 7e2a4a3652f964aaf1ee32356d2538a292c66ee0aaf9e622cf087b644aeaaa61
@coverage internal/status 6bf65bf3b04fdc150e2ec35d2d0d67046cf37b7c60a9dcde1c2204b75bcf8d2b
# Task 6 fix round 1 (the import cycle internal/park's tests saw), rev3=PROPOSED-self-serve-moves-2: PlacementMatches moves into internal/regionname (park.PlacementMatches delegates to it, unchanged in behaviour) so internal/moves no longer imports internal/park; no root read is added or moved. Envelopes: internal/moves in place, internal/park and internal/regionname added.
@@ -234,7 +234,7 @@ internal/console/selfserve_estimate.go Store.moveTimings legacy accepted-legacy
# Task 9 fix round 2, rev3=PROPOSED-self-serve-moves-2: Server.estimateRules (a rated row inside its fixed part, or within a tick of it, carries no rate and is left out) and Store.moveTimings (the floor bound) are re-pinned in place; the internal/console envelope is re-pinned in place.
# Task 10b (the end-to-end witnesses), rev3=PROPOSED-self-serve-moves-2: no root read, class or task is added and no unit changes. Two test seams are added: console.TestHooks.LifetimesOn (internal/console/testhooks.go: it reads the cutover marker through readCutoverMarker, the watcher's own reader, and activates the gate; called only by internal/e2e/selfserve under the e2e build tag) and host.BoxHandle.OptDir (internal/host/e2e_bridge.go: the test box's opt directory, a field). The internal/console and internal/host envelopes are re-pinned in place; the seal stands.
# Task 10b fix round 1, rev3=PROPOSED-self-serve-moves-2: the end-to-end bridge arms each box's instance id as armMovesPass arms the agent's (a per-process cache of the metadata answer, cachedInstanceID's shape, read at arm and lazily through throttledInstanceID) and E2E.status publishes it in the status block as the agent's status tick does, so E2E.status is re-pinned in place (tests only, no new read site); the internal/host envelope is re-pinned in place; the seal moves.
-# Final review fix round (console), rev3=PROPOSED-self-serve-moves-2: no root read, class or task is added. Store.issueSelfServeRequest (the queue's staging coordinates, F10), Store.migrateSelfServe (three additive columns, F9 and F10) and Store.selfServeRequest (the coordinates read back, F10) are re-pinned in place, sites unchanged in kind, offsets moved. ConfirmSelfServeRequest's statement moves to ConfirmSelfServeRequestLanded (it also records announce_seq, F9), frozen accepted-legacy below; ConfirmSelfServeRequest stays declared with no direct read, its row removed under a proposed H3 disposition. The internal/console, internal/moves and internal/alerting (alerting.Armed, F13) envelopes are re-pinned, and the internal/host envelope for the host implementer's F5, F7 and F17 commits (036528e6..e43e415e). The seal moves.
+# Final review fix round (console), rev3=PROPOSED-self-serve-moves-2: no root read, class or task is added. Store.issueSelfServeRequest (the queue's staging coordinates, F10), Store.migrateSelfServe (three additive columns, F9 and F10) and Store.selfServeRequest (the coordinates read back, F10) are re-pinned in place, sites unchanged in kind, offsets moved. ConfirmSelfServeRequest's statement moves to ConfirmSelfServeRequestLanded (it also records announce_seq, F9), frozen accepted-legacy below; ConfirmSelfServeRequest stays declared with no direct read, its row removed under a proposed H3 disposition. The internal/console, internal/moves and internal/alerting (alerting.Armed, F13) envelopes are re-pinned, and the internal/host envelope for the host implementer's F5, F7 and F17 commits (036528e6..e43e415e), and again for F6's host half (a2abea21: the box answers only a request naming its BoxID and no longer deletes the return index entry; no root read changes). The seal moves.
@legacy b4f1c3fb74e2c6b71833ae3a447fcd9b143432e38cef65c077d6f9f505e3db37 internal/console/selfserve_store.go Store.ConfirmSelfServeRequestLanded legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: ConfirmSelfServeRequest's UPDATE, which also records the landed record_seq as owed its serial bump (announce_seq = MAX(announce_seq, ?), final review F9), predicated on its nonce and unconsumed, unvoided, unconfirmed flags; no bucket or root action file:Exec@326 e7523e0d723468ac13dbbf021dcd54cc58fa10c57e2908d3c4979ba8a95216b4
internal/console/selfserve_store.go Store.ConfirmSelfServeRequestLanded legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: ConfirmSelfServeRequest's UPDATE, which also records the landed record_seq as owed its serial bump (announce_seq = MAX(announce_seq, ?), final review F9), predicated on its nonce and unconsumed, unvoided, unconfirmed flags; no bucket or root action file:Exec@326 e7523e0d723468ac13dbbf021dcd54cc58fa10c57e2908d3c4979ba8a95216b4
@removed internal/console/selfserve_store.go Store.ConfirmSelfServeRequest H3 census-ruling-PROPOSED-self-serve-moves-2;rev3=PROPOSED-self-serve-moves-2;basis=remains declared but has no direct read; its statement moved to ConfirmSelfServeRequestLanded, classified above, which ConfirmSelfServeRequest calls with no landed sequence (its UPDATE as before) and the job calls with the landed record_seq; proposed removal only