carlos / platform Public

docs: rehearsal and index for moves into home (home part 2)

Task 6. The rehearsal runbook:

- P-0: moves into home need part 2 merged and rolled, boxes first, then
  consoles; the door refuses them without host claims (MV-N510); the
  plan's rollout order (Plan 13, part 2, the IAM mesh with the home
  bucket, claim activation, the switch per run).
- P-0, new: one placementless edges file per deployment, and Ruling R2's
  five-step procedure for carlos-dev-ie-b, copied from the plan; it
  applies to every dev run that touches home, Run 1 included.
- P-2: the home data bucket in the staging mesh, recorded, not the
  deployment bucket.
- Run 1b (new): Run 1's host moved back into home (Ruling R6), with the
  lane, touched (ie, @home or both, nothing removed), quiet_after and
  parked-residue checks, the door's refusals named, and Ruling R7's note
  that the landing can run on the console's own box.
- Runs 2 and 3 run as written once part 2 is rolled; the out-of-home
  arrangement stays the fallback. Run 4 stays out of home and gates on
  Runs 1, 1b, 2 and 3. Run 6 gates on dev Runs 1 and 1b.
- Sources: this plan replaces the MV-N21 and moves-home-parks rows (merge
  commit left blank until the merge); loadMoveFleet cited for the
  placementless-box item.

The index gains a home part 2 row after Plan 7.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 4145ab7ef7c26a53e3f9b863b61e3fefd3601a8f parent 3ada18d
2 files changed, +175 −31
  • docs/superpowers/plans/2026-10-06-moves-phase1-0-index.md +1 −0
  • docs/superpowers/plans/2026-10-06-moves-phase1-rehearsal.md +174 −31

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.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

amadan: job cancelled — a newer push superseded it
diff --git a/docs/superpowers/plans/2026-10-06-moves-phase1-0-index.md b/docs/superpowers/plans/2026-10-06-moves-phase1-0-index.md
index 0b5a5552e..6b124c839 100644
--- a/docs/superpowers/plans/2026-10-06-moves-phase1-0-index.md
+++ b/docs/superpowers/plans/2026-10-06-moves-phase1-0-index.md
@@ -36,6 +36,7 @@ the same branch; its witness names are cited in the tests the plans port.
| 5 | [`2026-10-06-moves-phase1-5-cleanup-and-parks.md`](2026-10-06-moves-phase1-5-cleanup-and-parks.md) | the paging primitive, abort with the target-owned staging park, the staging horizon as a persisted `ParkScope`, the finalization parks and the release to `serving` (K8), the `moves/<host>` sweep scope, the db door's refusal on move kinds, the console's claim-half recovery pass and deadline sweeps | the acknowledgement after the horizon of every chore is C4's runner; the immediate half is built here; the console pass can later be called from claim Plan 5's `Recovery.Pass` |
| 6 | [`2026-10-06-moves-phase1-6-cli-status-observability.md`](2026-10-06-moves-phase1-6-cli-status-observability.md) | `carlos move` and its four sub-verbs, `POST …/move/resolve`, `GET …/move?detail=1`, exit codes 0/3/4/5, the move log, the member stream events, paging rules, the copy gate, the `docs/site/cli.md` heading | nothing (the `--declare-lost` flag calls Plan 4 Task 10's door and is built with it) |
| 7 | [`2026-10-06-moves-phase1-7-rehearsal-runbook.md`](2026-10-06-moves-phase1-7-rehearsal-runbook.md) | the `moveprobe` test app, the cross-version tests, the rollout steps and the rehearsal runbook in M12's order | every plan above, merged and rolled with Paul's go |
+| home part 2 | [`2026-10-15-moves-home-region-part2.md`](2026-10-15-moves-home-region-part2.md) | moves into the home region under host claims (#236): T3's home rule, the commit, revert, rollback, abort and loss with a home target, the door's home target (MV-N510, MV-N511), and the rehearsal's Run 1b | claim Plan 13's home naming and the signed designation; nothing new from the claim |
Plans 1 and 2 have no unmerged dependency and are built first, in order.
Plan 3's pure tasks (1 to 4, 8) and Plan 4's pure tasks (1 to 8) can be
diff --git a/docs/superpowers/plans/2026-10-06-moves-phase1-rehearsal.md b/docs/superpowers/plans/2026-10-06-moves-phase1-rehearsal.md
index f095dfd5b..2ca11c5b1 100644
--- a/docs/superpowers/plans/2026-10-06-moves-phase1-rehearsal.md
+++ b/docs/superpowers/plans/2026-10-06-moves-phase1-rehearsal.md
@@ -1,9 +1,10 @@
# Moves Phase 1: the rehearsal runbook
This is the checklist an operator follows to rehearse Phase 1 moves (spec
-§14.4, rollout §15). It covers seven runs in the spec's order: four on dev,
-one on the flagship with the probe app, one on a disposable customer-shaped
-instance, and one on a real customer instance.
+§14.4, rollout §15). It covers eight runs in the spec's order: five on dev
+(Runs 1, 1b, 2, 3 and 4; Run 1b is a move into home), one on the flagship
+with the probe app, one on a disposable customer-shaped instance, and one
+on a real customer instance.
**No run in this runbook can execute today.** Every run waits for P-0
below. Each run also has its own blockers, listed at its top. Never run one
@@ -215,13 +216,28 @@ deployment before its first run.
- [ ] **For any run that touches the home region: the home-region parks fix
part 1 merged** (#236, on main as 359e8ac2) **and claim Plan 13's home
naming** (a0806e4c, follow-ups 2ad5412c). Part 1 covers moves out of
- home. Moves into home need part 2, and the door refuses them until then
- ("moves to the home region are not supported yet. the home publishes no
- staging lane"). A home box arms only when the claim grants it the
+ home. **Any run that moves into home also needs part 2**
+ (`2026-10-15-moves-home-region-part2.md`) merged and rolled, boxes
+ first, then consoles. The door refuses moves into home without host
+ claims (MV-N510). A home box arms only when the claim grants it the
`@home` alias, which needs the signed fleet home designation. Plan 13's
rollout order is binding: every roster reader on a Plan 13 build first,
then publish the designation, then turn the flag on. Older binaries
- cannot read a roster that carries `home_region`.
+ cannot read a roster that carries `home_region`. For moves into home,
+ the whole order is:
+ 1. Plan 13 on every box and console. Check each box reads `current`
+ with `carlos system status`. Plan 13's own order applies inside
+ this step.
+ 2. Part 2 merged (dormant), released, and rolled **boxes first, then
+ consoles**. A console rolled first could admit a move into a home box
+ that still fails T3, and that move waits at `preparing` until an
+ operator aborts it.
+ 3. The IAM mesh, including the home data bucket (P-2), applied by Paul
+ with a scoped `tofu apply -target`.
+ 4. Claim activation (#250), which publishes the signed designation.
+ Until then a home box never arms, and the door refuses claim-mode
+ moves with MV-N177.
+ 5. `CARLOS_MOVES_PHASE1` per run, as P-7 says.
- [ ] **The claim's activation checklist (#250) done for this deployment.**
Among its items:
- the one-time files-manifest backfill for every host older than the
@@ -248,7 +264,38 @@ deployment before its first run.
can serve a stale local DB (amadan #286; `activator.go`, "only while a
region has one box"). So rehearsal apps in dev's home region are woken on
one box only, or the run waits for dev's claim. #286 ruled this a
- dev-only hazard, accepted.
+ dev-only hazard, accepted. During a window under the procedure in the
+ next item, `carlos-dev-ie-b` is stopped, so this holds by itself.
+- [ ] **One placementless box per deployment.** For each deployment, list
+ `control/edges/` and record that exactly one edges file has no
+ `placement`:
+
+ ```sh
+ for k in $(aws s3api list-objects-v2 --profile <profile> --bucket <deploy-bucket> \
+ --prefix control/edges/ --query 'Contents[].Key' --output text); do
+ printf '%s %s\n' "$k" "$(aws s3 cp --profile <profile> s3://<deploy-bucket>/$k - | jq -r '.placement // "(none)"')"
+ done
+ ```
+
+ S1 and T8 refuse a move into **or out of** home while two do, whether or
+ not the second box is running, because an edges file outlives its box.
+ They count every edges file per placement, fresh or not. A retained file
+ below the floor makes the door refuse with MV-N5.
+
+ On dev, `carlos-dev-ie-b` makes two. Ruling R2 (option a) applies to
+ every dev run that touches home, Run 1 included. Each window needs
+ **Paul's scoped go** in the amadan thread, naming dev and
+ `carlos-dev-ie-b`. The procedure, as the ruling gives it:
+
+ 1. stop `carlos-dev-ie-b` (the instance, or at least its agent and console units, so it neither writes status nor serves);
+ 2. delete `control/edges/carlos-dev-ie-b.json` from the deployment bucket;
+ 3. wait until its last `status/boxes/carlos-dev-ie-b.json` is more than 15 minutes old by its `generated_at`;
+ 4. before **each** run, verify the fleet's evidence is complete: every box with a status snapshot fresher than 15 minutes has an edges file, every edges file's box has a status snapshot, and exactly one edges file has no `placement`. The door's own confirmation is the run's **S-2**, the move request itself. Only a move request runs `selectMoveBoxes` and the floor check: `carlos move status` (`handleAPIInstanceMoveStatus`) reads the record and claim only, and S-1 only wakes the app, so neither confirms anything here. If S-2 answers "the fleet's edges files could not all be read" or MV-N5, the window is not ready. Both refusals come before any claim read or write, so nothing was started: fix the evidence and repeat S-2;
+ 5. after the window, start the box again. Its edges pass rewrites the file on boot. No move touching home runs until the window is reopened by steps 1 to 4.
+
+ Step 3's wait matters. If the edges file is deleted while the box's
+ status snapshot is still fresh, the door reads the fleet's evidence as
+ incomplete and refuses every move.
- [ ] **Rehearsal boxes keep instances flat.** Moves refuse a nested
instance. Set `CARLOS_LAYOUT_DEFAULT=flat` in each rehearsal box's
`host.env`. Pre-flight check, before any request: `carlos status
@@ -278,6 +325,14 @@ deployment before its first run.
grants (`s3:ListBucketVersions`, `s3:GetObjectVersion`,
`s3:DeleteObjectVersion` on its own bucket; `s3:GetObjectVersion` for
the console).
+- [ ] **The home data bucket is in the mesh.** Every named region's
+ `sibling_staging_arns` lists the home data bucket's ARN, and the home's
+ lists every named region's data bucket. Record the home data bucket's
+ name (`data_bucket` in the home box's edges file, the one with no
+ `placement`): `____`. Check that it is not the deployment bucket
+ (`<deploy-bucket>`). Without the mesh, a move into home fails the
+ source's lane round with `AccessDenied` (logged), or T10 fails on the
+ home box.
- [ ] Check after the first request reaches ready: [detail]
`detail.target_report.checks.T10.ok` is `true` (the lane probe).
@@ -339,7 +394,9 @@ for its deployment (Paul, N2(a)).
instance now.
- [ ] Record the list of hosts, with the run each one is for, in the
deployment's rehearsal record (outside this repo). **Never reuse a
- host.** A host that has run once is spent, whatever the outcome.
+ host.** A host that has run once is spent, whatever the outcome. The one
+ exception is Run 1b, which moves Run 1's host back into home (Ruling
+ R6).
- [ ] After the claim is on, check each host is adopted: [deploy]
`control/hosts/<host>.json` exists, `holder.location` is
`instances/<host>` (not `instances/<host>/<inc>`), and `op.kind` is
@@ -572,12 +629,19 @@ reached the stopped position). The target box logs both:
has no `CARLOS_REGION`). Confirm it with the dev console's
`CONSOLE_HOME_REGION` and record it here: `____`. If `ie` is home:
+- Every dev run touches home, so every dev run needs P-0's
+ one-placementless-box item: Ruling R2's procedure for
+ `carlos-dev-ie-b`, Run 1 included.
- Run 1 (`ie` to `uk`) moves out of home and needs **home fix part 1**.
+- Run 1b (`uk` to `ie`) moves Run 1's host back into home. It needs
+ **home fix part 2** rolled and dev's designation published.
- Run 4 (`ie` to `uk`) moves out of home and then rolls back into it. It
needs **part 1**, and part 1 must cover a rollback whose source is home.
+ It stays out of home even once part 2 is rolled (Ruling R6), because
+ dev's console runs on the home box.
- Runs 2 and 3 as written (`uk` to `ie`) move into home and need **home
- fix part 2**. The door refuses them until then.
-- **Until part 2 lands, only out-of-home moves run.** Run 1 goes ahead on
+ fix part 2**. Once part 2 is rolled, they run as written.
+- **Until part 2 is rolled, only out-of-home moves run.** Run 1 goes ahead on
part 1. Runs 2 and 3 then use the **out-of-home arrangement**: the host
is created in `ie`, the move goes `ie` to `uk`, and the failure switch
names `<pr-uk>`. Each rolls back into a home source, as Run 4 does, so
@@ -585,8 +649,8 @@ has no `CARLOS_REGION`). Confirm it with the dev console's
in part 1's own tests first. Part 1 is not built yet, so this is open.
If part 1 does not cover it, Runs 2, 3 and 4 all wait for the fix that
does.
-- Run 4 runs only after Runs 2 and 3 have passed, in whichever
- arrangement they ran. No run is skipped.
+- Run 4 runs only after Runs 1, 1b, 2 and 3 have passed, Runs 2 and 3 in
+ whichever arrangement they ran. No run is skipped.
- On the home box, moveprobe reports `home`, so `<pr-ie>` is `home`. Any
[report] check or `MOVEPROBE_FAIL_READY_IN` aimed at that box uses
`home`, never `ie`.
@@ -597,10 +661,12 @@ If `ie` is not home, the runs go as written and need no home fix.
### Run 1: dev, `ie` to `uk`, a commit
-**Blockers:** P-0; home fix part 1 if `ie` is home; dev topology above; P-1
-to P-7 on dev; Paul's go.
+**Blockers:** P-0; home fix part 1 if `ie` is home; if `ie` is home, P-0's
+one-placementless-box item, with Ruling R2's window open for
+`carlos-dev-ie-b`; dev topology above; P-1 to P-7 on dev; Paul's go.
-- [ ] Host used: `____` (created in `ie` before the claim).
+- [ ] Host used: `____` (created in `ie` before the claim). Run 1b moves
+ this host back.
- [ ] S-1, S-2 with `--to uk`.
- [ ] S-3, S-4. [CLI] M-C5 ("uk is ready. Starting the cut. <host> will be
offline for about <estimate>."), M-C6 ("Stopped in ie. Starting in
@@ -612,16 +678,84 @@ to P-7 on dev; Paul's go.
`last.outcome` is `done`; `detail.timings` holds `offline_s` and
`cut_budget_s`.
+### Run 1b: dev, `uk` to `ie`, a commit into home
+
+**Blockers:** Run 1 passed; Run 1's quiet window has passed (T12, see
+"Between runs"); home fix part 2 merged and rolled, boxes first, then
+consoles; the fleet home designation published on dev (claim activation,
+#250); P-0's one-placementless-box item, with Ruling R2's window open for
+`carlos-dev-ie-b`; P-2's home data bucket item; Paul's go.
+
+This run moves Run 1's host back into home (Ruling R6). It is the one
+host this runbook uses twice (P-5). It has the same checks as Run 1, with
+`<pr-ie>` = `home`.
+
+**Note for the operator (Ruling R7).** Where the console shares the home
+box, as dev's does, the landing runs on the console's own box. If that box
+dies mid-landing, the console's move pass stops with it until the box or
+the console is back. The record and the claim are durable, so nothing is
+lost, but recovery waits for the console's return.
+
+- [ ] Host used: Run 1's, `____`.
+- [ ] Before the request, record the claim's touched regions and quiet
+ windows:
+
+ ```sh
+ aws s3 cp --profile <profile> s3://<deploy-bucket>/control/hosts/<host>.json - | jq '{regions: .touched.regions, quiet_after}'
+ ```
+
+- [ ] S-1, S-2 with `--to ie`. Name the home by its configured name. There
+ is no `home` keyword, because a named region may be called `home`
+ (Ruling R3). If S-2 refuses, the sentence says what is missing:
+ - MV-N510 ("moves into the home region need host claims. this console
+ runs without them"): the console runs without `CARLOS_HOST_CLAIM`;
+ - MV-N511 ("<box> sets CARLOS_REGION, so it cannot take a move into
+ the home region"): a box's edges file names `ie` as its placement.
+ That box is not the home box. Fix its `host.env`, or delete its
+ edges file if the box is gone;
+ - MV-N177 ("the host roster does not allow this move. the source and
+ target must be active boxes in uk and ie"): the roster has no home
+ designation, or the console's home name differs from it;
+ - "the fleet's edges files could not all be read" or MV-N5: Ruling R2's
+ window is not ready (P-0, step 4).
+
+ Each of these refusals comes before any claim write.
+- [ ] At ready, the staging lane is in the home data bucket:
+ [data] `list-object-versions` on `<data-bucket-ie>` (the home data
+ bucket P-2 recorded) with `--prefix "moves/<host>/<move>/"` returns
+ versions.
+- [ ] S-3, S-4. [CLI] M-C5 ("ie is ready. Starting the cut. <host> will be
+ offline for about <estimate>."), M-C6 ("Stopped in uk. Starting in
+ ie."), M-C7 ("Moved. <host> is served from ie. It was offline for
+ <seconds> seconds."), exit 0.
+- [ ] R-1 to R-6, with `<pr-to>` = `home`.
+- [ ] [deploy] `log/` has `cutting-flipped`, `committed`,
+ `committed-opened`, `committed-source_parked` and `done`. [detail]
+ `last.outcome` is `done`; `detail.timings` holds `offline_s` and
+ `cut_budget_s`.
+- [ ] [deploy] the claim's `touched.regions` holds at least one spelling
+ of home: `ie`, `@home`, or both. Touch adds `@home` when the target box
+ is new to `touched`, and never removes `ie`. Every region it held
+ before the request is still there.
+- [ ] [deploy] the claim's `quiet_after` names `uk`.
+- [ ] After the close: [data] `list-object-versions` on
+ `<data-bucket-ie>` under `parked/<inc>/<host>/` holds the residue of
+ Run 1's stay in home, if there was any.
+- [ ] If any check fails, repeat Run 1 on a spare host created in `ie`,
+ then Run 1b on that host.
+
### Runs 2 and 3: the regions
Runs 2 and 3 use one of two arrangements, chosen before the dev hosts are
created (see "Dev's home region"):
- **As written**: the host is created in `uk`, and the move goes `uk` to
- `ie`. Needs home fix part 2 if `ie` is home.
-- **Out of home** (`ie` is home and part 2 has not landed): the host is
+ `ie`. Needs home fix part 2 if `ie` is home. Once part 2 is rolled,
+ Runs 2 and 3 run as written.
+- **Out of home** (`ie` is home and part 2 is not rolled): the host is
created in `ie`, and the move goes `ie` to `uk`. Needs part 1 to cover a
- rollback whose source is home.
+ rollback whose source is home. This stays the fallback while part 2 is
+ not rolled.
Below, `<from>` and `<to>` are the arrangement's regions as the CLI names
them, and `<pr-from>`, `<pr-to>` their probe regions. Record the
@@ -629,8 +763,9 @@ arrangement used in each run.
### Run 2: dev, forced clean rollback
-**Blockers:** P-0; the home fix its arrangement needs (above); Run 1
-passed; Paul's go.
+**Blockers:** P-0; the home fix its arrangement needs (above); if `ie` is
+home, P-0's one-placementless-box item, with Ruling R2's window open for
+`carlos-dev-ie-b`; Run 1 passed; Paul's go.
The target fails its readiness check in before-write mode, so it writes
nothing and the rollback should be clean.
@@ -674,8 +809,9 @@ finding, not a probe failure. Stop the series and report it.
### Run 3: dev, forced dirty rollback
-**Blockers:** P-0; the home fix its arrangement needs; Run 2 passed;
-Paul's go.
+**Blockers:** P-0; the home fix its arrangement needs; if `ie` is home,
+P-0's one-placementless-box item, with Ruling R2's window open for
+`carlos-dev-ie-b`; Run 2 passed; Paul's go.
The target writes one row, starts its worker, and then fails readiness, so
the rollback is dirty and the target's writes must come back.
@@ -711,15 +847,21 @@ the rollback is dirty and the target's writes must come back.
**Blockers:** P-0, including claim Plan 12's loss-bound admission and Plan
4 Task 10's box half; home fix part 1 if `ie` is home, covering a
-rollback whose source is home; **Runs 1, 2 and 3 passed** (Runs 2 and 3
-in whichever arrangement they ran; Run 4 is never run ahead of them);
-Paul's go.
+rollback whose source is home; if `ie` is home, P-0's
+one-placementless-box item, with Ruling R2's window open for
+`carlos-dev-ie-b`; **Runs 1, 1b, 2 and 3 passed** (Runs 2 and 3 in
+whichever arrangement they ran; Run 4 is never run ahead of them); Paul's
+go.
The target box is stopped mid-move and declared lost. With only two dev
regions, the target is `carlos-dev-uk`, and `uk` has no eligible box until
the readmit at the end. **Stop the box. Do not terminate it.** A terminated
box cannot be readmitted.
+Run 4 stays `ie` to `uk` even once part 2 is rolled (Ruling R6). It never
+stops the home box: dev's console runs there, so a declare-lost of the
+home box would stop the console too. It does not move into home itself.
+
- [ ] Host used: `____` (created in `ie` before the claim).
- [ ] **Paul's scoped go** in the amadan thread, before anything below,
to stop `carlos-dev-uk` during this run and take it out of service
@@ -915,9 +1057,8 @@ still applies, once the rollback has closed.
### Run 6: a disposable customer-shaped instance
**Blockers:** P-0; **the home-region parks fix, both parts, merged and
-rolled** (customer instances live in the home region); a dev rehearsal
-from home passed once the fix is in; Run 5 passed; the owner's agreement;
-Paul's go.
+rolled** (customer instances live in the home region); dev Runs 1 and 1b
+passed; Run 5 passed; the owner's agreement; Paul's go.
A disposable instance of a customer app with the same shape as moveprobe
(a key file, a store, a sealed secret), created for the run with the
@@ -1032,7 +1173,9 @@ copy is `docs/superpowers/plans/2026-10-06-moves-phase1-copy.md`.
| what | where |
|---|---|
-| no moves into home, and its sentence | `internal/console/apiinstancemove.go:537-540` |
+| moves into home: refused without host claims (MV-N510), and a box whose edges file names the home as its placement refused (MV-N511), both before any claim read; the home's staging from placementless edges files | `2026-10-15-moves-home-region-part2.md` (Task 5); merge commit `____`; on its branch `internal/console/apiinstancemove.go:614`, `:665-666`, `internal/console/instancesplacement.go:199` |
+| one placementless box: S1 and T8 count every edges file per placement, fresh or not, before the data-bucket check | `internal/host/movesreport.go:113` (`loadMoveFleet`) |
+| Ruling R2's procedure, R6 (Run 4 stays out of home, Run 1b) and R7 (a home landing on the console's box) | `2026-10-15-moves-home-region-part2.md`, "Rulings needed" |
| a region with no staging bucket is refused | `internal/console/apiinstancemove.go:580-583`; staging from `data_bucket`, `internal/console/instancesplacement.go:366` |
| M-C21, more than one box | `internal/console/apiinstancemove.go:595-600` |
| MV-N5, below the floor | `internal/console/apiinstancemove.go:613-616`; `copy:55` |
@@ -1110,7 +1253,7 @@ copy is `docs/superpowers/plans/2026-10-06-moves-phase1-copy.md`.
| what | where |
|---|---|
| the cut guard and T13 | branch `moves-cut-guard`, commits `083a6eaa` and `6871d2e3` |
-| the home parks fix parts 1 and 2 | branch `moves-home-parks` (no commits yet); #236 |
+| the home parks fix part 2 (moves into home) | `2026-10-15-moves-home-region-part2.md`, branch `moves-home-part2-plan`; merge commit `____`; #236. Part 1 is on main as 359e8ac2 |
| claim Plan 12; moves Plan 5 Tasks 9b and 8, Plan 4 Tasks 9 and 10 | plans only; the landing, rollback and recovery are unarmed until then, `internal/host/movespass.go:335-340` |
| dev's second region | platform-infrastructure#42 |