Review findings on #120907:
- MAJOR-1: resolveComposerAddress mapped 'new' to target 'active', so
insertText/submit/focus('new') landed in whatever composer the bus
routed to (a tile showing session X) while the docs promised the
session-less draft. 'new' now resolves to the primary composer only
while it shows no session ($activeSessionId and $selectedStoredSessionId
both empty — the same condition under which its getIds() is
['__new__']), otherwise to no target and the verbs return false / drop.
- minor-1: an id-addressed draft request was answered by every owning
surface (primary pane + keep-alive tile of one session both painted).
The first owner stamps `claimed` on the shared detail; later owners skip.
- minor-2: getDraft's stash fallback keyed on ids[0] (the requested,
possibly runtime, id); the stash is keyed by the stored id, so a
runtime-addressed read of an unmounted session came back null. Key on
the resolved stored id.
- minor-3: JSDoc claimed an absent surface "rejects"; nothing rejects —
wording now matches the null/false contract.
- shape: the ~130 lines of verb bodies + resolver move out of the
sdk/index.ts facade into sdk/composer.ts (`composer: composerHost`),
like the settings/bridge lanes.
- tests: collapse the six "no surface -> null/false" assertions and the two
self-responding plumbing tests; each behaviour keeps <= 2 tests, the
three findings above are folded into existing tests (all red on 8fe7b682).