carlos / platform Public

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

The journal's twenty-two console SQL units and the lease's console name
frozen accepted-legacy; serve, NewServer and Store.migrate re-pinned; the
seal moves. In the record-write inventory the console lease
(leaseToken.put, control/offline-moves/console.json) is a new nonrecord
write shape, WriteRecord is re-pinned for the write guard and NewServer
stays a key-site. Envelopes re-pinned in place.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev dc6dcefcdfd019678ea7385fee7b44c287f9ae27 parent 0d237b3
4 files changed, +82 −7
  • docs/superpowers/notes/census-proposal-self-serve-moves-2.md +2 −0
  • internal/provision/plan14_root_reads_test.go +4 −2
  • internal/provision/self_serve_moves2_census_test.go +20 −0
  • internal/provision/testdata/plan14-root-reads-self-serve-moves-2.tsv +56 −5
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 9a6b3b80a..241ad885b 100644
--- a/docs/superpowers/notes/census-proposal-self-serve-moves-2.md
+++ b/docs/superpowers/notes/census-proposal-self-serve-moves-2.md
@@ -9,3 +9,5 @@ Task 1 (the move-capable token and the self-serve gate) changes no record envelo
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. Task 3's fix round 1 re-pins `Server.reserveBoxes` (the reclaim answers held, unreadable or reclaimed, so an unreadable holder keeps today's 502; two lost races answer busy) and the internal/console envelope; no write or read site changes.
+
+Task 4 (the journal, the events table, the console lease and the pass skeleton) adds no root read, class or task. The console SQLite journal in `selfserve_store.go` adds twenty-two units (the schema migration, admission, the row readers, the request lifecycle, the advance, the expiry and cut writes, abort, the row setters, the finaliser, the reclaim rule's two row reads, the event insert, the undelivered-event list and the transactional audit insert), each frozen accepted-legacy under `rev3=PROPOSED-self-serve-moves-2` with a reviewed basis: console SQL as `carlos-console`, no bucket or root action. `Server.selfServeConsoleID` reads `crypto/rand` once per process for the console name the lease carries, as `mintLifetime` does. `serve` is re-pinned in place (the paging arm takes the store and reads the journal and `active.json`; the self-serve moves pass is started as a recorded worker), and `NewServer` (the store as the reclaim rule's row evidence, the recovery-mode scan) and `Store.migrate` (`migrateSelfServe`) are re-pinned. The seal moves. The cmd/console, internal/console and internal/provision envelopes are re-pinned in place. In the record-write inventory the console lease is a new write shape: `leaseToken.put` is added as a nonrecord unit (`sel:Put`), whose only write is `control/offline-moves/console.json`, a conditional Put under the ETag read (create-only when absent). `WriteRecord`, the record primitive, is re-pinned for the write guard (`WithWriteGuard`: a check immediately before the Put that can only refuse it), and `NewServer` stays a key-site and is re-pinned. No record write is added in this task; the pass's record writes arrive with Task 7. Paul's ruling is still outstanding.
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go
index 519318a56..dae8ba1e5 100644
--- a/internal/provision/plan14_root_reads_test.go
+++ b/internal/provision/plan14_root_reads_test.go
@@ -1308,8 +1308,10 @@ func TestPlan14RootReadCensus(t *testing.T) {
// which moves the seal. Task 2 (the queued state, the receipts, the
// caller's stamps, the bound write and the open record) re-pins
// offlineBegin, which now also creates the queued move, and moves it
- // again.
- seal = "aaa55ce2c1f834171f5d1ee2be96b00445acbd08dfe570bd941f50355a2ac20a"
+ // again. Task 4 (the journal, the lease and the pass skeleton) adds the
+ // self-serve journal's store units and the lease's console name, and
+ // re-pins serve, NewServer and Store.migrate, which moves it once more.
+ seal = "1ab22492b6cfafd533e6f1911769d451ae0453278edfa3b319201cd2e213c28e"
errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...)
errs = append(errs, checkRootReadDeclarations(got, inv)...)
if len(errs) > 0 {
diff --git a/internal/provision/self_serve_moves2_census_test.go b/internal/provision/self_serve_moves2_census_test.go
index 0d2d012a0..1c92194b7 100644
--- a/internal/provision/self_serve_moves2_census_test.go
+++ b/internal/provision/self_serve_moves2_census_test.go
@@ -58,6 +58,21 @@ package provision
// exclusion; its pre-fence read is observeRecord. Fix round 1
// re-pins reserveBoxes (the reclaim's three-way verdict; two lost
// races answer busy); its write is unchanged.
+//
+// Task 4 (the journal, the console lease and the pass skeleton): one
+// nonrecord unit is added and two units re-pinned; none is raw.
+// - selfserve_lease.go:leaseToken.put is added, nonrecord (sel:Put): its
+// only write is control/offline-moves/console.json, the self-serve
+// moves console lease, a conditional Put under the ETag read
+// (create-only when absent), never an instance record.
+// - record_sequence.go:WriteRecord, the record primitive, is re-pinned:
+// a guard on the context (WithWriteGuard) runs immediately before the
+// Put and can only refuse it; the Put is unchanged.
+// - server.go:NewServer stays a key-site (the instances literal) and is
+// re-pinned: it sets the row evidence and scans the self-serve journal
+// and active.json for recovery mode, reading no record.
+// - The journal itself is console SQLite, not a bucket write; the pass's
+// record writes arrive with Task 7 (selfServeApply under the lease).
func plan14SelfServeMoves2WriteCensusInventory() []censusEntry {
removed := []struct{ file, unit string }{
{"internal/console/apiinstancemove.go", "Server.handleAPIInstanceMoveRequest"},
@@ -65,6 +80,8 @@ func plan14SelfServeMoves2WriteCensusInventory() []censusEntry {
{"internal/provision/provision.go", "updateRecordSeeding"},
{"internal/provision/seed.go", "SetRecordSeed"},
{"internal/console/apiinstancemove_offline.go", "Server.offlineTakeLock"},
+ {"internal/console/server.go", "NewServer"},
+ {"internal/provision/record_sequence.go", "WriteRecord"},
}
added := []censusEntry{
{"internal/console/apiinstancemove.go", "Server.handleAPIInstanceMoveRequest", "record-writer", "key:SetRecordMove,lit:instances", "6be4b2d5ed59e3e42c47589039daba2cdb4081db45e0258d8889ef8477290591"},
@@ -76,6 +93,9 @@ func plan14SelfServeMoves2WriteCensusInventory() []censusEntry {
{"internal/console/apiinstancemove_offline.go", "Server.offlineApplyBound", "record-writer", "key:SetRecordMoveAt", "039573bfd1f3ec621474e9c761f55ce301227d812f4dd6a7d206dad0c18b9980"},
{"internal/console/offlinereserve.go", "Server.reserveBoxes", "nonrecord", "sel:Put", "d8f9896e36b25b8dd9e2d25df30b3f2b01a08259a7ee1952299d4e1015bba48f"},
{"internal/console/offlinereserve.go", "Server.releaseBoxes", "nonrecord", "sel:Put", "0d7cad2d3a92890c10d7a538784713124d3659d89c872e75fbc107bf96794751"},
+ {"internal/console/selfserve_lease.go", "leaseToken.put", "nonrecord", "sel:Put", "e574640142eb930463ce8c4a91cd599857870311303072902ca8d98901d64fac"},
+ {"internal/console/server.go", "NewServer", "key-site", "lit:instances", "4a3aea119aaef70a5eb1a0e0a01c47ea8f91b1f3b121bb209624d1752e0086e3"},
+ {"internal/provision/record_sequence.go", "WriteRecord", "record-primitive", "key:IsRecordKey,key:WriteRecord,key:preparedRecordKey,sel:Put", "85bcdcc6bfe53d3f170fa3a2b79709b374646460301d12456b453d04e347428e"},
}
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 2a256c912..68abb01dd 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
@@ -1,7 +1,7 @@
# Census overlay self-serve-moves-2, marked rev3=PROPOSED-self-serve-moves-2 (not an accepted ruling; docs/superpowers/notes/census-proposal-self-serve-moves-2.md). Loaded after the self-serve-moves-1 overlay.
# Task 1 (the move-capable token and the self-serve gate), rev3=PROPOSED-self-serve-moves-2: no record envelope changes and no root read, class or task is added. Eight accepted-legacy units are re-pinned in place with reviewed bases and take new frozen fingerprints (the seal moves): the cli_tokens moves column (migrateCLIAuth), its writer and readers (claimRawNewAppToken, CLITokens, ServicePrincipals, CLIPrincipal), the tokens page template, serve (CARLOS_SELF_SERVE_MOVES) and the request door's dispatch. Envelopes re-pinned: cmd/console, internal/console (selfserve_gate.go, the shared instance rails, the mint door's moves field) and internal/console/templates.
-@legacy c50698b41f928d59a9be3f5913eb7f04813ef0f3841f1a4dd8bc9c35635477d7 cmd/console/main.go serve legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=the console (cmd/console serve under console.service, user carlos-console): plan 1's body (Task 0 starts the lifetimes cutover watch, Task 3 the lifetime recovery pass and Task 5 the incarnation migration pass, off unless CARLOS_LIFETIMES_MIGRATE=1) and plan 2 Task 1 reads CARLOS_SELF_SERVE_MOVES into Config.SelfServeMoves, an environment string; the same three data path literals; no privileged reader is added data-literal@4077,data-literal@5178,data-literal@5415 9b46448d569c9244988b281e181acc3031bbfff5cb227d4a16cf6f2e0add7683
-cmd/console/main.go serve legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=the console (cmd/console serve under console.service, user carlos-console): plan 1's body (Task 0 starts the lifetimes cutover watch, Task 3 the lifetime recovery pass and Task 5 the incarnation migration pass, off unless CARLOS_LIFETIMES_MIGRATE=1) and plan 2 Task 1 reads CARLOS_SELF_SERVE_MOVES into Config.SelfServeMoves, an environment string; the same three data path literals; no privileged reader is added data-literal@4077,data-literal@5178,data-literal@5415 9b46448d569c9244988b281e181acc3031bbfff5cb227d4a16cf6f2e0add7683
+@legacy 2107e4464500907572f1d145aa4e0421e7641269cc224b17832f52bbfb7d1b8b cmd/console/main.go serve legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=the console (cmd/console serve under console.service, user carlos-console): plan 1's body (Task 0 starts the lifetimes cutover watch, Task 3 the lifetime recovery pass and Task 5 the incarnation migration pass, off unless CARLOS_LIFETIMES_MIGRATE=1) and plan 2 Task 1 reads CARLOS_SELF_SERVE_MOVES into Config.SelfServeMoves, an environment string; plan 2 Task 4 passes the console store to the paging arm (armMovesPaging reads the self-serve journal and the bucket's active.json) and starts the self-serve moves pass as a recorded worker; the same three data path literals; no privileged reader is added data-literal@4077,data-literal@5178,data-literal@5415 cecee2685e8802c2d9223141b57359203c0fba5b29b2e04f173cd37420f8de13
+cmd/console/main.go serve legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=the console (cmd/console serve under console.service, user carlos-console): plan 1's body (Task 0 starts the lifetimes cutover watch, Task 3 the lifetime recovery pass and Task 5 the incarnation migration pass, off unless CARLOS_LIFETIMES_MIGRATE=1) and plan 2 Task 1 reads CARLOS_SELF_SERVE_MOVES into Config.SelfServeMoves, an environment string; plan 2 Task 4 passes the console store to the paging arm (armMovesPaging reads the self-serve journal and the bucket's active.json) and starts the self-serve moves pass as a recorded worker; the same three data path literals; no privileged reader is added data-literal@4077,data-literal@5178,data-literal@5415 cecee2685e8802c2d9223141b57359203c0fba5b29b2e04f173cd37420f8de13
@legacy 74173dd0410354dc3c0e4d80bec910c2107355d36b6b7bf30e719fdf6c20a1a9 internal/console/apiinstancemove.go Server.handleAPIInstanceMoveRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=the console (cmd/console serve under console.service, user carlos-console): the census-ruling-region-ha-2a body unchanged; with Phase 1 off the dispatch sends self-serve mode to selfServeMoveRequest (the self-serve gate: binding SQL only, no bucket read, no record read; Task 1's body answers the family's 404) and every other request to the legacy body as before; read sites unchanged, offsets moved file:Exec@10257,file:Exec@10434,file:Exec@10585,file:Exec@11585,file:Exec@9178,file:Exec@9266,file:Read@7664,file:Read@9976 6be4b2d5ed59e3e42c47589039daba2cdb4081db45e0258d8889ef8477290591
internal/console/apiinstancemove.go Server.handleAPIInstanceMoveRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=the console (cmd/console serve under console.service, user carlos-console): the census-ruling-region-ha-2a body unchanged; with Phase 1 off the dispatch sends self-serve mode to selfServeMoveRequest (the self-serve gate: binding SQL only, no bucket read, no record read; Task 1's body answers the family's 404) and every other request to the legacy body as before; read sites unchanged, offsets moved file:Exec@10257,file:Exec@10434,file:Exec@10585,file:Exec@11585,file:Exec@9178,file:Exec@9266,file:Read@7664,file:Read@9976 6be4b2d5ed59e3e42c47589039daba2cdb4081db45e0258d8889ef8477290591
@legacy 5aa23d2f21436a1f9ad50c36a41b12e53bbd234e3fafb2eaaab7b4597d83d4b4 internal/console/authstore.go Store.CLITokens legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: lists one address's live cli_tokens rows from the console SQLite store, now with the moves column; no bucket or root action file:Query@78 5c231bb3848378ec9ec9edf92d35e57927557b521d4bfed415f24ace0fb78689
@@ -16,12 +16,63 @@ internal/console/svcprincipal.go Store.CLIPrincipal legacy accepted-legacy accep
internal/console/svcprincipal.go Store.ServicePrincipals legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: lists one account's live app and service cli_tokens rows from the console SQLite store, now with the moves column; no bucket or root action file:Query@87 6497829090bbca886acbd1a2fec6f2130ad9d0e9eb8474f36642f3f20abc41b6
@legacy 058a42b1f720a95b9590b30f143ba9892bb169f1c43a386caabdf1fca6b32829 internal/console/templates/settings.html external legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: the tokens page template marks a move-capable app token beside its app; it renders the rows its handler read and reads nothing of its own file:external@0 4e903910a7001a43e9f206a7d43cdf9d1fbf1e0af331f6b08f194b21681e05e4
internal/console/templates/settings.html external legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: the tokens page template marks a move-capable app token beside its app; it renders the rows its handler read and reads nothing of its own file:external@0 4e903910a7001a43e9f206a7d43cdf9d1fbf1e0af331f6b08f194b21681e05e4
-@coverage cmd/console b5a2b44792eeca719204eed8f55cf3c8d91f67e17532727f7adedda313470b37
+@coverage cmd/console 82b97d3333df4e52b486d98f22b469cbdb0b83d36276e5832d686cfa6c832199
@coverage internal/console/templates d3e4b3b103e9a9966a8c91e6683c241b2e53ce55a24f6511c53266586b7f1c5d
# 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/provision 177bbc7f94d9ba7e4d999f832920e8552a0af4315d7ce2445572efb21fada342
+@coverage internal/provision 42588b9f64803b41c4463efb31e69fda0fc66573efd5d6a2d6e709682eeca385
# 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 55df205ec0e85b6e40263c55a6673a715a385c15b3bbc33a119f110dd59eecd9
+@coverage internal/console bc37711925780197a23dee96565006cb22e012f26d4e0decbc04b29b5a990c2a
@coverage internal/moves 7fcb4834a033d8cea99621143ba2afcbf14568c9467f08329af7c692d0c1359b
+# Task 4 (the journal, the events table, the console lease and the pass skeleton), rev3=PROPOSED-self-serve-moves-2: no root read, class or task is added. The console SQLite journal (selfserve_store.go) adds twenty-two units, each frozen accepted-legacy with a reviewed basis (console SQL as carlos-console, no bucket or root action), and the lease's console name adds one crypto/rand read (Server.selfServeConsoleID); serve (in place: the paging arm takes the store, the pass is started), NewServer (the row evidence and the recovery-mode scan) and Store.migrate (migrateSelfServe) are re-pinned. The seal moves. The lease and the pass read control/offline-moves/console.json and active.json from the bucket, not a file. Envelopes re-pinned in place: cmd/console, internal/console and internal/provision (WithWriteGuard in WriteRecord).
+@legacy a8dd51a494ab3cd2ad371ded2850b6d78492569729527ea82cd8dd0f2b418a78 internal/console/selfserve_lease.go Server.selfServeConsoleID legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: reads crypto/rand once per process for the random console name the self-serve moves lease carries; randomness grants no root authority file:Read@110 3303b18ec5c77ccaf662064801f94276bfe947c31378cc1ecb8c4fe509d5d4d1
+internal/console/selfserve_lease.go Server.selfServeConsoleID legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: reads crypto/rand once per process for the random console name the self-serve moves lease carries; randomness grants no root authority file:Read@110 3303b18ec5c77ccaf662064801f94276bfe947c31378cc1ecb8c4fe509d5d4d1
+@legacy b363b2d8860b7c4a98f0bcb6f1bfad8cd677a31124f35fd2653c87800f341cec internal/console/selfserve_store.go InsertSelfServeEventTx legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: allocates a self-serve row's next event seq and inserts the event inside the caller's SQLite transaction; no bucket or root action file:Exec@473,file:Exec@79,file:QueryRow@295 7a4026a7b9c7d66811f3cdd8524e626d75943685dc01ea620344b1beb32a4b0a
+internal/console/selfserve_store.go InsertSelfServeEventTx legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: allocates a self-serve row's next event seq and inserts the event inside the caller's SQLite transaction; no bucket or root action file:Exec@473,file:Exec@79,file:QueryRow@295 7a4026a7b9c7d66811f3cdd8524e626d75943685dc01ea620344b1beb32a4b0a
+@legacy 5609b7244aff708a4bf14eaa46171a863287670f4f268763d026c7b2b73b1c22 internal/console/selfserve_store.go Store.AbandonSelfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: abandons a confirmed self-serve request with the next deadline, predicated on its nonce; no bucket or root action file:Exec@112 457a13c673d462a5a9f448156d5018b60272cfaec5eb00304171369695635c94
+internal/console/selfserve_store.go Store.AbandonSelfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: abandons a confirmed self-serve request with the next deadline, predicated on its nonce; no bucket or root action file:Exec@112 457a13c673d462a5a9f448156d5018b60272cfaec5eb00304171369695635c94
+@legacy d11a9d367edfc25b752df39e2f77721789134ecf156e6eb1c210323d818318c6 internal/console/selfserve_store.go Store.AdmitSelfServeMove legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: inserts one self-serve move row and its first phase event in one SQLite transaction, or reads the instance's open row back; no bucket or root action file:Exec@808,file:QueryRow@1501 b7efbed05c6a80a85258a561188c9a112d6ce2f3a5a0fca0463adc0d36221845
+internal/console/selfserve_store.go Store.AdmitSelfServeMove legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: inserts one self-serve move row and its first phase event in one SQLite transaction, or reads the instance's open row back; no bucket or root action file:Exec@808,file:QueryRow@1501 b7efbed05c6a80a85258a561188c9a112d6ce2f3a5a0fca0463adc0d36221845
+@legacy c2e1ad98b9e02efa012eee032c98c16b38efbc62b4ba9ff1b52bfb0822762cd2 internal/console/selfserve_store.go Store.AdvanceSelfServe legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: advances one self-serve row's phase, predicated on the phase, the abort flag and an optional confirmed nonce it consumes, with the phase event in one SQLite transaction; no bucket or root action file:Exec@230 79b0ec7f50cf599f0661de8fbb273db6e20faf7adb95d9f6dd8e459c5a1de41e
+internal/console/selfserve_store.go Store.AdvanceSelfServe legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: advances one self-serve row's phase, predicated on the phase, the abort flag and an optional confirmed nonce it consumes, with the phase event in one SQLite transaction; no bucket or root action file:Exec@230 79b0ec7f50cf599f0661de8fbb273db6e20faf7adb95d9f6dd8e459c5a1de41e
+@legacy bb4226610c6d82f9c7ca9a79e6e4132ad0150bfa1841fa8db3364c4793c97fbb internal/console/selfserve_store.go Store.ConfirmSelfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: marks a self-serve request confirmed, predicated on its nonce and unconsumed, unvoided, unconfirmed flags; no bucket or root action file:Exec@221 811edad437cdf69f10bd91c33daa7330bfdf92934ef10c9b7e61ba01b62e4f94
+internal/console/selfserve_store.go Store.ConfirmSelfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: marks a self-serve request confirmed, predicated on its nonce and unconsumed, unvoided, unconfirmed flags; no bucket or root action file:Exec@221 811edad437cdf69f10bd91c33daa7330bfdf92934ef10c9b7e61ba01b62e4f94
+@legacy 95da6de389a580fa5c0802c37d9409f171f34d1b756765ffa962087687aa679b internal/console/selfserve_store.go Store.ConsumeSelfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: consumes a confirmed self-serve request with the next substep and deadline, predicated on its nonce; no bucket or root action file:Exec@122 56964152a1adc56490a602c9649b40affee2e11b86ee48200721e9e24ca95229
+internal/console/selfserve_store.go Store.ConsumeSelfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: consumes a confirmed self-serve request with the next substep and deadline, predicated on its nonce; no bucket or root action file:Exec@122 56964152a1adc56490a602c9649b40affee2e11b86ee48200721e9e24ca95229
+@legacy 30032f05c9b16baf962ad740663f1bb176db19df4e0a597d9a91a62a1b6c43c0 internal/console/selfserve_store.go Store.FinishSelfServeMove legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: records a self-serve row's outcome, consumes its confirmed request and inserts the terminal event in one SQLite transaction, only while the row has no outcome; no bucket or root action file:Exec@290,file:QueryRow@743 c1a5cc032c23ac2ebfe7fccebdd88a8912c2063f59fffe5369081186d817d0a6
+internal/console/selfserve_store.go Store.FinishSelfServeMove legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: records a self-serve row's outcome, consumes its confirmed request and inserts the terminal event in one SQLite transaction, only while the row has no outcome; no bucket or root action file:Exec@290,file:QueryRow@743 c1a5cc032c23ac2ebfe7fccebdd88a8912c2063f59fffe5369081186d817d0a6
+@legacy 888bde94f97959ae38336fc48530f9dd76532faea049743f1f57bee01550a20a internal/console/selfserve_store.go Store.HostHasIssuedRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: counts a host's self-serve rows with an issued request still in flight, for the reservation reclaim rule; no bucket or root action file:QueryRow@96 a0fef5ee78031bafdbebd06f2268011a2f2d4f700c01617d04da596ce40c8c89
+internal/console/selfserve_store.go Store.HostHasIssuedRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: counts a host's self-serve rows with an issued request still in flight, for the reservation reclaim rule; no bucket or root action file:QueryRow@96 a0fef5ee78031bafdbebd06f2268011a2f2d4f700c01617d04da596ce40c8c89
+@legacy 09a6b77755e93a3002743007c6689309f6c40cd5308c15da53cfbc7dae6e2c7e internal/console/selfserve_store.go Store.IssueSelfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: persists one request on a self-serve row, predicated on its phase, no outcome and no unconsumed request; no bucket or root action file:Exec@541 39cee79531f56be7080e0e8a030d7913a19554034b19118050d7a55ecb249e86
+internal/console/selfserve_store.go Store.IssueSelfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: persists one request on a self-serve row, predicated on its phase, no outcome and no unconsumed request; no bucket or root action file:Exec@541 39cee79531f56be7080e0e8a030d7913a19554034b19118050d7a55ecb249e86
+@legacy ddf0838225e17f4c4c292464b21b1690f716b7ec626987047be1776e377974e8 internal/console/selfserve_store.go Store.MarkSelfServeCut legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: the cut door's one write: marks a ready self-serve row cut, predicated on no abort, cut or expiry; no bucket or root action file:Exec@222 d887f36b2630a505cad973beb413d26c6234a54ef18a1671c313387de766141f
+internal/console/selfserve_store.go Store.MarkSelfServeCut legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: the cut door's one write: marks a ready self-serve row cut, predicated on no abort, cut or expiry; no bucket or root action file:Exec@222 d887f36b2630a505cad973beb413d26c6234a54ef18a1671c313387de766141f
+@legacy 5eb8d1adda16992f46c5e9ff1e029e266ecdb7f3adc147a90f737c5c1f936dd5 internal/console/selfserve_store.go Store.MoveTerminalOrAbsent legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: reads one self-serve row's outcome and release flag for the reservation reclaim rule; no bucket or root action file:QueryRow@111 7d2f7eacc22eeb69b8a95ef146a472a915dc3c1a143d75fb4eb87e11786f6a13
+internal/console/selfserve_store.go Store.MoveTerminalOrAbsent legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: reads one self-serve row's outcome and release flag for the reservation reclaim rule; no bucket or root action file:QueryRow@111 7d2f7eacc22eeb69b8a95ef146a472a915dc3c1a143d75fb4eb87e11786f6a13
+@legacy eb87ab1c91a9120514bc2a0b6fb7e56f8d44006684ae3d3922dac93f5bc31213 internal/console/selfserve_store.go Store.RequestSelfServeAbort legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: marks an abort requested on a self-serve row in one of the given phases; no bucket or root action file:Exec@372 89bb4c26a83a01c46ffce17e91382182c1356a24aa02f0eb0584f4ec2a0178d7
+internal/console/selfserve_store.go Store.RequestSelfServeAbort legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: marks an abort requested on a self-serve row in one of the given phases; no bucket or root action file:Exec@372 89bb4c26a83a01c46ffce17e91382182c1356a24aa02f0eb0584f4ec2a0178d7
+@legacy 44c2b0be5f7e536d50699223225b253a8b805c834a35138f44232ca9a83dea34 internal/console/selfserve_store.go Store.SelfServeMove legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: reads one self-serve move row from the console SQLite store; no bucket or root action file:QueryRow@96 607827d36fccc1f64607427fce954cf9f6f1e2a5e005e50f72bed50d1944152d
+internal/console/selfserve_store.go Store.SelfServeMove legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: reads one self-serve move row from the console SQLite store; no bucket or root action file:QueryRow@96 607827d36fccc1f64607427fce954cf9f6f1e2a5e005e50f72bed50d1944152d
+@legacy 20dc8f1831efbb9568fbc0c6cf830c21aaa6a0df1d996c3404823e81c53a57f9 internal/console/selfserve_store.go Store.SelfServeRecoveryPending legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: counts the self-serve rows and undelivered events the pass still owes, for recovery mode; no bucket or root action file:QueryRow@78 2f1f0f6502aead50b7e0ae48179b7babd36bca3a1537b09aa7bd6fe59fd3be31
+internal/console/selfserve_store.go Store.SelfServeRecoveryPending legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: counts the self-serve rows and undelivered events the pass still owes, for recovery mode; no bucket or root action file:QueryRow@78 2f1f0f6502aead50b7e0ae48179b7babd36bca3a1537b09aa7bd6fe59fd3be31
+@legacy 98d0f21730dc4441280f9f8f55310393eab6ad37ec0cd0ee533b94e23a6a5a23 internal/console/selfserve_store.go Store.SetSelfServeExpiry legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: records an expiry bound to a self-serve row's phase and the deadline read; no bucket or root action file:Exec@368 d095c79c42af88f5865e3e4e1dc123028ccf6741489c918768979eae5102d578
+internal/console/selfserve_store.go Store.SetSelfServeExpiry legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: records an expiry bound to a self-serve row's phase and the deadline read; no bucket or root action file:Exec@368 d095c79c42af88f5865e3e4e1dc123028ccf6741489c918768979eae5102d578
+@legacy bc152b81b20eb8142d84c8db3493e16c6b9b3c2b83a8c6a5768cf1f2d8f99ec3 internal/console/selfserve_store.go Store.UnemittedSelfServeEvents legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: lists undelivered self-serve events from the console SQLite store; no bucket or root action file:Query@94 be88efeaf911708fdc26a7ab288ff49ac04768ce0a657f1107ceb4eb4fd65d2a
+internal/console/selfserve_store.go Store.UnemittedSelfServeEvents legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: lists undelivered self-serve events from the console SQLite store; no bucket or root action file:Query@94 be88efeaf911708fdc26a7ab288ff49ac04768ce0a657f1107ceb4eb4fd65d2a
+@legacy 319506acaa32df215a15bbdbac67c2da01be9ec85c9e06124052397c4665397e internal/console/selfserve_store.go Store.VoidSelfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: voids an unconfirmed, unconsumed self-serve request, predicated on its nonce; no bucket or root action file:Exec@74 a34f7e295861352741da1c27a13ab5185b4e900eedf597b05c391306d0603c57
+internal/console/selfserve_store.go Store.VoidSelfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: voids an unconfirmed, unconsumed self-serve request, predicated on its nonce; no bucket or root action file:Exec@74 a34f7e295861352741da1c27a13ab5185b4e900eedf597b05c391306d0603c57
+@legacy 5dfb7814df4942c488984e6aa1a0e4872884468c735fb2e5217fe0f19148528c internal/console/selfserve_store.go Store.migrateSelfServe legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: creates the self-serve journal tables (self_serve_moves with its open-row unique index, self_serve_events, self_serve_timings) and move_timings at unarmed store boot, idempotently; no bucket or root action file:Exec@4602 2738ade2f699608c3878071326acc0140d764ab5d21b036b28faa7add6e30f7b
+internal/console/selfserve_store.go Store.migrateSelfServe legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: creates the self-serve journal tables (self_serve_moves with its open-row unique index, self_serve_events, self_serve_timings) and move_timings at unarmed store boot, idempotently; no bucket or root action file:Exec@4602 2738ade2f699608c3878071326acc0140d764ab5d21b036b28faa7add6e30f7b
+@legacy 8df4dff0cd99a5606cdc1197e88f0039ded580fa3043b7705de91ccf362bf435 internal/console/selfserve_store.go Store.selfServeMoves legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: lists self-serve move rows under a fixed predicate from the console SQLite store; no bucket or root action file:Query@99 3780495187ef409c945d603d2aabe9aeecd02044ba034ee9e349d2f38b551466
+internal/console/selfserve_store.go Store.selfServeMoves legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: lists self-serve move rows under a fixed predicate from the console SQLite store; no bucket or root action file:Query@99 3780495187ef409c945d603d2aabe9aeecd02044ba034ee9e349d2f38b551466
+@legacy 7696cdcbbb86d8e2adb5e1e896d4def7c4ffe6d62aeea4839f426e74c6f718f6 internal/console/selfserve_store.go Store.selfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: reads one self-serve row's request columns with cut_requested_at; no bucket or root action file:QueryRow@270 491bb9fe1c4b487f130d3a07c2a840995e408e86d6f8afaa561be57c61442dbb
+internal/console/selfserve_store.go Store.selfServeRequest legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: reads one self-serve row's request columns with cut_requested_at; no bucket or root action file:QueryRow@270 491bb9fe1c4b487f130d3a07c2a840995e408e86d6f8afaa561be57c61442dbb
+@legacy 0d9f415961b7e7fd3645705ab6a10d8153ac6e49153e839ef6da687b5177e929 internal/console/selfserve_store.go Store.setSelfServe legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: updates fixed columns of one self-serve row (the estimate, health, gates, warning, notified seq, abort facts, effects, release, open record); no bucket or root action file:Exec@141 8db17fe770db1c0f0f44dd8623a38156518ebbba8ceead4cb7c649e0fc2a4c83
+internal/console/selfserve_store.go Store.setSelfServe legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: updates fixed columns of one self-serve row (the estimate, health, gates, warning, notified seq, abort facts, effects, release, open record); no bucket or root action file:Exec@141 8db17fe770db1c0f0f44dd8623a38156518ebbba8ceead4cb7c649e0fc2a4c83
+@legacy d98c167a2f01901679bd70c307f6f447f83283cc339592a6a68bc615a49dd26a internal/console/selfserve_store.go auditActivityTx legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: resolves an account sqid and inserts one audit row inside the caller's SQLite transaction (a self-serve event's delivery); no bucket or root action file:Exec@285,file:QueryRow@112 8027f01859caad9c9f8a732ce6978b267cbe5f5a1e1e8fecbf24d4240f021710
+internal/console/selfserve_store.go auditActivityTx legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: resolves an account sqid and inserts one audit row inside the caller's SQLite transaction (a self-serve event's delivery); no bucket or root action file:Exec@285,file:QueryRow@112 8027f01859caad9c9f8a732ce6978b267cbe5f5a1e1e8fecbf24d4240f021710
+@legacy da71e59223c0f111d52ecd23d5e75362ab4356c3e01c9f4b65e0cd1e31feab09 internal/console/server.go NewServer legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console construction adds the lifetime judge, the cutover admission gate and its first marker read; claims-off app materialisation moves to the cutover watch; plan 2 Task 4 sets the store as the reclaim rule's row evidence and scans the self-serve journal and the bucket's active.json for recovery mode on a claims-off console; the existing logs/data route literal moves and is not a filesystem read data-literal@9149 4a3aea119aaef70a5eb1a0e0a01c47ea8f91b1f3b121bb209624d1752e0086e3
+internal/console/server.go NewServer legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console construction adds the lifetime judge, the cutover admission gate and its first marker read; claims-off app materialisation moves to the cutover watch; plan 2 Task 4 sets the store as the reclaim rule's row evidence and scans the self-serve journal and the bucket's active.json for recovery mode on a claims-off console; the existing logs/data route literal moves and is not a filesystem read data-literal@9149 4a3aea119aaef70a5eb1a0e0a01c47ea8f91b1f3b121bb209624d1752e0086e3
+@legacy 21e8d6011940ef180e4d52a7da73ed962fbab94f7ee3e720ab94ce99e632249f internal/console/store.go Store.migrate legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: adds the three account/app-scoped lifetime intent tables and apps.lifetime at unarmed store boot, then backfills missing lifetimes; plan 2 Task 4 calls migrateSelfServe last; no bucket or root action file:Exec@13226,file:Exec@19282 21f4de8f705ef043ce2f2b10805c2e51c92fcf04a435b47a19dc148f6c3042cf
+internal/console/store.go Store.migrate legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-2;basis=console.service user carlos-console, J4 root-input: adds the three account/app-scoped lifetime intent tables and apps.lifetime at unarmed store boot, then backfills missing lifetimes; plan 2 Task 4 calls migrateSelfServe last; no bucket or root action file:Exec@13226,file:Exec@19282 21f4de8f705ef043ce2f2b10805c2e51c92fcf04a435b47a19dc148f6c3042cf