feat(interaction): multi-ref press plan lowered through the ordinary press lane
What
press @e13,@e14,@e15,@e16 — a comma-separated ref plan (2 to 16, repeats allowed for PIN digits). One ADR 0014 logical command: every member resolves and admits against the active frame before key 1 (the same #4461 admission a single press gets, judged once for the whole plan); the frame then expires once, and the dispatch walks the admitted points — press --count with N points. Later keys tap where the snapshot showed them; the help says so plainly. Resolutions and admissions are all pre-dispatch (dispatched: 'no'); after key 1 the request dispatch ledger owns the dispatchedSteps disclosure, and a failure names the refused key (details.refPlanKey) and the keys that did not run (details.refPlanPending). The sequence lane (~850 lines, its own identity re-find and per-key rest check, lazy import on the press route) is gone in favor of two small phases.
MCP
pinRef pins each member of a comma list against the same scope, so a renumbered frame refuses a member through its generation pin (test: snapshot → press list asserts ~sN on every forwarded member).
Evidence
docs/evidence/multi-ref-plan-ios-simulator-2026-11-12.md (head 98f78ec01): a real PIN pad on the iPhone 17 Pro simulator — press @e12,@e13,@e12,@e13 → sequence.pressed: 4, four points, page oracle ENTERED=1212 RESULT=PASS with four dots (2104ms); negative press @e13,@e999 → dispatchedSteps: 1, dispatched: 'unknown', refPlanKey: '@e999'. Wall time vs the alternatives and the later-key-mutation semantics are in the file. The batch shape question is framed with live data in #4476.
Validation
check:affected --run --base old-origin/main passes every stage except unit, which fails only eager-closure-budgets — this clone's origin names a private fork and the gate reads the wrong merge base; pointed at the upstream head for one pnpm run check:unit, the chain exits 0. CI on the pushed head is authoritative.
🤖 Generated with Claude Code