# W2 #345 RESOLVER — lane notes (todlando, 2026-09-26)

Lane `feat/351-w2-resolver` on da5b3415 (W0+W1 accepted head). Worktree `.worktrees/351-w2`, pool claimed as `351-w2-resolver`.

## Release-note line (conditions 1 + F2)
Bare-id sends and attaches now reach the ACTIVE instance of an endpoint, even when a dormant instance of it runs on your own node (`spt send ling` / `spt rc ling` used to take the local one first). With nothing active, a dormant instance is preferred to a suspended one, and the one on your node wins among equals. Two consequences you can notice:
- A bare send to an id whose local instance is suspended or offline, and which has two or more dormant instances on other nodes, now refuses with `AMBIGUOUS:` (exit 1) and lists each candidate with its state. Before, it spooled to the local instance.
- A send to `id@<this node>` now lands in the local perch. Before, it reported `NO_PERCH`.

Unchanged: a bare send to your own id stays on your own instance; `--busy-only` and `--force-native` stay local; with the daemon down or no subnet the send behaves exactly as before.

## Condition 2: latency (bare local send, wall time, debug vs debug)
- Arms: BEFORE = doyle's gate debug `spt.exe` at da5b3415 (copied to scratch). AFTER = my debug `spt.exe` at 37e8900c (copied). F1 and the force-native skip came after this build; neither touches this path.
- Setup: same daemon, same target, interleaved A,B,A,B, n=25 per arm, 2 warm-ups per arm discarded. All 54 runs exited 0 with `QUEUED:w2-latency-probe`.
- Target: a throwaway offline perch `w2-latency-probe` (dead pid). It was not in the registry, so the AFTER arm ran the full pre-route (node id, snapshot load, exclusions, local view) and then fell to the local leg. It was purged afterwards and checked gone: no list row, no perch dir.
- Results:
  - BEFORE: median 432.7 ms, p95 4790.9 ms, min 165.2, max 5641.6
  - AFTER: median 414.1 ms, p95 1397.8 ms, min 167.5, max 2678.7
- Reading: no cost is measurable at this resolution. The medians differ by -18.6 ms, inside the noise. Outliers drive both p95s, and the box was loaded (low-memory episode earlier; doyle killed rust-analyzer during the window). The AFTER p95 being lower is noise, not a speed-up.

## Condition 4: qualified own-node gap, red on purpose
The unit `own_node_qualified_send_reaches_the_local_perch` handed `ling-own@<own hex>` straight to `spt_msg::deliver::send_windowed`, in an isolated home with a perch `ling-own`. It FAILED with left `NoPerch`, right `Queued` (nextest exit 100). `crates/spt-msg` has zero diff vs da5b3415. The unit now routes through the pre-route and passes.

## F1 (doyle pre-gate review): self-send guard with no proven id
- Measured by reading the code, not live: the recharge wake is sent by the claude-spt HOOK binary, a subprocess of CC's SessionStart hook on a `clear` boundary (`spt-claude-code/tools/claude-spt/src/hook.rs:2478`), as `spt send --from <eid> <eid> --json-payload {wake_fire} --force-native`.
- `detect_self_id` (`crates/spt/src/roster.rs:203`) resolves from four sources:
  - `OWL_SESSION_ID` matching a perch's session. After a clear the session id rotates, so this may miss.
  - `SPT_AGENT_ID`.
  - `SPT_ENDPOINT_ID` when a perch of that id exists.
  - process ancestry.
- The hook inherits CC's environment, which carries `SPT_ENDPOINT_ID`, so it most likely resolves through the third source. This was NOT measured inside a live hook.
- The wake is now covered twice, independently:
  - (a) F1: the self-send check falls back to the bare id of `from` (here `--from <eid>`).
  - (b) `--force-native` sends never pre-route. This was found while measuring F1: force-native is binary-or-nothing through this node's broker, and pre-routing it could have refused a wake as AMBIGUOUS.
- Unit: `unproven_self_send_still_stays_local`.

## Condition 6: open question for #349/#350 (not ruled here)
`presence::addressed_target` (`crates/spt-daemon/src/presence.rs:214`) resolves ONE addressed id's instances by most-recently-active, for addressed notifs. That is an instance resolution inside the notif domain, and it does not follow S6. Left as is.

## Probe residue
`w2-latency-probe` produced edge notices (`offline`, then `suspended` on HFENDULEAM) from its one advertisement before the purge. After the purge it has no row and no dir.

## UNPROVEN-LIVE (doyle ruling (a), 2026-09-26)
No int drives the `cmd_send_verdict` / `rc` CALL SITES against a live registry. `spt_daemon::is_running()` pings the seed-control socket (`daemon.rs:581`), and no rig runs a seed daemon, so a CLI send in a rig home never reads the registry. Every bare send there stays local with or without the fix, and the control (`--from Y X` reaching B) would land local too. Covered instead by:
- units through `pre_route_with` over a live-shaped registry: `pre_route_*`, `unproven_self_send_still_stays_local` with its control;
- the loopback two-broker int leg in `wan_send_ships_to_the_resolved_node` (pre-route → pinned ship → B's spool);
- axes cell 5 (the resolver over each host's live registry).
The live CLI arm (a bare send to X from A reaches B's active X; a self-send `--from X X` stays on A; control `--from Y X` reaches B) RIDES #351 FIELD ACCEPTANCE on real daemons. A seed-control stub for rig homes is filed with hertz as an infra-register entry (non-blocking).
