carlos / platform Public
Census: the offline move's harness fixes (Run 9, defects 6 to 8)
Status: RULED. Every row carries rev3=PROPOSED-moves-offline-fixes. The ruling is asked of the census owner, platform-1c.
The branch is moves-offline-fixes, from main after 54ae7fb9 (v0.28.5). It fixes three defects the deployment harness found in the offline operator move (harness RUNLOG, Run 9). It adds one census overlay, internal/provision/testdata/plan14-root-reads-moves-offline-fixes.tsv, loaded last in TestPlan14RootReadCensus, after armed-box-updates. The record-write census (TestPlan9B8RecordWriteCensus) does not change: no record write is added or moved.
The footing
Every unit in the overlay runs in the box agent (/opt/carlos/carlos agent, carlos-edge.service, User=carlos). None is reached from a root process, and none makes a root decision. So each is proposed accepted legacy at the disposition main gives it or its siblings: J1 (root-route) for registry and route reads, J4 (root-input) for file reads, J1,J4 where a unit does both. That is the footing census-ruling-moves-offline accepted for the offline move's own units.
The units, by defect
Defect 6: config.env for a nested row whose instance directory is not made yet
| unit | change | executing identity | disposition | |---|---|---|---| | internal/appconfig/syncexec.go SyncExecWithCallbacks | changed legacy: a write that found no instance directory is named and skipped (errInstanceDirAbsent, WhenInstanceDirAbsent), and the pass goes on; no new read site | the agent, through syncExecSerialized (config loops) | re-frozen, J1,J4 | | internal/appconfig/syncexec.go renderExecConfigBefore | changed legacy: one Lstat of the nested row's instance directory after its write failed, to tell "not made yet" from any other failure; follows no link | the agent: config sync and the moves worker's held render | re-frozen, J1,J4 | | internal/host/agent.go configGenLoop | changed legacy: the tick's body moved into configGenTick; the registry listing stays | the agent | re-frozen, J1 | | internal/host/agent.go configGenTick | new: configGenLoop's tick over the routes it listed; also runs the agent's own config pass once a directory the last pass waited for exists | the agent | accepted legacy, J1 (as configGenLoop) | | internal/host/agent.go pendingDirs.appeared | new: an Lstat (never follows a link) of the instance directories the last config pass could not write into | the agent | accepted legacy, J4 (as the moves units' isDir, lexists) |
Envelopes re-pinned: internal/appconfig, internal/host.
Defect 7: fresh reports at every door step
No unit row. The re-assert (movesWorker.offlineReassert, run by offlineReassertLoop in the agent every 15 s or so) reads the record from the deployment store and calls the source and sealer steps census-ruling-moves-offline already classified (offlineSourceStop, offlineKeepStop); it has no root read of its own. The console's stale-report refusal (MV-O178) reads nothing new. Envelopes re-pinned: internal/console, internal/host.
Defect 8: the source's scheduler wrote the moved host's env file after open
The schedule runner is built only by the agent (armSchedule, from runAgent); no root process makes one.
| unit | change | executing identity | disposition | |---|---|---|---| | internal/host/schedule_wiring.go newScheduleDeps | changed legacy: the runner's Skip is set to scheduleSkip; no new read site | the agent | re-frozen, J1 | | internal/host/schedule_wiring.go scheduleSkip | new: reads a row's status (any hold) and host (the armed offline view says the record names another region) to tell the runner to leave the host alone; it only withholds a tick | the agent's schedule runner | accepted legacy, J1 (as the runner's own hold checks) | | internal/schedule/runner.go Runner.Tick | changed legacy: a host Skip names gets no tick and no bookkeeping at the pre-filter | the agent's schedule runner | re-frozen at census-ruling-seeded-instances' disposition | | internal/schedule/runner.go Runner.fire | re-pinned: with Skip set, the row is read again before the token, and a skipped host gets no token and no record | the agent's schedule runner (overlay e names the service controller and root callbacks for the claim design; neither sets Skip) | hint, E2, unchanged |
Envelopes re-pinned: internal/host, internal/schedule.
Fix round 1 (Astra's review)
| finding | unit | change | executing identity | disposition | |---|---|---|---|---| | 1 | internal/schedule/runner.go Runner.Tick | re-pinned: its due query moved into Runner.dueRows | the agent's schedule runner | re-frozen, as above | | 1 | internal/schedule/runner.go Runner.dueRows | new: held hosts left out by the query when Skip is set, and hosts Skip names paged past before the limit (one row read per host in the batch) | the agent's schedule runner | accepted legacy, J1 (as Runner.Tick) | | 2 | none | the refresh calls offlineSealerRefresh (seal, lease and source reports read, only the report written, the lease never), no root read of its own | the agent | envelope internal/host only | | 3 | internal/appconfig/syncexec.go SyncExecWithCallbacks | re-pinned: names each in-scope host done or not once the pass has gone through every route (WithHostOutcome); no new read site | the agent | re-frozen, J1,J4 | | 3 | internal/host/agent.go pendingDirs.appeared | re-pinned: the same Lstat, with a retry backoff for a host a pass left undone | the agent | re-frozen, J4 |
The seal
The accepted-legacy rows add frozen declarations, so the legacy seal moves, from 393e7f45…bec29 (main, after moves-offline and armed-box-updates) to edc04f7349acf2b9ea98ffc8a1d3b71516a6ddb95505abfc2e38e49fd802ccc2 (after fix round 1). The test checks main's seal before it loads this overlay, so a change on main's side still shows on its own.
What the ruling is asked to decide
- That every unit above runs only in the agent and makes no root decision, so accepted legacy at main's disposition is the right footing.
- That
Runner.firekeeps its class and task (hint, E2): the change only withholds a token and a record for a hostSkipnames. - That the re-assert of defect 7 needs no row: it calls only steps already classified under census-ruling-moves-offline.
Ruling (platform-1c, 2026-10-20): census-ruling-moves-offline-fixes, ACCEPTED as proposed
Accepted at d75b7e0b (F1 to F4) and re-ruled for fix round 1 at 7cd5db4d (F5 to F7). After Astra's scoped re-review, the backoff counter saturates (agent.go pendingDirs.record): an internal/host envelope re-pin only. The rev3 tag was rewritten to the ruling's name, so the seal moves to 826c54be…a62f.
- After merging main 21850356 (install-console-upgrade, loaded before this overlay): internal/host envelope re-pinned to the combined tree; seal unchanged.