# v0.14.1 — Home-subnet picker layer (REQ-RUN-PICKER-HOME)

**doyle design → todlando build → doyle gate.** Reviving the deferred half of
v0.14.0's interactive home-resolution path (ADR-0026 §3). Roll into todlando's
v0.14.1 work alongside F-018.

## What's missing (operator-flagged)

v0.14.0 shipped multi-subnet home resolution, but the **interactive** path only
got the CLI `Ok to proceed? Y/n` confirm in `decide_run_home`. The **ratatui
Create-new picker** (bare `spt endpoint run`) never got a home-subnet selection
layer — it passes `None` for the subnet (`picker/mod.rs:275`, comment *"No
picker-collected `--subnet` yet"*) and relies on the post-TUI Y/n confirm firing
after the picker tears down. ADR-0026 §3 specced *"the interactive picker lists
subnets MRU-ordered"*; that layer was consciously deferred. This builds it.

## Operator constraints (binding)

1. The post-TUI `Ok to proceed? Y/n` confirm **stays** for CLI-only bringup
   (`endpoint run --adapter X --id Y` on a TTY, no `--subnet`). Do NOT remove or
   change `decide_run_home`'s Confirm branch or the non-interactive
   `MULTI_SUBNET_HOME` refuse.
2. The picker (bare `endpoint run`) path must use the **new selection layer** and
   must NOT fall through to that Y/n confirm — it bakes an explicit `--subnet`, so
   `decide_run_home` resolves `Home` directly and never reaches Confirm.

## Design

### Flow

Today (Create-new): `CreateAdapter → CreateId → Confirm`.
New on a **multi-subnet** node: `CreateAdapter → CreateId → CreateHome → Confirm`.
Single-subnet / local-only (unpaired) node: **unchanged** — skip `CreateHome`
(`assign_home` auto-homes the sole subnet; `--subnet=None` is correct).

The gate = "does this node hold ≥2 member subnets?" (same condition that makes
`assign_home` return `Ambiguous`). Compute from `SubnetStore` member subnets.

### Files & edits (`crates/spt/src/picker/`)

- **model.rs**
  - `Screen::CreateHome` (new variant).
  - `PickerModel` fields: `home_subnets: Vec<String>` (MRU-ordered member subnets),
    `home_cursor: usize`. Populate `home_subnets` in `new()` from the gathered data
    (below). `selected_home()` = `home_subnets.get(home_cursor).cloned()`.
  - Transition: on `CreateId` Enter (valid id) → if `home_subnets.len() >= 2`
    set `screen = CreateHome` else `screen = Confirm` (the existing target). On
    `CreateHome` Enter → `screen = Confirm`. Up/Down move `home_cursor`.
  - `back()` (Esc): `CreateHome → CreateId`; and `Confirm → CreateHome` when the
    layer is present (`home_subnets.len() >= 2`), else `Confirm → CreateId`
    (current behavior). Keep the existing back chain otherwise.
  - `Outcome::Run` gains `subnet: Option<String>`. Every constructor of
    `Outcome::Run` (the create-new Start/View/Attach terminal builders) sets
    `subnet: self.selected_home()` (which is `None` when the layer was skipped).
    `Outcome::Shortcut` does NOT need it (shortcuts bake terminal flags only;
    `--subnet` is a creation-time pick — leave shortcut as-is for now, note it).
- **data.rs**
  - `home_subnet_options() -> Vec<String>`: the node's **member** subnets
    (`SubnetStore::load()`), MRU-ordered via
    `spt_store::recent_home::mru_preference(project_id)` +
    the same ordering `cli::order_by_mru` uses. project_id from the picker cwd
    (`project_id_for_dir(current_dir)`). Returns `< 2` entries → caller skips the
    layer. (If `order_by_mru` is private to cli.rs, lift the tiny helper to a
    shared spot or duplicate the 6-line move-to-front — prefer reuse.)
- **view.rs**
  - Render `CreateHome`: a titled list of `home_subnets`, cursor highlight, a
    one-line hint ("select home subnet · ↑↓ move · Enter select · Esc back").
    Mirror the `CreateAdapter`/`CreateId` styling.
- **mod.rs**
  - `dispatch(Outcome::Run { .., subnet, .. })` → pass `subnet.as_deref()` as the
    last arg to `cmd_endpoint_run` (replacing the hardcoded `None` at ~mod.rs:275).
  - `handle_key` routes Up/Down/Enter/Esc for `Screen::CreateHome`.

### Why this is correct & minimal

`cmd_endpoint_run(subnet=Some(x))` → `resolve_home_and_write_skeleton(Some(x))` →
`assign_home(store, Some(x))` → `Home(x)` (or `RefuseNotMember` if x isn't a
member — can't happen from the picker since the list IS the member set) → no
Confirm branch. The picker stays a pure front-end routing through the one bringup
core (REQ-RUN-PICKER invariant). MRU recording already happens in
`resolve_home_and_write_skeleton` (`record_home`) — no double-record.

### Edge notes (todlando: confirm, don't expand scope)

- **ChangeAdapter / Instantiate** re-enter create-new screens. ChangeAdapter
  targets an EXISTING perch → `resolve_home_and_write_skeleton` returns early
  (home immutable, perch exists) so a stray subnet is ignored — harmless, but
  cleaner to skip `CreateHome` when the id already has a perch. Instantiate
  creates a fresh local perch → it SHOULD flow through `CreateHome`. Gate
  `CreateHome` on `home_subnets.len() >= 2 && no existing perch for the typed id`
  if cheap; otherwise the multi-subnet-only gate is acceptable (ChangeAdapter's
  early-return covers it). Keep it simple; note what you chose.
- **Fork** (cross-subnet clone) has its own subnet semantics — OUT OF SCOPE here.

## Evidence (ALL in one commit; flip REQ-RUN-PICKER-HOME stages same commit)

- `// [impl->REQ-RUN-PICKER-HOME]` on the Screen variant, transitions,
  Outcome.subnet wiring, data::home_subnet_options, dispatch arg.
- **unit** (model.rs / data.rs tests), `// [unit->REQ-RUN-PICKER-HOME]`:
  - `create_new_multisubnet_inserts_home_layer` — ≥2 member subnets → CreateId
    Enter lands on CreateHome; CreateHome Enter → Confirm.
  - `single_subnet_skips_home_layer` — <2 → CreateId Enter → Confirm directly;
    `selected_home()` is None.
  - `home_selection_bakes_into_run_subnet` — choosing the 2nd subnet → the Run
    outcome carries `subnet: Some(that)`.
  - `esc_backs_out_of_home_layer` — CreateHome Esc → CreateId; Confirm Esc →
    CreateHome (layer present).
  - `home_subnet_options_is_mru_ordered` (data) — members ordered by MRU head.
- **doc**: extend CONTEXT.md §"spt-hosted bringup picker" with the CreateHome
  layer (multi-subnet only; replaces the Y/n confirm for the picker path; CLI
  path keeps it). Tag `<!-- [doc->REQ-RUN-PICKER-HOME] -->`.
- Flip `required_stages = ["doc","impl","unit"]` (NO int — TUI live keypress is
  HITL manual-verify like REQ-RUN-PICKER; the subnet→Home resolution is already
  int-covered by REQ-RUN-MULTISUBNET-HOME's `multi_subnet_bringup_e2e`).

## Gates (todlando runs; doyle re-runs on gate)

1. `cargo clippy --workspace --all-targets` (deny-warnings workspace-wide).
2. `cargo test -p spt --lib picker::` (the new model/data units) + the run_home tests.
3. `traceable-reqs check` — exit 0.
4. `cargo run -p xtask -- gen` — only if clap `///` help changed (it shouldn't —
   no new CLI flag; the picker is runtime UI). Skip if unchanged.
5. **HITL manual-verify (operator/doyle):** on a real ≥2-subnet node, bare
   `spt endpoint run` → Create-new → adapter → id → the home-subnet layer appears,
   pick one → endpoint homes there, no Y/n confirm. Single-subnet node → no layer.

## Coordination

This is the originally-specced v0.14.0 picker layer, shipping in v0.14.1 with
F-018. perri may want to re-validate the bare-run picker UX on real claude-spt
once v0.14.1 publishes (her ≥2-subnet node is the only real multi-subnet rig).
