# Multi-subnet home resolution at `endpoint run` creation

<!-- [doc->REQ-RUN-MULTISUBNET-HOME] -->

## Status

accepted (2026-06-23; v0.14.0 endpoint-creation-flow, W6 int keystone green — `multi_subnet_bringup_e2e` proves the multi-subnet refuse + `--subnet` bind-inherit on a ≥2-subnet node, no double-seed). Refines ADR-0010 (immutable home subnet) at the spt-hosted creation seam. Sibling of ADR-0027 (Unbound state + attach-on-session). Delivered in the endpoint-creation-flow milestone (post-v0.13.2).

## Context

`spt endpoint run` on a **multi-subnet** node cannot bring up a fresh endpoint: the harness's `SessionStart` bind hook runs `spt api bind "$SPT_ENDPOINT_ID" --set-session-id "$sid"` with no `--subnet`, and `cmd_bind → establish_perch → stamp_creation_fields` (prior=None branch) calls `assign_home`, which on a node holding ≥2 subnets returns `HomeError::Ambiguous` → `HOME_REFUSED` (the established no-guess policy, ≥v0.11.0). The hook swallows it (`|| true`) → no perch ever binds → `ENDPOINT_RUN_ONLINE_TIMEOUT` after 25s.

This is a **latent gap, not a regression** (perri pinned: spt 0.11/0.12/0.12.1/0.13.1 all `HOME_REFUSED`). The spt-hosted bringup never supported a multi-subnet home — it only worked while the node was single-subnet (`assign_home` auto-homes the sole subnet); adding a 2nd subnet (e.g. BIGNET) exposed it. The spt-hosted path gives the adapter no way to supply the home: `endpoint run` has no `--subnet`, the broker injects no home env, and bind has no fallback. Same class as F-013 (bringup can't satisfy bind from available inputs).

Home is **immutable** (ADR-0010 — no re-home; `spt fork` is the cross-subnet move), so a *silent* wrong default is costly (stuck → must fork). The fix must decide the home *correctly at creation*, not guess and let it be fixed later.

## Decision

**`endpoint run` resolves the home subnet and pre-creates the skeleton perch carrying it, reusing the existing skeleton→live model** (CONTEXT §post-spawn seam: "the call flips the perch from skeleton → live"). Today `cmd_endpoint_run` does **not** pre-create the skeleton for the spt-hosted path — it spawns straight to `launch_harness_brokered_in`, and the hook's bind creates the perch fresh (the `assign_home` → `HOME_REFUSED` path). The fix:

1. **Resolve the home at the skeleton-create step**, before spawning the harness:
   - sole subnet → auto (`assign_home`, unchanged);
   - multi-subnet, no `--subnet`, **non-interactive** terminal (`std::io::IsTerminal` on stdin) → **refuse early** with a clear, MRU-ordered `--subnet` guidance message (never the silent 25s timeout);
   - multi-subnet, no `--subnet`, **interactive** → print the proposed endpoint config (id, project, adapter[:profile], home = the MRU default) and prompt `Ok to proceed? Y/n`; `n` → print the `--subnet` guidance;
   - `--subnet <name>` always overrides and validates membership (`assign_home` `NotMember`).
2. **Write the skeleton perch `info.json` with the resolved `home_subnet`** before spawn. The hook's `bind` then hits `establish_perch`'s **prior-branch**, which **inherits** `p.home_subnet` (immutable) — no `HOME_REFUSED`, **no hook change, no env injection**.
3. **MRU = ordered lists** (move-to-front) of recently-chosen home subnets at **two levels**: a **per-project** list and an always-updated **node-global** list (the fallback for a project with no list yet). The default home = project-list head ?? node-global head; the interactive picker / `--subnet` guidance lists subnets MRU-ordered. Both lists update on every home choice.

## Considered Options

- **Broker injects `$SPT_ENDPOINT_SUBNET` + `cmd_bind` env-fallback** (the first design) — works with unchanged hooks too, but adds env plumbing through `endpoint run → broker → SpawnReq` plus a `cmd_bind` branch, and gives no pre-bind perch. **Rejected** in favor of the skeleton-with-home, which reuses the existing immutable prior-branch inherit (≈no new bind/hook code) and yields a pre-bind perch (the attach target ADR-0027 needs).
- **Relax ADR-0010 to a mutable home** (so a silent MRU default is fixable later) — reopens the sync-scope / stale-mind ambiguity ADR-0010 closed and makes `spt fork` redundant. **Rejected**; home stays immutable, the pick is made correctly at creation.
- **A persisted single node-default-home** — too coarse; the operator wanted per-project memory with a node-global fallback, and a *list* (history), not one value.
- **Require `--subnet` unconditionally on multi-subnet** — correct but poor UX for the common case; the interactive confirm + MRU default keeps the flag-free path working while never *guessing* silently.

## Consequences

- A multi-subnet node gets a flag-free interactive bringup (confirm the MRU default) and a hard, clear refusal in non-interactive contexts (CI/scripts must pass `--subnet`) — never a silent timeout.
- Resume/re-bind is unaffected (it already inherits the recorded home via the prior-branch).
- New state: the two-level MRU lists (per-project + node-global) under `$SPT_HOME`.
- perri's repro is the regression test and **must run on a multi-subnet node** (a single-subnet node auto-homes and hides the gap).
