carlos / platform Public

census-ruling-moves-offline-precopy (round 2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev e457f59d79cd3b5a95bc153fdd16ad02d56dcf7e parent d5b020b
4 files changed, +143 −39
  • docs/superpowers/notes/census-proposal-moves-offline-precopy.md +116 −23
  • internal/provision/moves_offline_precopy_census_test.go +9 −3
  • internal/provision/plan14_root_reads_test.go +4 −3
  • internal/provision/testdata/plan14-root-reads-moves-offline-precopy.tsv +14 −10

CI passed — run details

CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok  	amadan.net/carlos/platform.git/launchctx	0.011s
?   	amadan.net/carlos/platform.git/launchctx/controlname	[no test files]
?   	amadan.net/carlos/platform.git/launchctx/wireid	[no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok  	github.com/carlosframework/platform	105.835s
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	2.842s
ok  	github.com/carlosframework/platform/cmd/moveprobe	93.337s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.707s
ok  	github.com/carlosframework/platform/internal/activator	251.050s
ok  	github.com/carlosframework/platform/internal/agentsock	0.020s
ok  	github.com/carlosframework/platform/internal/alerting	0.004s
ok  	github.com/carlosframework/platform/internal/analytics	0.025s
ok  	github.com/carlosframework/platform/internal/appconfig	2.480s
ok  	github.com/carlosframework/platform/internal/apperr	0.089s
ok  	github.com/carlosframework/platform/internal/applog	0.214s
ok  	github.com/carlosframework/platform/internal/awsinv	0.007s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.005s
ok  	github.com/carlosframework/platform/internal/boxid	0.797s
?   	github.com/carlosframework/platform/internal/boxid/boxidtest	[no test files]
?   	github.com/carlosframework/platform/internal/boxid/internal/signtest	[no test files]
ok  	github.com/carlosframework/platform/internal/boxvolumes	0.012s
ok  	github.com/carlosframework/platform/internal/caps	0.013s
ok  	github.com/carlosframework/platform/internal/certwarm	0.003s
ok  	github.com/carlosframework/platform/internal/cgroup	0.719s
ok  	github.com/carlosframework/platform/internal/changelog	0.358s
ok  	github.com/carlosframework/platform/internal/claimactivation	0.863s
ok  	github.com/carlosframework/platform/internal/clamd	0.061s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
ok  	github.com/carlosframework/platform/internal/console	444.143s
?   	github.com/carlosframework/platform/internal/controlname	[no test files]
?   	github.com/carlosframework/platform/internal/controlsock	[no test files]
ok  	github.com/carlosframework/platform/internal/dbdoor	6.275s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.009s
ok  	github.com/carlosframework/platform/internal/dbverify	0.005s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.003s
ok  	github.com/carlosframework/platform/internal/docsite	0.009s
ok  	github.com/carlosframework/platform/internal/domains	2.461s
ok  	github.com/carlosframework/platform/internal/doorkey	0.006s
ok  	github.com/carlosframework/platform/internal/economics	0.004s
ok  	github.com/carlosframework/platform/internal/edge	45.108s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.008s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.134s
ok  	github.com/carlosframework/platform/internal/gate	1.665s
ok  	github.com/carlosframework/platform/internal/genselect	0.016s
ok  	github.com/carlosframework/platform/internal/geo	0.016s
ok  	github.com/carlosframework/platform/internal/geoip	0.030s
ok  	github.com/carlosframework/platform/internal/historyreadback	0.155s
ok  	github.com/carlosframework/platform/internal/host	194.052s
ok  	github.com/carlosframework/platform/internal/hostactors	6.235s
ok  	github.com/carlosframework/platform/internal/hostactors/awsmembership	0.004s
ok  	github.com/carlosframework/platform/internal/hostbox	2.649s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.559s
ok  	github.com/carlosframework/platform/internal/hostenv	0.004s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.005s
ok  	github.com/carlosframework/platform/internal/hostexec	4.836s
ok  	github.com/carlosframework/platform/internal/hostfence	0.031s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.506s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.015s
ok  	github.com/carlosframework/platform/internal/hostguard	0.996s
ok  	github.com/carlosframework/platform/internal/hostops	1.636s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.002s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.004s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.456s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.143s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.018s
ok  	github.com/carlosframework/platform/internal/infraview	0.017s
ok  	github.com/carlosframework/platform/internal/instancectl	5.245s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.740s
ok  	github.com/carlosframework/platform/internal/instancefs	0.020s
ok  	github.com/carlosframework/platform/internal/instancekey	0.015s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.021s
ok  	github.com/carlosframework/platform/internal/layout	1.598s
ok  	github.com/carlosframework/platform/internal/lbhealth	0.002s
ok  	github.com/carlosframework/platform/internal/lease	10.090s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.380s
ok  	github.com/carlosframework/platform/internal/ledger	0.011s
ok  	github.com/carlosframework/platform/internal/mail	0.173s
ok  	github.com/carlosframework/platform/internal/maintenance	0.084s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.524s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.065s
ok  	github.com/carlosframework/platform/internal/metrics	0.005s
ok  	github.com/carlosframework/platform/internal/moves	21.262s
ok  	github.com/carlosframework/platform/internal/moves/archive	0.795s
ok  	github.com/carlosframework/platform/internal/objstore	0.211s
ok  	github.com/carlosframework/platform/internal/ops	0.183s
ok  	github.com/carlosframework/platform/internal/park	0.212s
ok  	github.com/carlosframework/platform/internal/plans	0.005s
ok  	github.com/carlosframework/platform/internal/privatefile	0.004s
ok  	github.com/carlosframework/platform/internal/provision	32.642s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.008s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	5.432s
ok  	github.com/carlosframework/platform/internal/relay	7.238s
ok  	github.com/carlosframework/platform/internal/release	24.030s
ok  	github.com/carlosframework/platform/internal/rollout	3.722s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.004s
ok  	github.com/carlosframework/platform/internal/rootaws	0.007s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.058s
ok  	github.com/carlosframework/platform/internal/rootstate	0.026s
ok  	github.com/carlosframework/platform/internal/runs	0.584s
ok  	github.com/carlosframework/platform/internal/s3compat	0.005s
ok  	github.com/carlosframework/platform/internal/safefile	0.004s
ok  	github.com/carlosframework/platform/internal/schedule	2.564s
ok  	github.com/carlosframework/platform/internal/seeds	0.010s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	9.834s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	4.123s
ok  	github.com/carlosframework/platform/internal/status	0.063s
ok  	github.com/carlosframework/platform/internal/steering	0.006s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.291s
ok  	github.com/carlosframework/platform/internal/svcrole	0.002s
ok  	github.com/carlosframework/platform/internal/telemetry	0.004s
ok  	github.com/carlosframework/platform/internal/termfence	0.004s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.004s
ok  	github.com/carlosframework/platform/internal/unixdial	0.002s
ok  	github.com/carlosframework/platform/internal/updatewords	0.004s
ok  	github.com/carlosframework/platform/internal/usage	0.005s
ok  	github.com/carlosframework/platform/internal/version	0.002s
ok  	github.com/carlosframework/platform/internal/vet	1.554s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
linux/arm64 carlos binary: 49873058 bytes (ceiling 52000000)
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
  minio: downloading https://github.com/minio/minio/releases/download/RELEASE.2025-09-07T16-13-09Z/minio.linux-arm64.RELEASE.2025-09-07T16-13-09Z
  minio: downloaded, digest verified
  mc: downloading https://github.com/minio/mc/releases/download/RELEASE.2025-08-13T08-35-41Z/mc.linux-arm64.RELEASE.2025-08-13T08-35-41Z
  mc: downloaded, digest verified
  minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
  buckets and scoped user ready
=== RUN   TestDeliveredContractMinio
=== RUN   TestDeliveredContractMinio/put
=== RUN   TestDeliveredContractMinio/get_round-trips_the_body
=== RUN   TestDeliveredContractMinio/list_shows_the_key
=== RUN   TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN   TestDeliveredContractMinio/delete_removes_it
=== RUN   TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
    --- PASS: TestDeliveredContractMinio/put (0.00s)
    --- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
    --- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
    --- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
    --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
    --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
    --- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
    --- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok  	github.com/carlosframework/platform/internal/objstore	0.024s
=== RUN   TestT4StorageProbeMinio
2026/10/21 13:41:25 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18E08F2F5ABC9408, HostID: 7c420053e0fbcd7f41653fdd92ffa2802c903d05303b1aa15adc2e8bb65d183f, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
--- PASS: TestT4StorageProbeMinio (0.01s)
PASS
ok  	github.com/carlosframework/platform/internal/host	0.022s
minio contract: PASS
diff --git a/docs/superpowers/notes/census-proposal-moves-offline-precopy.md b/docs/superpowers/notes/census-proposal-moves-offline-precopy.md
index 285840b0d..f61fc041e 100644
--- a/docs/superpowers/notes/census-proposal-moves-offline-precopy.md
+++ b/docs/superpowers/notes/census-proposal-moves-offline-precopy.md
@@ -4,8 +4,10 @@ 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, ruled at 21cd69a4
-(after Astra's round 1 fixes, 692d41cf). It does two things. It runs a mirror
+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.
@@ -13,10 +15,10 @@ 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 in `TestPlan14RootReadCensus`, after scan-demand-revival, once
- scan-demand-revival's seal has been checked;
+ loaded last in `TestPlan14RootReadCensus`, 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 scan-demand-revival's inventory, which
+ (9B8) overlay on design-plans-and-caps' inventory, which
`TestPlan9B8RecordWriteCensus` now reads;
- one line in `internal/console/testdata/claim-routes.txt`.
@@ -45,24 +47,25 @@ 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`);
-- the fleet's edges files, for the target's coordinates (`offlineTarget`, the
- same helper the seal uses);
- this box's receipts object.
It then:
-- deletes `moves/<host>/` in the target's data bucket. It refuses a bare
- `moves/`, and never deletes in this box's own bucket. `offlineSealRevert`
+- deletes `moves/<host>/` in the staging bucket the stage door fixed on the
+ record (`move.stage_bucket`), through a mirror for `move.stage_region`
+ (round 2). It refuses a bare `moves/`, a missing bucket and this box's own
+ bucket, and in those cases acknowledges nothing. `offlineSealRevert`
already deletes the same prefix the same way;
- clears `move-receipts/<host>.json` in `cfg.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 and the target from
-the edges files: the operator names only a region code, and the tenant
-controls none of these paths.
+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)
+### 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.
@@ -114,30 +117,120 @@ the same one-move lock. That is not a new write site.
- **Tests.** Its claim-refusal fixture and its `svcroutes_test.go` exemption
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.StageBucket` and `StageRegion`, from the one target box's edges
+ file. The door refuses a source box's own bucket (MV-O191).
+ `provision.OfflineStage` takes a `StageTarget` and refuses an empty stager
+ or bucket. These are record fields written through the typed
+ `SetRecordMove` (`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.GetRecord` is an object-store
+ read, not a local file, and the scan finds no root-read unit in
+ `movesoffline_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 through `writeOfflineReport`, an existing
+ nonrecord site, and is called from `offlineSourceStep` in stopping through
+ landed.
+- **offlineUnstage deletes from the stored destination:** `moves/<host>/` in
+ `move.stage_bucket`, through a mirror for `move.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:** `PlacementView` gains `DataBucketRegion`, 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
-The two accepted-legacy rows add frozen declarations, so the legacy seal moves
-from `a21dba12…f262` (main, after scan-demand-revival) to
-`965fda6e58a16662833fcfe5435e9f45b525eb1abdd1f1f1715d611cb699bc81`. A removal
-is not in the seal. The test checks scan-demand-revival's seal before it loads
-this overlay. The seal was read from a temporary `t.Logf` of
+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
1. offlineSourceRevert and offlineUnstage: ACCEPTED. Object-store reads only,
- on the pinned stager only; there is no root read of an operator- or
- tenant-controlled path.
+ 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.
2. offlineBegin: ACCEPTED (J4, as OfflineStart). OfflineStart is retired by
the H3 removal, since it is behaviour-identical.
3. offlineBegin's `file:Open@198` is `rec.ActiveRun.Open()`: ACCEPTED.
-4. The six envelopes: ACCEPTED, re-pinned at 21cd69a4.
+4. The envelopes: ACCEPTED, re-pinned on the merged tree (981294cf with main
+ f550afa2), internal/edgesfile included.
5. MirrorPrefixKeep: ACCEPTED at versioned-transport. Its record writes still
go through the book.
6. registerCLIRoutes: ACCEPTED at key-site.
7. The stage route: ACCEPTED. It is operator-only, refused with MV-O20 on
claims consoles, and has its fixture.
+8. offlineTakeSeal (round 2): ACCEPTED, re-frozen at J1 and nonrecord in
+ 9B8. It adds one refusal and no read or write.
+9. The round 2 record fields and reads (`StageBucket`, `StageRegion`, the
+ record re-reads, the stop acknowledgement): ACCEPTED. The fields go through
+ `SetRecordMove`, the reads go to the object store, and the report write
+ goes through `writeOfflineReport`.
+10. 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`, and the receipts
-preload.
+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).
diff --git a/internal/provision/moves_offline_precopy_census_test.go b/internal/provision/moves_offline_precopy_census_test.go
index d65857ffe..cb47a8312 100644
--- a/internal/provision/moves_offline_precopy_census_test.go
+++ b/internal/provision/moves_offline_precopy_census_test.go
@@ -6,9 +6,14 @@ package provision
// coordinator, as census owner for this branch (2026-10-21), accepted as
// classified (docs/superpowers/notes/census-proposal-moves-offline-precopy.md).
//
-// Two sites change and keep their class; no site is added.
+// Three sites change and keep their class; no site is added.
// registerCLIRoutes gains the stage door's route line beside the six
-// offline doors (key-site, main's). MirrorPrefixKeep plans its skips first
+// offline doors (key-site, main's), pinned on the merged tree with
+// design-plans-and-caps' two account plan doors. offlineTakeSeal
+// (nonrecord, census-ruling-moves-offline's) gains one refusal before its
+// mirror after Astra's round 2: a staged move seals only into the staging
+// bucket and region fixed on the record; its Puts and their keys under the
+// move's moves/@offline/ prefix are unchanged. MirrorPrefixKeep plans its skips first
// and then runs the copies and, after every copy has landed, the deletes on
// up to DefaultMirrorWorkers goroutines; it still writes only the
// destination prefix (Copy or CopyVersioned, Delete) and the receipts
@@ -18,8 +23,9 @@ package provision
// start whenever the round copies anything, so set and drop touch memory
// only and no request is made under the mutex; the write set is unchanged.
var movesOfflinePrecopyWriteCensusOverlay = []censusEntry{
- {"internal/console/authcli.go", "Server.registerCLIRoutes", "key-site", "lit:instances,lit:sidecars", "2aee959d5c58a81b47f10629f6954b3d0b6d6860f999d9fe9eead9155f12c459"},
+ {"internal/console/authcli.go", "Server.registerCLIRoutes", "key-site", "lit:instances,lit:sidecars", "a0aff01b7e2f4bb6fd23e8b54d41d90f1cd4171822f9defb9bd16cb2a08e2e87"},
{"internal/lease/mirror.go", "MirrorPrefixKeep", "versioned-transport", "sel:CopyVersioned", "dff4d03fa4e56219c5a3e08862f24ad2a6bce7a7e0df806571fa52e8fb29fb19"},
+ {"internal/host/movesoffline_seal.go", "movesWorker.offlineTakeSeal", "nonrecord", "sel:Put", "f606873fb09e8644d449a36ae64cecbb84b2f83791d4ebec4cb491d3d3f7cdaa"},
}
// movesOfflinePrecopyWriteCensusInventory is design-plans-and-caps' inventory
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go
index f4e65c2ed..4fbe08f89 100644
--- a/internal/provision/plan14_root_reads_test.go
+++ b/internal/provision/plan14_root_reads_test.go
@@ -1070,15 +1070,16 @@ func TestPlan14RootReadCensus(t *testing.T) {
// (docs/superpowers/notes/census-proposal-moves-offline-precopy.md).
// offlineSourceRevert is re-frozen at its disposition, offlineBegin
// (OfflineStart's body) is new accepted legacy, and OfflineStart is
- // retired by an H3 census-ruling removal. Two frozen declarations are
- // added, so the seal moves; design-plans-and-caps' is checked first.
+ // retired by an H3 census-ruling removal; offlineTakeSeal is re-frozen
+ // for Astra's round 2. Three frozen declarations are added, so the seal
+ // moves; design-plans-and-caps' is checked first.
if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 {
t.Fatal(errs)
}
if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-moves-offline-precopy.tsv"), &inv, true); err != nil {
t.Fatal(err)
}
- seal = "8ef09f416d7a7acbb5f92791feb5e3c69bd3ef86cb3a72610faa136dd1941a4d"
+ seal = "940ed1bba8d911e0f57af83ecac253c77ba5e14dad590258e0ee03ec98ccb818"
errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...)
errs = append(errs, checkRootReadDeclarations(got, inv)...)
if len(errs) > 0 {
diff --git a/internal/provision/testdata/plan14-root-reads-moves-offline-precopy.tsv b/internal/provision/testdata/plan14-root-reads-moves-offline-precopy.tsv
index a58345c27..4688b7ec8 100644
--- a/internal/provision/testdata/plan14-root-reads-moves-offline-precopy.tsv
+++ b/internal/provision/testdata/plan14-root-reads-moves-offline-precopy.tsv
@@ -1,18 +1,22 @@
-# Census overlay moves-offline-precopy (parallel copies in lease.MirrorPrefix and the carlos move offline stage door, an offline-staging record state), loaded LAST, after scan-demand-revival. RULED as census-ruling-moves-offline-precopy (the coordinator, as census owner for this branch, 2026-10-21), accepted as proposed: every row is rev3=census-ruling-moves-offline-precopy. The ruling, with each unit's executing identity: docs/superpowers/notes/census-proposal-moves-offline-precopy.md.
+# Census overlay moves-offline-precopy (parallel copies in lease.MirrorPrefix and the carlos move offline stage door, an offline-staging record state), loaded LAST, after design-plans-and-caps. RULED as census-ruling-moves-offline-precopy (the coordinator, as census owner for this branch, 2026-10-21), accepted as proposed, re-ruled for Astra's rounds 1 and 2 and the merge of main f550afa2: every row is rev3=census-ruling-moves-offline-precopy. The ruling, with each unit's executing identity: docs/superpowers/notes/census-proposal-moves-offline-precopy.md.
# Every unit here runs in the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos) or the console (user carlos-console) and makes no 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).
-# movesWorker.offlineSourceRevert re-frozen with its new digest: it calls offlineUnstage, which reads only the object store; its read sites are unchanged and one offset moved. offlineBegin is new: OfflineStart's body, moved unchanged with the state as a parameter; its one site is rec.ActiveRun.Open(), a method, not a file read. OfflineStart is retired by an H3 census-ruling removal (the carlos-run getJSON form); a removal is not in the seal. Two frozen declarations are added, so the seal moves.
-# Round 1 (Astra P2s, 692d41cf): the stage door pins the stager on the record (MoveState.Stager, written through SetRecordMove like every other move field); offlineUnstage cleans whenever staged_at is set, on the pinned stager only; the console finish of a staged move waits for that stager's reverted report; MirrorPrefixKeep reads the receipts book before its workers start. No unit's read sites change; offlineSourceRevert's basis names offlineUnstage's reads as they now are; the envelopes below are pinned at 21cd69a4.
-# Envelopes re-pinned: . (move.go: the stage subcommand, its status and abort lines; no read), internal/console (the stage door, offlineBeginChecks, the status view; no read site but offlineClaimEvidence's, unchanged), internal/host (movesoffline_stage.go, new, reads only the object store; edgesfile.go publishes offline-stage-1; the box-side stager election is gone, only the pinned box stages or cleans; offlineRound's staging branch), internal/lease (mirror.go: the round's copies and deletes run side by side; forEachParallel), internal/moves (the stager role, staged phase, report fields, OfflineStager) and internal/provision (offline_move.go: OfflineStage, which needs a stager, OfflineRestage, OfflineStartStaged; provision.go: MoveState.StagedAt and MoveState.Stager).
-@legacy 20c1c784429a572d5bd14513c5905d569e06ab0c4007922bf3383816b61636a9 internal/host/movesoffline.go movesWorker.offlineSourceRevert legacy accepted-legacy accepted-legacy;rev3=census-ruling-moves-offline-precopy;basis=root-route (J1, census-ruling-moves-offline's disposition); the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos), on abort: census-ruling-moves-offline's body (reads the row's hold and releases only this move's hold, under reconcileMu), now calling offlineUnstage before it reports reverted. offlineUnstage acts only for a move carrying staged_at and only on the stager the stage door pinned on the record (move.stager); it reads only the object store (its own stager report, the fleet's edges files for the target's coordinates, this box's receipts object) and deletes the target's moves/<host>/ staging prefix (never in this box's own bucket) and this box's move-receipts/<host>.json, then writes its stager report reverted; it reads no local file and makes no root decision. Read sites unchanged; one offset moved (whole:Route@701 to @843) registry:HoldOp@562,whole:Route@549,whole:Route@843 b917ac2d1976eec14839ed65e3e562327d54af956e86f5218ed011731182aa1b
-internal/host/movesoffline.go movesWorker.offlineSourceRevert legacy accepted-legacy accepted-legacy;rev3=census-ruling-moves-offline-precopy;basis=root-route (J1, census-ruling-moves-offline's disposition); the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos), on abort: census-ruling-moves-offline's body (reads the row's hold and releases only this move's hold, under reconcileMu), now calling offlineUnstage before it reports reverted. offlineUnstage acts only for a move carrying staged_at and only on the stager the stage door pinned on the record (move.stager); it reads only the object store (its own stager report, the fleet's edges files for the target's coordinates, this box's receipts object) and deletes the target's moves/<host>/ staging prefix (never in this box's own bucket) and this box's move-receipts/<host>.json, then writes its stager report reverted; it reads no local file and makes no root decision. Read sites unchanged; one offset moved (whole:Route@701 to @843) registry:HoldOp@562,whole:Route@549,whole:Route@843 b917ac2d1976eec14839ed65e3e562327d54af956e86f5218ed011731182aa1b
+# movesWorker.offlineSourceRevert re-frozen with its new digest: it calls offlineUnstage, which reads only the object store; its read sites are unchanged and one offset moved. offlineBegin is new: OfflineStart's body, moved unchanged with the state as a parameter; its one site is rec.ActiveRun.Open(), a method, not a file read. OfflineStart is retired by an H3 census-ruling removal (the carlos-run getJSON form); a removal is not in the seal.
+# Round 1 (Astra, 692d41cf): the stage door pins the stager on the record (MoveState.Stager, written through SetRecordMove like every other move field); offlineUnstage cleans whenever staged_at is set, on the pinned stager only; the console's finish of a staged move waits for that stager's reverted report; MirrorPrefixKeep reads the receipts book before its workers start.
+# Round 2 (Astra, 0a6bb375): the stage door also fixes the staging destination on the record (MoveState.StageBucket, StageRegion, from the one target box's edges file, refusing a source box's own bucket, MV-O191); the stage round, the seal's hand-over (offlineTakeSeal, re-frozen here) and an abort's cleanup use only those; the stager re-reads the record (provision.GetRecord, the object store) before a round and before its delete phase (stageDeleteGate) and acknowledges it has stopped (offlineStagerStopped, its own stager report through writeOfflineReport); the console's start and seal doors wait for that acknowledgement and refuse a missing stager (MV-O190) or changed coordinates (MV-O192). No new root read or record write. Three frozen declarations in all, so the seal moves.
+# Envelopes re-pinned on the merged tree (main f550afa2 with this branch): . (move.go: the stage subcommand, its status and abort lines; no read), internal/console (the stage door, offlineBeginChecks, the stager pin and destination, the seal and start barriers, the finish gate, the status view; no read site but offlineClaimEvidence's, unchanged), internal/edgesfile (PlacementView reads data_bucket_region; no read site), internal/host (movesoffline_stage.go reads only the object store; edgesfile.go publishes offline-stage-1; the box-side stager election is gone, only the pinned box stages or cleans; offlineRound's staging branch), internal/lease (mirror.go: the round's copies and deletes run side by side; forEachParallel; the book read up front), internal/moves (the stager role, staged phase, report fields, OfflineStager) and internal/provision (offline_move.go: OfflineStage, which needs a StageTarget, OfflineRestage, OfflineStartStaged; provision.go: MoveState.StagedAt, Stager, StageBucket, StageRegion).
+@legacy e7c1242684b2da24edf4c9f56545d1208bd8433562aa494a911dda42e55d3452 internal/host/movesoffline.go movesWorker.offlineSourceRevert legacy accepted-legacy accepted-legacy;rev3=census-ruling-moves-offline-precopy;basis=root-route (J1, census-ruling-moves-offline's disposition); the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos), on abort: census-ruling-moves-offline's body (reads the row's hold and releases only this move's hold, under reconcileMu), now calling offlineUnstage before it reports reverted. offlineUnstage acts only for a move carrying staged_at and only on the stager the stage door pinned on the record (move.stager); it reads only the object store (its own stager report, this box's receipts object) and deletes moves/<host>/ in the staging bucket and region the stage fixed on the record (move.stage_bucket, move.stage_region; it refuses a missing one or this box's own bucket and then acknowledges nothing) and this box's move-receipts/<host>.json, then writes its stager report reverted; it reads no local file and makes no root decision. Read sites unchanged; one offset moved (whole:Route@701 to @843) registry:HoldOp@562,whole:Route@549,whole:Route@843 b917ac2d1976eec14839ed65e3e562327d54af956e86f5218ed011731182aa1b
+internal/host/movesoffline.go movesWorker.offlineSourceRevert legacy accepted-legacy accepted-legacy;rev3=census-ruling-moves-offline-precopy;basis=root-route (J1, census-ruling-moves-offline's disposition); the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos), on abort: census-ruling-moves-offline's body (reads the row's hold and releases only this move's hold, under reconcileMu), now calling offlineUnstage before it reports reverted. offlineUnstage acts only for a move carrying staged_at and only on the stager the stage door pinned on the record (move.stager); it reads only the object store (its own stager report, this box's receipts object) and deletes moves/<host>/ in the staging bucket and region the stage fixed on the record (move.stage_bucket, move.stage_region; it refuses a missing one or this box's own bucket and then acknowledges nothing) and this box's move-receipts/<host>.json, then writes its stager report reverted; it reads no local file and makes no root decision. Read sites unchanged; one offset moved (whole:Route@701 to @843) registry:HoldOp@562,whole:Route@549,whole:Route@843 b917ac2d1976eec14839ed65e3e562327d54af956e86f5218ed011731182aa1b
+@legacy 06eb20ca59fc8172dfa9ef5bc6872987974b44f08202417705566816ecaeaf9f internal/host/movesoffline_seal.go movesWorker.offlineTakeSeal legacy accepted-legacy accepted-legacy;rev3=census-ruling-moves-offline-precopy;basis=root-route (J1, census-ruling-moves-offline's disposition); the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): census-ruling-moves-offline's body (seal steps 1 to 9 from the row: database path, next due, replication rung and timer rows, read for the seal) with one refusal before the mirror: a staged move (staged_at set) is sealed only into the staging bucket and region the stage fixed on the record (move.stage_bucket, move.stage_region), a comparison of record and edges-file fields that reads and writes nothing new. Read sites unchanged; every offset after the check moved by 487 bytes registry:DBPath@2621,registry:NextDue@12050,registry:NextDue@12962,registry:Replication@11955,registry:Replication@12937,whole:Route@10690,whole:Route@1713,whole:Route@2682,whole:Route@3463,whole:Route@5452,whole:Route@8226,whole:Timer@9377,whole:Timer@9442,whole:Timer@9448,whole:Timer@9707,whole:Timer@9806 f606873fb09e8644d449a36ae64cecbb84b2f83791d4ebec4cb491d3d3f7cdaa
+internal/host/movesoffline_seal.go movesWorker.offlineTakeSeal legacy accepted-legacy accepted-legacy;rev3=census-ruling-moves-offline-precopy;basis=root-route (J1, census-ruling-moves-offline's disposition); the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): census-ruling-moves-offline's body (seal steps 1 to 9 from the row: database path, next due, replication rung and timer rows, read for the seal) with one refusal before the mirror: a staged move (staged_at set) is sealed only into the staging bucket and region the stage fixed on the record (move.stage_bucket, move.stage_region), a comparison of record and edges-file fields that reads and writes nothing new. Read sites unchanged; every offset after the check moved by 487 bytes registry:DBPath@2621,registry:NextDue@12050,registry:NextDue@12962,registry:Replication@11955,registry:Replication@12937,whole:Route@10690,whole:Route@1713,whole:Route@2682,whole:Route@3463,whole:Route@5452,whole:Route@8226,whole:Timer@9377,whole:Timer@9442,whole:Timer@9448,whole:Timer@9707,whole:Timer@9806 f606873fb09e8644d449a36ae64cecbb84b2f83791d4ebec4cb491d3d3f7cdaa
@legacy 5e6a7aa9d23e5559c9b93f3a5c038e019a9a5d3bbcfbcf9229725ce7682b936a internal/provision/offline_move.go offlineBegin legacy accepted-legacy accepted-legacy;rev3=census-ruling-moves-offline-precopy;basis=root-input (J4, as OfflineStart); the console (cmd/console serve under console.service, user carlos-console): new, OfflineStart's body moved unchanged with the state as a parameter (offline-stopping for OfflineStart, behaviour-identical; offline-staging plus staged_at for OfflineStage), the record transition the start and stage doors apply through SetRecordMove; its one site is rec.ActiveRun.Open(), a method on the record's run state (RunState.Open), not a file read file:Open@198 66e0216314538687d102a4cee1089a19896c7693260f748a046a5ff16e76dc1e
internal/provision/offline_move.go offlineBegin legacy accepted-legacy accepted-legacy;rev3=census-ruling-moves-offline-precopy;basis=root-input (J4, as OfflineStart); the console (cmd/console serve under console.service, user carlos-console): new, OfflineStart's body moved unchanged with the state as a parameter (offline-stopping for OfflineStart, behaviour-identical; offline-staging plus staged_at for OfflineStage), the record transition the start and stage doors apply through SetRecordMove; its one site is rec.ActiveRun.Open(), a method on the record's run state (RunState.Open), not a file read file:Open@198 66e0216314538687d102a4cee1089a19896c7693260f748a046a5ff16e76dc1e
# OfflineStart retired (H3, the carlos-run form). A removal is not in the seal.
@remove internal/provision/offline_move.go OfflineStart
@removed internal/provision/offline_move.go OfflineStart H3 census-ruling-moves-offline-precopy: still declared, and reads nothing now: its body and its one site (rec.ActiveRun.Open(), a method, not a file read) moved unchanged into offlineBegin, an accepted-legacy row in this overlay; OfflineStart calls offlineBegin with offline-stopping and is behaviour-identical. It runs in the console as carlos-console.
-@coverage . 44fc0f3ea061670389aae27fb0ef47397adecba16951a173a4b0bbbe728a8bdd
-@coverage internal/console 6ae90250ebff821c8e64d0e64827860ddf80213177a86dc9cba6db9ec68592d6
-@coverage internal/host e9234db89d1fc6b9a966c8a0c66b983c4e4bb18a7c8222ccbf91a46318c98f47
+@coverage . 649bb6a5f474312600edb213fb76a1a6444c6ff37a39412d1bbb04eeed49eabd
+@coverage internal/console 7bdd3caafb3a474a1bb3797c26c5a38e76b11ca7f0a46cd9c3a6af8ca62781c9
+@coverage internal/edgesfile 716924380656f85e428541c170e8c5bab9f0ad9c8abe931488f7a44e7953811e
+@coverage internal/host d58c8e606d61557dce896f82fc41c06969d99fd1e097b863aa986e0fa416e219
@coverage internal/lease 980f38c4804ae1485c0c0df77bade8990b1d0248c0f44e41e0194c7731562a14
@coverage internal/moves 33c7b507f13b4f4081c3bdb0a84e31d875e2cf1be61b480a7ca856193125e071
-@coverage internal/provision 8c86869568a559329a0d8dcbc1df5cac18b4bcabd5ca3b4f403949ebd56f4b64
+@coverage internal/provision 210b174030fa650ef7345999643ef7e98add1a24378802cc134f9dea64d234dc