carlos / platform Public

harness: Run 9 (B) moveapp moved PASS (366 s offline, ~23 s of steps), defect 8 (source scheduler re-creates the env after open), nesting deferred

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 72022e7f3e2ba18975e5242439f05cbce4e70835 parent 624ff04
1 file changed, +62 −0
  • harness/RUNLOG.md +62 −0

CI cancelled — superseded by a newer push — run details

CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok  	amadan.net/carlos/platform.git/launchctx	0.012s
?   	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

amadan: job cancelled — a newer push superseded it
diff --git a/harness/RUNLOG.md b/harness/RUNLOG.md
index d4b8814e7..b2dea7040 100644
--- a/harness/RUNLOG.md
+++ b/harness/RUNLOG.md
@@ -1751,3 +1751,65 @@ isn't there. (My first flip came 2 min 40 s after the seal because my own proces
`flip` and `open` straight after the previous door, or retry them every 15 s until the next pass. The script's `MVR`
does that and logs each try.
+Resumed at flip (`r9-move.host.sh moveapp move resume 2026-10-20T17:20:31.070Z`), after the 17:25:43Z timed pass had
+rewritten the reports:
+
+| step | at | took | result |
+|---|---|---|---|
+| flip | 17:26:22.520Z | 4.3 s (try 1) | `Stopped in ie. Starting in uk.` / `uk holds everything moveapp.dev.harness.test needs. Nothing is served yet. Run carlos move offline open ...` exit 0. Dennis: row `landing`, region uk, **flat** `/data/moveapp.dev.harness.test.db`; `<host>.env` `27ff170a...` (= sophie's byte for byte), instance key sha256 `067b3156...` (same digest), `config.env` with all seven names (the store binding), files `instance.key` `95b88862...` (same), timers `every-minute` and `once-1` (due 17:37:43Z) carried |
+| open | 17:26:27.150Z | 4.2 s (try 1) | `moveapp.dev.harness.test is served from uk. Run carlos move offline finish ... once ie has cleaned up.` exit 0. Sophie parks: `env file absent`, `parked: .../source`; fence lifted 17:26:28Z |
+| first request on dennis | 17:26:31.352Z | 5.7 s | 404 at 17:26:31.45Z (not ready yet), **200 at 17:26:37.054Z** (2.5 s request: the first wake plus root's stage pass) |
+| backhaul | 17:26:37Z | | through sophie's edge 200 (34 ms), through leo's 200 (24 ms) (H13b) |
+| finish | 17:26:39.342Z | 262.0 s (try 1; the CLI waits) | `Waiting for ada-letterpress to clean up ...` then **`Moved. moveapp.dev.harness.test is served from uk. It was offline for 356 seconds.`** `Move mv_twameyccfp7ryimabnjg32ltla of moveapp.dev.harness.test is done.` exit 0, at 17:31:01.377Z. Sophie 17:30:59Z `removed retired instance route ... (record gone)`, `tombstone lifted` |
+
+**Downtime budget.** Offline from start (17:20:31.070Z) to the first 200 on dennis (17:26:37.054Z): **366 s**; the
+console's own figure is 356 s. Of that, 2 min 40 s was my late first flip and about 1 min 10 s was waiting for the
+5-minute pass (finding 7). The work itself: start 2.2 s, seal 6.1 s, flip 4.3 s, open 4.2 s, first request 5.7 s:
+**about 23 s of steps**, plus operator time between them. For a budget: 25 s of steps per 4 MB instance, plus up to
+6 minutes for each of flip and open if the reports have gone stale. `finish` adds 4 to 5 minutes but nothing is
+offline by then.
+
+**B3. Verification on dennis: PASS on every item**
+- **Rows:** dennis `/report` (17:31:35Z): `bg` home seq 1-2046 then uk 2047-2644; `worker` home 1-1024 then uk
+ 1025-1323; `tick` home 17 then uk 6. Contiguous: no row lost or repeated. 2046 > the 2042 read at 17:20:28Z: the
+ four rows written between that read and the stop arrived too (`db-tail`: everything at or below TXID 117).
+- **Instance key:** `platform_key_sha256 067b3156...` on dennis = sophie's before; `<host>.env` byte-equal
+ (`27ff170a...`). Files: `file_key_sha256 c5a85dd6...` unchanged (`instance.key` `95b88862...`).
+- **config.env with the store binding:** all seven names on dennis; the app wrote and read back
+ `probe/0456deb532974a46.txt` (`... region uk at 2026-10-20T17:26:36Z`) with the store user; `has_secret true`.
+- **Timers:** both rows carried; `carlos schedule ls`: `moveapp.dev.harness.test (box dennis) every-minute ... → 200`.
+ The ticks due while the instance was offline (17:21-17:26Z) did not fire (refused `maintenance fence active`, then
+ gone with the row), as the plan's Risks say. once-1: see below.
+- **Source parked, never deleted:** `/data/.offline-moves/moveapp.dev.harness.test/mv_twameyccfp7ryimabnjg32ltla/`
+ `sealer/`, `source/instance/`, `source/moveapp.dev.harness.test.json`; the parked report leaves
+ `/data/instances/<host>` (the mount view), `/data/.instances/<host>` (empty) and `/opt/carlos/live/<host>`
+ (root-owned, `-> /opt/carlos/versions/mp1/moveapp`) in place, as the plan says.
+- **Tombstone lifted at finish:** sophie `tombstone lifted` 17:30:59Z; the sealer's report phase `lifted`; data-ie
+ `instances/<host>/lock.json` is now `{"generation":1,"expires_at":"1970-01-01T00:00:00Z","owner":"moved:mv_..."}`:
+ lifted by an expired rewrite, not a delete (the object and its `moved:` owner remain). data-uk's lease is dennis's
+ (`dennis:215`, 15-minute expiry).
+- **Record:** region `uk`, `move` null, `last_move {"id":"mv_twameyccfp7ryimabnjg32ltla","outcome":"done",
+ "offline_s":356.08}`.
+
+**Defect 8 (code): after `open`, the source box's scheduler re-creates the parked instance's env file.** At
+17:27:04Z (the first every-minute tick after open), sophie wrote `/data/.instances/moveapp.dev.harness.test/` and in it
+a new `moveapp.dev.harness.test.env` (carlos 600, 84 bytes) holding only a fresh `CARLOS_ADMIN_TOKEN`. Its row was
+still in sophie's registry (`moving`, kept until finishing) with its timers, and open had lifted the fence, so the
+runner's `scheduleToken` ran `activator.EnsureAdminToken` on the nested env path
+(`internal/host/schedule_wiring.go:154-180`; the runner's call at `internal/schedule/runner.go:502-505`) before any
+serve check refused the held row. The row and its timers went at finishing (17:30:59Z); the file stays, outside the
+park directory, contradicting the parked report's "the instance directory stays, empty". Impact: a stray secret for a
+host that no longer lives there, and a non-empty instance directory a later return move's landing will meet. Fix
+direction: the runner must not ensure a token for a row that is held (status `moving`/`landing`), or open must drop
+the source's timers when it parks.
+
+### B4. The post-open nesting step: dennis defers it (outline P0.7 holds only for an idle app)
+
+After the first request the row is **flat** on dennis (`/data/moveapp.dev.harness.test.db`), as the outline says.
+Dennis's layout pass: 17:26:30Z `refused -> db is missing at /data/moveapp.dev.harness.test.db` (before the first
+wake), then 17:26:40Z and every pass since `deferred until asleep -> moveapp.dev.harness.test (awake (stopped for the
+move in 1h0m0s unless it sleeps first))`. The pass nests only an instance that is asleep, or stops it once a 1-hour
+grace runs out (`internal/layout/pass.go:318-322`); an app with a 1-minute schedule never reaches its 15-minute idle.
+`carlos status --layout`: `noor-marquetry (nested by default) moveapp.dev.harness.test flat` and the warning "runs
+nested instances beside flat ... Nested isolation on this box is not in force until none of those remains".
+