---
name: f015b-adapter-apply-silent-noop
description: "Live adapter update silently no-ops for :profile endpoints — broker apply matcher skew (exact vs parent-match) + empty-affected branch returns success WITHOUT swapping. RCA'd 2026-07-02, fix BUILD-F015B-APPLYMATCH rides F-023 wave."
metadata: 
  node_type: memory
  type: project
  originSessionId: 0c1a5493-59c3-4e19-aea3-c3b6de88f7b5
---

Operator-escalated 2026-07-02 (ADR-0025 promised live update without endpoint takedown; field showed DONE-but-old-version, 2 nodes). doyle /diagnose RCA, static chain + field-validated (both repros were :ccs endpoints; a bare-adapter endpoint would NOT trip it).

**D1 ROOT (matcher skew):** broker `dispatch_adapter_apply` (broker.rs:2667) filters `s.adapter == req.adapter` EXACT; sessions store COMPOSITE `<adapter>:<profile>` (cmd_endpoint_run passes `--adapter claude-spt:ccs` verbatim → SpawnReq.adapter, harnesshost.rs:262); req carries PARENT record.name. CLI's live-gate `adapter_has_live_endpoint` (cli.rs:7614) parent-matches via split(':') — two matchers, one seam, divergent. :profile endpoint ⇒ affected=[].

**D2 AMPLIFIER (silent):** empty-affected branch (broker.rs:2671) sends KIND_APPLIED SUCCESS and returns WITHOUT the CRC swap — but CLI has fully delegated (no fallback swap after daemon Ok) ⇒ LIVE + DONE printed, files stay old, re-register re-reads old manifest, version-of-truth honestly old. Translate never stopped; lock never even reached.

**Exonerated:** crc_swap (loud + rollback), the Windows exe lock + terminate-async race (never reached), ADR-0025 design. Recovery that worked (stop→update→relaunch) worked because CLI gate went false → direct apply_release_crc_swap path.

**Second infected comparator:** `select_endpoints_running_adapter` (broker.rs:1392 `adp == adapter`); its unit (:3600) tests bare names only.

**Fix (BUILD-F015B-APPLYMATCH, todlando, THIS milestone/wave):** (1) ONE shared adapter_parent_matches fn at all 4+ sites; (2) never-success-without-swap — empty-affected branch still swaps; (3) red-first: composite-profile apply swaps+cycles / no-session apply still swaps / different-adapter guard / composite row in select unit; (4) REQ-HAZARD-ADAPTER-APPLY-SILENT-NOOP + REQ-ADAPTER-LIVE-UPDATE extension + KH + ADR-0025 amendment note; (5) perri's PENDING-QUIESCE backlog item downgrades to verify-post-fix — DONE 2026-07-02 (perri verdict: reword NOT close): "verify-post-fix (F015B): mid-live adapter update on a PROFILE-composite endpoint swaps for real and reports honestly — perri runs on-node at wave-publish"; her docs/SPT-CORE-FINDINGS.md carries the fix pointer; wave-publish release-ping must include the verify ask.

**Lessons:** (a) two matchers on one seam WILL skew — matcher parity is a hazard class (kin [[v091-resolve-dotstem]] name-normalization skew); (b) "nothing to coordinate ⇒ success" forgot delegation semantics — a delegated apply owns the WHOLE effect, success must imply the effect happened; (c) honest version-of-truth (adapter list reads manifest, not update claims) is what caught this in the field — keep read-side truth independent of write-side claims.

**SHIPPED v0.20.0 counter 40 @3043f4e [[v0200-published]]. Post-publish verify #1 = INVALID, NOT fix-FAIL (2026-07-02, perri evidence @e34b3e7):** `spt update apply` on-node bumped disk+CLI to 0.20.0 but the RUNNING daemon stayed on the 0.19.1 image (no restart, no notice) → the mid-live :ccs `adapter update` (0.10.4→0.11.0, real composite delta) hit the OLD apply path in the stale daemon → reproduced pre-fix DONE-but-old no-op VERBATIM. My swap was never exercised; e2e coverage untouched. Do NOT read this as the fix failing. Re-run pends doyle-sanctioned daemon-bounce sequence (perri hall-a live, can't tear PTYs unilaterally). New finding one layer up = **F-025: update-apply/daemon-restart gap** (apply must bounce-or-notice the running daemon; version-of-truth honest but the process image is stale). doyle owns F-025 verdict. **F-025 RULED (2026-07-02, applyhost.rs:188-196 + ADR-0018 D3-3):** apply restarts BRAIN only BY DESIGN; BROKER deliberately survives (anchors hosted PTYs) → broker-side surfaces (incl. the F015B dispatch_adapter_apply fix) run the OLD image until a FULL daemon bounce — part design, part CORE GAP (nothing surfaces running-broker-image vs disk version; no BROKER_RESTART_REQUIRED notice = silent dormancy). Next-wave item (todlando): version-of-truth surfacing (spt version/doctor shows running broker image) + loud apply notice + already-applied classifier for APPLY (second apply reached the rename → os-error-5; FETCH got this classifier in v0.18.0, apply lacks it); broker-restart choreography = ADR-0004 future, needs design. Bounce sequence (operator-gated, hall-a + doyle perch on box): endpoint stop each live → daemon stop → daemon start → relaunch (--start baseline; live agents via live-resume). Perri HOLDS for operator decision, re-verifies same-day post-bounce. **CLOSED (2026-07-02, post-bounce re-verify GREEN, perri evidence @4fbca79):** operator green-lit bounce, doyle executed (hall-a stop→daemon stop→0.20.0-image respawn pid 17052/50840→hall-a relaunch; listeners+psyches survived). Perri re-verify on the bounced broker: fresh :ccs probe, mid-live adapter update 0.10.4→0.11.0 → (a) REAL swap (registry+install manifest 0.11.0, binary discriminator `launch --node` parses), (b) honest DONE with the effect behind it, (c) endpoint survived online. Parent-aware matcher + unconditional swap HOLD on the real published path. PENDING-QUIESCE tracker item RETIRED. F-025 wave item stands (it invalidated attempt #1, unaffected by this green).

**HOLD-SCOPE CLARIFIED (2026-07-03, perri discrepancy flag):** HFENDULEAM F015B arc = CLOSED (@4fbca79 GREEN above) — do NOT re-gate it. **ENLYZEAM leg GO ISSUED 2026-07-03:** operator updated+bounced ENLYZEAM; doyle on-node verify spt 0.21.0 + `broker image: 0.21.0 (matches installed)` + ball-b stamps clean → precondition zero MET, perri released for ball-b/E2E-0104 + F015B-class verify there. HFENDULEAM hold MOOT: operator updated+bounced this box too (2026-07-03) — now 0.21.0 broker==installed, perches survived.

Related: [[f023-f024-wan-idle-starvation]] (the wave this rides), [[counter39-field-bugs-1-3]], [[adapter-glue-model-boundary]].

**BUILT + INTERIM-GATED GREEN (2026-07-02, token INTERIM-G1-0408f78):** wave branch f023-wan-idle-delivery @0408f78 (pushed): 6811ed0 leg-1 WAN inject (shared spt_daemon::inject module, DeliveredInject→"delivered" no wire change, red-first int on real broker PTY) · 8b9975d F015B fix (adapter_parent_matches @ 3 seams + unconditional swap, red-first units) · 615e749 KH 7.24 + ADR-0025 Amendment 2 · 0408f78 incremental-activation de-stage (doyle correction; todlando also self-caught a KH-number collision — 7.23 starvation / 7.24 adapter-apply / 7.25 session-pin-wedge). doyle rig: diff PASS, clippy 0, FULL nextest 1443/1443, traceable 341/341. Remaining wave: F015B e2e a/b/c (int stage activates in landing commit) → PENDING-QUIESCE downgrade → F-023 leg 2 idle-edge-drain → auth-wedge re-pin → sentinel diagnostic → {node} key → residue diagnostics → FINAL wave gate (full protocol + wedge ×2).
