---
name: f018-adapter-add-nondestructive
description: "v0.14.1 builds — F-018 adapter-add (@6920ea8) + REQ-RUN-PICKER-HOME picker layer (@1ecdc1c); BOTH doyle GATE-PASS 2026-06-23, int-deferral ratified, on branch (not pushed) awaiting v0.14.1 release cut"
metadata: 
  node_type: memory
  type: project
  originSessionId: 8fdec519-d622-4c8c-a382-d86ef72d5662
---

**DOYLE GATE-PASS (2026-06-23, perch up, re-ran ALL gates myself — did NOT trust the report):** clippy `--workspace --all-targets` EXIT0; `cargo test -p spt --bin spt` 242 passed/0 failed (incl. all 8 new: registered_github_home_detects_active_collision, stage_then_swap_failed_fetch_preserves_install, create_new_multisubnet_inserts_home_layer, single_subnet_skips_home_layer, home_selection_bakes_into_run_subnet, esc_backs_out_of_home_layer, home_subnet_options_is_mru_ordered, create_home_lists_subnets); `traceable-reqs check` EXIT0 (REQ-INSTALL-13 + REQ-RUN-PICKER-HOME both +doc+impl+unit). Diffs design-faithful + correct. **int-deferral RATIFIED** (todlando's deviation was right — matches INSTALL-4/9; activating int with no in-CI real-repo E2E would RED gate-3; activate together when that E2E lands). BOTH ready for the v0.14.1 release cut (operator to direct: cut now with these two, or batch with netstream flake #2 + ADAPTER_INSTALL_DEFERRED label #3).

**v0.14.1 = TWO builds, both on `design/endpoint-creation-flow`, GATE-PASS, NOT pushed:**
- **F-018 / REQ-INSTALL-13** @6920ea8 (design @2d8d603) — non-destructive adapter add; see below.
- **REQ-RUN-PICKER-HOME** @1ecdc1c (design @d2111db `V0.14.1-HOME-SUBNET-PICKER-LAYER-DESIGN.md`) — home-subnet selection LAYER in bare `spt endpoint run` ratatui Create-new (deferred interactive half of ADR-0026 §3). New `Screen::CreateHome` (CreateAdapter→CreateId→**CreateHome**→terminal Run) shown ONLY on ≥2-member-subnet nodes; lists members MRU-ordered (data::home_subnet_options reuses `cli::order_by_mru` made pub(crate) + recent_home::mru_preference), pick bakes `Outcome::Run{subnet}`→cmd_endpoint_run `--subnet`, NO Y/n in TUI. Single-subnet/unpaired SKIP (subnet=None auto-homes). **BINDING HELD:** CLI-path decide_run_home Y/n confirm + non-interactive MULTI_SUBNET_HOME refuse UNCHANGED. 3 todlando calls flagged to doyle: (1) create-new has NO Confirm SCREEN — terminates via create_outcome, CreateHome inserts before that yield (design's "→Confirm"=that step); (2) existing-perch bringups (confirm/headless/resume) carry subnet:None not selected_home() (home immutable, cleaner); (3) kept simple multi-subnet-only gate (no no-existing-perch check) — ChangeAdapter early-returns, Instantiate flows through correctly. Units: 4 model + 1 data (real SubnetStore+recent_home under isolated_home) + 1 view. REQ doc+impl+unit (NO int — HITL TUI; resolution int-covered by multi_subnet_bringup_e2e). Gates green: clippy --workspace EXIT0, `cargo test -p spt --bin spt picker::` 40/40, traceable EXIT0. HITL manual-verify (real ≥2-subnet node) = doyle/operator/perri at publish.
- **GOTCHA (both builds):** `spt` crate has NO lib target → unit tests run via `cargo test -p spt --bin spt -- <names>` (NOT `--lib`, errors "no library targets found"); CLAUDE-memory's "--lib" hint is WRONG for this crate.
- **doyle is NO_PERCH** (fires assignments but holds no listener) → reports undeliverable; he gates the branch commits directly. Retry reports opportunistically.

**F-018 = v0.14.1 fast-follow #1 (REQ-INSTALL-13).** doyle design → todlando build → doyle gate (same legs as [[v0140-endpoint-creation-flow]]). Design doc: `F-018-ADAPTER-ADD-NONDESTRUCTIVE-DESIGN.md` @2d8d603.

**Bug (perri, real claude-spt):** `spt adapter add --github user/repo` over an adapter already installed via `--release` git-cloned a SOURCE tree over the extracted built binaries. Both add sources land at `adapters/_github/<safe>` (safe=spec with `/`→`-`) and both WIPED-before-success → registered pointer-mode record dangled → `os error 2` / DeferredManifest.

**BUILT @6920ea8** on `design/endpoint-creation-flow` (NOT pushed — doyle gates first). impl+unit+doc + REQ flip ONE commit:
- **A) Collision refusal** — new `registered_github_home(adapters, spec)` (cli.rs, above cmd_adapter): `registry::registered()` ACTIVE record whose `source_dir`==`_github/<safe>` → `ADAPTER_ADD_ALREADY_REGISTERED:<name>` rc2 routing to `adapter update`/`remove`. Guard runs in cmd_adapter Add BEFORE source-build, only for exactly one of `--github`/`--release` (local `<path>` unaffected — never touches `_github`). No wipe on refusal.
- **B) Stage-then-swap both arms** — `fetch_release_adapter`: dropped `remove_dir_all(&dest)`-before-extract; now writes `<safe>.add.spt` + `apply_release_crc_swap(&staged,&dest)` (the never-strand content-hash swap `adapter update` already uses), removes staged after. `--github` clone arm: clones to `<safe>.staging`, on clone SUCCESS `remove_dir_all(dest)`+`rename(staging→dest)` (same-FS atomic), staging cleaned on EVERY error path. Failed fetch/clone never strands the prior install.

**Units (spt BIN, not lib — `spt` has NO lib target; run `cargo test -p spt --bin spt -- <names>`):** `registered_github_home_detects_active_collision` (seeds active delegated/pointer record, asserts hit name+source_dir + dest bytes untouched + unregistered spec=None); `stage_then_swap_failed_fetch_preserves_install` (corrupt staged → apply_release_crc_swap Err → prior manifest+binary survive byte-for-byte + no `.crc-stage` residue). Reuses existing test helpers `stage_file`/`build_spt_fixture`/`has_swap_temp_residue`. Doc: CONTEXT.md §adapter registration non-destructive note.

**GATES ALL GREEN (local Win):** clippy `--workspace --all-targets` EXIT0 no-warn; `cargo test -p spt --bin spt` 3/3 (2 new + apply_release_crc_swap sibling); `traceable-reqs check` EXIT0. No clap `///` change (refusal=runtime stderr) → xtask gen skipped, no reference.md drift.

**ONE DEVIATION flagged to doyle (his call):** set `required_stages=["doc","impl","unit"]` NOT `[...,"int"]` despite design TL;DR saying flip-to-four. Activating int with NO in-CI evidence RED-fails gate 3 (traceable requires evidence per declared stage). **INSTALL-4 AND INSTALL-9 BOTH keep int DEFERRED for the identical reason** (real-repo/clone E2E, env-gated, not run in CI) — matched the sibling pattern + commented to activate int together when that real-repo E2E lands. doyle to rule: flip to [..,int] now → I co-tag an existing env-gated E2E.

**NEXT:** retry report to doyle (he was NO_PERCH/offline at build-done — assigned async, gates the branch commit @6920ea8 directly). Then push on his GREEN. Lesson reaffirmed: green-unit pattern OK here (durability proven via apply_release_crc_swap garbage-extract failure, network-free); real-repo re-add is the deferred int.
