carlos / platform Public
Census: parallel mirror rounds and the offline move's stage door
Status: RULED. census-ruling-moves-offline-precopy, by the coordinator as census owner for this branch, 2026-10-21: accepted as proposed. Every row carries rev3=census-ruling-moves-offline-precopy.
The branch is moves-offline-precopy, from main 3d444a1b. It was ruled at 21cd69a4 (after Astra's round 1 fixes, 692d41cf), then re-ruled at 981294cf (round 2, 0a6bb375) merged with main f550afa2 (see "Round 2 and the merge of main" below). It does two things. It runs a mirror round's copies and deletes side by side in lease.MirrorPrefixKeep. And it adds carlos move offline stage, which pre-copies the replica while the instance keeps serving, as a new record state, offline-staging.
The ruling adds:
internal/provision/testdata/plan14-root-reads-moves-offline-precopy.tsv, loaded last inTestPlan14RootReadCensus, after design-plans-and-caps, once design-plans-and-caps' seal has been checked;internal/provision/moves_offline_precopy_census_test.go, the record-write (9B8) overlay on design-plans-and-caps' inventory, whichTestPlan9B8RecordWriteCensusnow reads;- one line in
internal/console/testdata/claim-routes.txt.
No earlier overlay is edited.
The footing
Every unit here runs in the box agent (/opt/carlos/carlos agent, carlos-edge.service, User=carlos) or the console (cmd/console serve, user carlos-console). None makes a root decision. So each stays accepted legacy at the disposition census-ruling-moves-offline gave it or its sibling: J1 (root-route) for registry and route reads, J4 (root-input) for file reads.
Root-read census
| unit | change | executing identity | disposition | |---|---|---|---| | internal/host/movesoffline.go movesWorker.offlineSourceRevert | changed legacy: calls offlineUnstage before it reports reverted. Read sites unchanged; whole:Route@701 moved to @843 (the same rt read, shifted by the inserted lines) | the agent, on abort, through the armed moves worker | re-frozen, J1 | | internal/provision/offline_move.go offlineBegin | new: OfflineStart's body moved unchanged, with the state as a parameter. Its one site, file:Open@198, is rec.ActiveRun.Open(), i.e. RunState.Open() (queued, picked or cancel), a method, not a file read | the console, through SetRecordMove | accepted legacy, J4 (as OfflineStart) | | internal/provision/offline_move.go OfflineStart | retired: still declared, reads nothing. It calls offlineBegin(…, OfflineStopping) and is behaviour-identical to main: same refusals in the same order, same stamps, and the StagedAt stamp fires only for staging | the console | H3 census-ruling removal (the carlos-run getJSON form) |
What offlineUnstage reads and writes
It does nothing unless the record carries staged_at and this box is the stager the stage door pinned on the record (move.stager). On that one box it reads only the object store:
- its own stager report (
OfflineReportKey); - this box's receipts object.
It then:
- deletes
moves/<host>/in the staging bucket the stage door fixed on the record (move.stage_bucket), through a mirror formove.stage_region(round 2). It refuses a baremoves/, a missing bucket and this box's own bucket, and in those cases acknowledges nothing.offlineSealRevertalready deletes the same prefix the same way; - clears
move-receipts/<host>.jsonincfg.OwnBucket, the same bucket the stage round writes them to (tombstoneAt); - writes its stager report as reverted.
It reads no local file. The host comes from the record. The stored bucket and region come from the target box's edges file at stage time, written to the record by the console through SetRecordMove. The operator names only a region code, and the tenant controls none of these paths.
Envelopes re-pinned (at 21cd69a4; superseded by the round 2 table below)
None of these changes adds a read site. Every changed production file was read for this ruling.
| envelope | what changed | hash | |---|---|---| | . | move.go: the stage subcommand and its status and abort lines; no os, file, exec or env use | 44fc0f3e…8bdd | | internal/console | the stage door; offlineBeginChecks, shared by start and stage; the stager pin; the finish gate on the stager's reverted report; the status view | 6ae90250…92d6 | | internal/host | movesoffline_stage.go, new: reads only the object store, and imports os only for os.ErrNotExist. edgesfile.go publishes offline-stage-1. offlineRound has a staging branch | e9234db8…8f47 | | internal/lease | mirror.go: forEachParallel; copies, then deletes, side by side; the receipts book read before the workers start | 980f38c4…62a14 | | internal/moves | the stager role, the staged phase, the report's progress fields, OfflineStager | 33c7b507…e071 | | internal/provision | OfflineStage (which needs a stager), OfflineRestage, OfflineStartStaged; MoveState.StagedAt and MoveState.Stager | 8c868695…4b64 |
Record-write census (9B8)
Two sites change, each keeping main's class. No site is added.
| file:function | kinds | class | why | |---|---|---|---| | internal/console/authcli.go:Server.registerCLIRoutes | lit:instances,lit:sidecars | key-site (main's) | one route line for the stage door, beside the six offline doors; no write added. Digest 2aee959d…c459 | | internal/lease/mirror.go:MirrorPrefixKeep | sel:CopyVersioned | versioned-transport (main's) | see below. Digest dff4d03f…fb19 |
MirrorPrefixKeep plans its skips in key order first. It then runs the copies on up to DefaultMirrorWorkers (8) goroutines and, only once every copy has landed, the deletes. Its writes are the same as before:
- Copy or CopyVersioned into the destination prefix;
- Delete of destination keys the source no longer has;
- receipts through the book (
set,drop,save). Every update is under one mutex, andreceiptLedger.saveis still the one Put, unchanged.
After round 1, the book is read once, before the workers start, whenever the round has copies to make. So set and drop touch memory only, and no request is made under the mutex.
The stager pin is a new field on MoveState. It is written through the typed provision.SetRecordMove (offlineApply), like every other move field, under the same one-move lock. That is not a new write site.
Claim route census
POST /api/cli/apps/{account}/{app}/instances/{host}/move/offline/stage joins claim-routes.txt.
- Access. The door is operator-only:
offlineGatecallsmoveOperatorGate, and any token that is not an operator's gets a 404. - Claims consoles. With host claims or Phase 1 on, it is refused with MV-O20.
- Tests. Its claim-refusal fixture and its
svcroutes_test.goexemption match the six sibling offline doors exactly.
Round 2 and the merge of main
The merge (d5b020ba)
Main (f550afa2) added two overlays, run-lifecycle-logs-325 and design-plans-and-caps, and a 9B8 inventory, plansCapsWriteCensusInventory. They touch none of this branch's units. The merge keeps both of main's overlays and loads this branch's last, after design-plans-and-caps, whose seal (4f5e0ed6…df17) is checked first. This branch's 9B8 overlay now sits on plansCapsWriteCensusInventory.
Three envelopes (., internal/console, internal/host) and registerCLIRoutes are changed by both sides. Because this branch's overlay loads last, it pins them on the merged tree.
Round 2 (0a6bb375): what changed, and the census reading
- The stage door fixes the staging destination on the record:
MoveState.StageBucketandStageRegion, from the one target box's edges file. The door refuses a source box's own bucket (MV-O191).provision.OfflineStagetakes aStageTargetand refuses an empty stager or bucket. These are record fields written through the typedSetRecordMove(offlineBegin,offlineApply), so there is no new write site. - The stager re-reads the record before a round, and once before its delete phase (
stageDeleteGate).provision.GetRecordis an object-store read, not a local file, and the scan finds no root-read unit inmovesoffline_stage.go. A move that has left staging starts no round and deletes nothing. - The stager acknowledges it has stopped (
offlineStagerStopped). It writes its own stager report throughwriteOfflineReport, an existing nonrecord site, and is called fromofflineSourceStepin stopping through landed. - offlineUnstage deletes from the stored destination:
moves/<host>/inmove.stage_bucket, through a mirror formove.stage_region. It refuses, and acknowledges nothing, when no bucket is stored or the stored bucket is this box's own. It reads only the object store. - offlineTakeSeal gains one refusal before its mirror: a staged move seals only into the stored bucket and region. This is a comparison of record and edges-file fields. Its 16 read sites are unchanged, each shifted by 487 bytes. Its Puts (nonrecord, under the move's
moves/@offline/prefix) are unchanged. - The console: - Start and seal refuse when the pinned stager is missing from the fleet (MV-O190), and stage, start and seal refuse changed coordinates (MV-O192). - Seal waits for a fresh acknowledgement from the stager (M-C10, or MV-O178 when it is stale). - These checks read only the fleet and the report evidence the doors already read.
- internal/edgesfile:
PlacementViewgainsDataBucketRegion, a JSON field. No read site.
Rows and sites changed by round 2 and the merge
| census | unit or envelope | ruling | |---|---|---| | Plan 14 | internal/host/movesoffline_seal.go movesWorker.offlineTakeSeal | re-frozen at J1, census-ruling-moves-offline's disposition; digest f606873f…cdaa | | Plan 14 | movesWorker.offlineSourceRevert | basis updated: offlineUnstage now uses the stored destination; digest and sites unchanged | | Plan 14 | envelopes ., internal/console, internal/edgesfile, internal/host, internal/provision | re-pinned on the merged tree; internal/lease and internal/moves unchanged since round 1 | | 9B8 | movesWorker.offlineTakeSeal | nonrecord (census-ruling-moves-offline's), sel:Put; digest f606873f…cdaa | | 9B8 | Server.registerCLIRoutes | key-site, pinned on the merged tree; digest a0aff01b…2e87 |
| envelope | hash | |---|---| | . | 649bb6a5…eabd | | internal/console | 7bdd3caa…81c9 | | internal/edgesfile | 71692438…811e | | internal/host | d58c8e60…e219 | | internal/lease | 980f38c4…62a14 | | internal/moves | 33c7b507…e071 | | internal/provision | 210b1740…34dc |
The seal
Three accepted-legacy rows add frozen declarations (offlineSourceRevert, offlineBegin and offlineTakeSeal), so the legacy seal moves from 4f5e0ed6…df17 (main, after design-plans-and-caps) to 940ed1bba8d911e0f57af83ecac253c77ba5e14dad590258e0ee03ec98ccb818. A removal is not in the seal. The test checks design-plans-and-caps' seal before it loads this overlay. Each seal was read from a temporary t.Logf of rootReadLegacySeal(inv), which was then removed.
Earlier seals of this overlay:
965fda6e…c81, on its own after scan-demand-revival;8ef09f41…1a4d, after the merge, before round 2.
Ruling (the coordinator, 2026-10-21): census-ruling-moves-offline-precopy, ACCEPTED as proposed
- offlineSourceRevert and offlineUnstage: ACCEPTED. Object-store reads only, on the pinned stager only, deleting only in the staging destination fixed on the record; there is no root read of an operator- or tenant-controlled path.
- offlineBegin: ACCEPTED (J4, as OfflineStart). OfflineStart is retired by the H3 removal, since it is behaviour-identical.
- offlineBegin's
file:Open@198isrec.ActiveRun.Open(): ACCEPTED. - The envelopes: ACCEPTED, re-pinned on the merged tree (981294cf with main f550afa2), internal/edgesfile included.
- MirrorPrefixKeep: ACCEPTED at versioned-transport. Its record writes still go through the book.
- registerCLIRoutes: ACCEPTED at key-site.
- The stage route: ACCEPTED. It is operator-only, refused with MV-O20 on claims consoles, and has its fixture.
- offlineTakeSeal (round 2): ACCEPTED, re-frozen at J1 and nonrecord in 9B8. It adds one refusal and no read or write.
- The round 2 record fields and reads (
StageBucket,StageRegion, the record re-reads, the stop acknowledgement): ACCEPTED. The fields go throughSetRecordMove, the reads go to the object store, and the report write goes throughwriteOfflineReport. - The merge with main f550afa2: ACCEPTED. Both overlays are kept, this branch's is loaded last, and the seal is recomputed.
The first verification was at 25c0db26. It was re-verified at 21cd69a4 for the round 1 fixes (the stager pin, cleanup by staged_at, the receipts preload), then at 981294cf merged with main f550afa2 for the round 2 fixes (the stager in the seal barrier, the fixed staging destination, the record re-reads).