carlos / platform Public

provision: census overlays for self-serve moves plan 2, Task 3 (PROPOSED-self-serve-moves-2)

Record writes: active.json's table form is a new write shape:
Server.reserveBoxes and Server.releaseBoxes added as nonrecord (sel:Put),
Server.offlineTakeLock removed, Server.offlineApplyBound added as a
record-writer (key:SetRecordMoveAt). Root reads: no read, class or task
added and no unit re-pinned, so the seal stays; the internal/console and
internal/moves envelopes re-pinned in place. The note gains the Task 3
paragraph. Not an accepted ruling.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev d34150e6d229f68c7f8d74d4d80361e05e08d95c parent e42fdad
3 files changed, +26 −2
  • docs/superpowers/notes/census-proposal-self-serve-moves-2.md +2 −0
  • internal/provision/self_serve_moves2_census_test.go +21 −0
  • internal/provision/testdata/plan14-root-reads-self-serve-moves-2.tsv +3 −2
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 fbf681fca..81b68c520 100644
--- a/docs/superpowers/notes/census-proposal-self-serve-moves-2.md
+++ b/docs/superpowers/notes/census-proposal-self-serve-moves-2.md
@@ -7,3 +7,5 @@ The root-read overlay is `internal/provision/testdata/plan14-root-reads-self-ser
Task 1 (the move-capable token and the self-serve gate) changes no record envelope and adds no root read, class or task. Eight accepted-legacy units are re-pinned in place, each with `rev3=PROPOSED-self-serve-moves-2` and a reviewed one-line basis, and take new frozen fingerprints, so the seal moves. Five are console SQL running as `carlos-console`: `migrateCLIAuth` adds the `cli_tokens` column `moves INTEGER NOT NULL DEFAULT 0` through the idempotent ALTER list; `claimRawNewAppToken` writes it (true only from the owner-only mint door); `CLITokens` and `ServicePrincipals` list it; `CLIPrincipal` reads it (honoured for kind=app rows only) and returns the stored `token_hash` as `Principal.TokenID`. The tokens page template (`settings.html`) marks a move-capable app token and reads nothing of its own. `serve` reads `CARLOS_SELF_SERVE_MOVES` into `Config.SelfServeMoves`, an environment string, beside plan 1's workers. `Server.handleAPIInstanceMoveRequest` keeps its census-ruling-region-ha-2a body; with Phase 1 off it now sends self-serve mode to `selfServeMoveRequest` (the self-serve gate, binding SQL only, no bucket or record read; its Task 1 body answers the family's 404) and every other request to the legacy body. Its read sites are unchanged and their offsets moved. The cmd/console, internal/console and internal/console/templates envelopes are re-pinned. In the record-write inventory the same door stays a record-writer (`key:SetRecordMove,lit:instances`) and takes a new digest; no record write is added, moved or made raw. The cut and abort doors' dispatch is not a census unit. A green census checks these proposed rows against the code; Paul's ruling is still outstanding.
Task 2 (the queued state, the receipts, the caller's stamps, the bound write and the open record) adds no root read, class or task. In the root-read overlay one accepted-legacy unit is re-pinned in place under `rev3=PROPOSED-self-serve-moves-2` with a reviewed basis and takes a new frozen fingerprint, so the seal moves: `offlineBegin`, the shared body of `OfflineStart` and `OfflineStage`, now also creates the self-serve move in `offline-queued` for `OfflineQueue` (queued_at, staged_at and precheck_at at the caller's stamp, move.seq set to the record's move_seq, no advance) and records `return_of`. Its one site is still `rec.ActiveRun.Open()`, a method, and its offset moved. The other new transitions, `ProposeStamp`, `Receipt`, `SetRecordMoveAt` and `moves.OpenRecordKey` read no file. The internal/console envelope is re-pinned (Task 1's row replaced in place), and internal/moves and internal/provision are pinned in this overlay. The plan asks for `@fields` rows for the new record fields (`self_serve`, `participants`, `queued_at`, `precheck_at`, `return_of`, `last_move.request`); the census's field rule derives its list from the `internal/registry` structs only, so these provision record fields change no `@fields` row, and none is added. In the record-write inventory `SetRecordMoveAt` is added as a record-writer (`key:SetRecordMoveAt`): SetRecordMove bound to an observed incarnation and record_seq, one attempt through `updateRecordSeeding` and so `WriteRecord`. `setrecordmoveat` joins the census's exact key names, so a writer that moves from `SetRecordMove` to the bound write stays a typed writer in the census rather than leaving it. `updateRecordSeeding` is re-pinned (the expect parameter: nil from every other caller; with it, a read of another incarnation or record_seq is refused before the mutation and the retry loop is skipped), and `updateRecord` and `SetRecordSeed` are re-pinned for passing nil. `moves/offline.go:OpenRecordKey` is a new key-site: `control/offline-moves/<host>/<id>/open.json`, beside the seal and never an instance record; nothing writes it until Task 8. The offline transitions are mutations for the record writers and write nothing themselves. Paul's ruling is still outstanding.
+
+Task 3 (per-box reservations with a legacy projection and a sentinel, the fenced reclaim and the operator doors' bound admission) adds no root read, class or task, and re-pins no accepted-legacy unit, so the root-read seal does not move. The new code reads no file: `moves.ParseOfflineActive` and `moves.OfflineActiveBody` are pure, and the console's `offlinereserve.go` reads `control/offline-moves/active.json` from the bucket and the instance record only through the judged reader (`observeRecord`) and plan 1's `fenceRecord`. The internal/console and internal/moves envelopes are re-pinned in place (Task 2's rows replaced). In the record-write inventory, `active.json`'s table form is a new write shape: `Server.reserveBoxes` and `Server.releaseBoxes` are added as nonrecord units (`sel:Put`): their only writes are that control key, a conditional Put under the ETag they read (create-only when absent) and, for release, a DeleteIfMatch when the table empties. `Server.offlineTakeLock`, the one-move lock's writer, is removed. `Server.offlineApplyBound` is added as a record-writer (`key:SetRecordMoveAt`): the start and stage doors' one transition bound to the incarnation and record_seq of the judged record the gate read before reserving. The reclaim's only record write is `fenceRecord`'s (WriteRecord, plan 1), reached under the holder's app lock then hostname exclusion and only once `admit` passes. Paul's ruling is still outstanding.
diff --git a/internal/provision/self_serve_moves2_census_test.go b/internal/provision/self_serve_moves2_census_test.go
index 8d2234ba6..e53d0d814 100644
--- a/internal/provision/self_serve_moves2_census_test.go
+++ b/internal/provision/self_serve_moves2_census_test.go
@@ -39,12 +39,30 @@ package provision
// - The offline transitions (offline_move.go) are muts for SetRecordMove
// and SetRecordMoveAt and write nothing themselves, so they are not
// census units.
+//
+// Task 3 (per-box reservations, the sentinel, the reclaim and bound
+// operator admission): one record writer is added, one nonrecord unit
+// removed and two added; none is raw.
+// - apiinstancemove_offline.go:Server.offlineTakeLock is removed (the
+// one-move lock is now the reservation table).
+// - apiinstancemove_offline.go:Server.offlineApplyBound is added, a
+// record-writer (key:SetRecordMoveAt): the start and stage doors'
+// transition bound to the incarnation and record_seq of the judged
+// record the gate read before reserving, one attempt.
+// - offlinereserve.go:Server.reserveBoxes and Server.releaseBoxes are
+// added, nonrecord (sel:Put): their only writes are
+// control/offline-moves/active.json in its table form (a conditional
+// Put under the ETag read, create-only when absent; releaseBoxes also
+// DeleteIfMatch). The reclaim's record write is fenceRecord's (plan 1,
+// WriteRecord), reached under the holder's app lock and hostname
+// exclusion; its pre-fence read is observeRecord.
func plan14SelfServeMoves2WriteCensusInventory() []censusEntry {
removed := []struct{ file, unit string }{
{"internal/console/apiinstancemove.go", "Server.handleAPIInstanceMoveRequest"},
{"internal/provision/provision.go", "updateRecord"},
{"internal/provision/provision.go", "updateRecordSeeding"},
{"internal/provision/seed.go", "SetRecordSeed"},
+ {"internal/console/apiinstancemove_offline.go", "Server.offlineTakeLock"},
}
added := []censusEntry{
{"internal/console/apiinstancemove.go", "Server.handleAPIInstanceMoveRequest", "record-writer", "key:SetRecordMove,lit:instances", "6be4b2d5ed59e3e42c47589039daba2cdb4081db45e0258d8889ef8477290591"},
@@ -53,6 +71,9 @@ func plan14SelfServeMoves2WriteCensusInventory() []censusEntry {
{"internal/provision/seed.go", "SetRecordSeed", "record-writer", "key:SetRecordSeed", "b9b7e479f5217f26356e31e1568e1d1aa0f31c8d0f719633e7f2b15f25119d60"},
{"internal/provision/provision.go", "SetRecordMoveAt", "record-writer", "key:SetRecordMoveAt", "e818d6acd4429185c5fc11db4100a73b2d83409b9f6f32435e88aac7f9012f41"},
{"internal/moves/offline.go", "OpenRecordKey", "key-site", "key:OpenRecordKey", "ebb4f0dcd050b5d183b642829423ba71740bf6d86427af2cba64475d54276be9"},
+ {"internal/console/apiinstancemove_offline.go", "Server.offlineApplyBound", "record-writer", "key:SetRecordMoveAt", "039573bfd1f3ec621474e9c761f55ce301227d812f4dd6a7d206dad0c18b9980"},
+ {"internal/console/offlinereserve.go", "Server.reserveBoxes", "nonrecord", "sel:Put", "15d70cf3aacf38f7ebb1fc0a55a35e1b233dbb23d15c46c2487858e4af18229d"},
+ {"internal/console/offlinereserve.go", "Server.releaseBoxes", "nonrecord", "sel:Put", "0d7cad2d3a92890c10d7a538784713124d3659d89c872e75fbc107bf96794751"},
}
entries := plan14SelfServeMoves1WriteCensusInventory()
for _, r := range removed {
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 c82a4e4ca..5f83cbfa4 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
@@ -21,6 +21,7 @@ internal/console/templates/settings.html external legacy accepted-legacy accepte
# Task 2 (the queued state, the receipts, the caller's stamps, the bound write and the open record), rev3=PROPOSED-self-serve-moves-2: no root read, class or task is added. offlineBegin (the shared body of OfflineStart, OfflineStage and now OfflineQueue) is re-pinned in place with a reviewed basis and takes a new frozen fingerprint, which moves the seal; its one site moved. The other new transitions, ProposeStamp, Receipt, SetRecordMoveAt and moves.OpenRecordKey read no file. Envelopes re-pinned: internal/console (the operator doors propose their stamps, the queued state in the verb and the view, the abort door's OfflineAbortAt; Task 1's row replaced in place), internal/moves (OpenRecord, OpenRecordKey) and internal/provision (the record fields, the transitions, the bound write). The new record fields need no @fields row: the census's field rule covers internal/registry structs only.
@legacy 94b21acdfb6eb3f8ee0a3dd937605e18edd852766f1091d7d664e4bc566f4ad2 internal/provision/offline_move.go offlineBegin legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=root-input (J4, as OfflineStart); the console (cmd/console serve under console.service, user carlos-console): self-serve moves 1 Task 7's body (OfflineStart's, the state a parameter; a direct start advances move_seq and stamps move.seq, refusing ErrLifetimeExhausted at MaxInt64 before any write), and it now also creates the self-serve move in offline-queued for OfflineQueue (queued_at, staged_at and precheck_at at the caller's stamp, move.seq set to the record's move_seq, no advance) and records return_of; a stage and a queue hold nothing; its one site is still rec.ActiveRun.Open(), a method on the record's run state (RunState.Open), not a file read; self-serve moves 2 Task 2 file:Open@208 0a9b432ddf2f39a977c0651e377258b35c1d05635e9979583eeac6253d6c0ec8
internal/provision/offline_move.go offlineBegin legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=root-input (J4, as OfflineStart); the console (cmd/console serve under console.service, user carlos-console): self-serve moves 1 Task 7's body (OfflineStart's, the state a parameter; a direct start advances move_seq and stamps move.seq, refusing ErrLifetimeExhausted at MaxInt64 before any write), and it now also creates the self-serve move in offline-queued for OfflineQueue (queued_at, staged_at and precheck_at at the caller's stamp, move.seq set to the record's move_seq, no advance) and records return_of; a stage and a queue hold nothing; its one site is still rec.ActiveRun.Open(), a method on the record's run state (RunState.Open), not a file read; self-serve moves 2 Task 2 file:Open@208 0a9b432ddf2f39a977c0651e377258b35c1d05635e9979583eeac6253d6c0ec8
-@coverage internal/console 29ae289dc256408f6debca9150987ba5ce7a2e80a0bbf5fc3064ead950d16bd1
-@coverage internal/moves 51465341e311dc007d75ed4f68474fe4cec05d51c6ec07ee5c64653936faa410
@coverage internal/provision 177bbc7f94d9ba7e4d999f832920e8552a0af4315d7ce2445572efb21fada342
+# Task 3 (per-box reservations with a legacy projection and a sentinel, the fenced reclaim and the operator doors' bound admission), rev3=PROPOSED-self-serve-moves-2: no root read, class or task is added and no accepted-legacy unit is re-pinned, so the seal does not move. The new units (moves.ParseOfflineActive, moves.OfflineActiveBody, the console's offlinereserve.go, the doors' offlineApplyBound and offlineAdvanceBound) read no file: their reads are the bucket's control/offline-moves/active.json and the instance record through the judged readers and fenceRecord. Envelopes re-pinned in place (Task 2's rows replaced): internal/console (offlinereserve.go, the start and stage doors, the Server's two evidence fields) and internal/moves (the reservation table in offline.go).
+@coverage internal/console 7e63ab646b55ccc0d48592ca79d8c214165271439b8bc6336aacca6b49ae5059
+@coverage internal/moves 7fcb4834a033d8cea99621143ba2afcbf14568c9467f08329af7c692d0c1359b