Issue #3608385: Rework the interaction capability-token dispatch into a decide-then-apply resolver
The dispatcher's decision sequence now lives in one place. A CapabilityResolver (orchestra_interaction.capability_resolver) evaluates a validated grant and returns a CapabilityOutcome value object with four kinds: finished (the terminal chain's messages, or the generic status page), dispatch (this parked token, this interaction, branch-scoped or not), messages (the render-only status list for the supersession rule), or nothing. It encodes exactly the historical order: terminal check, parked resolution by grant scope, consumed-branch continuation (#3608298), opt-in cross-chain catch-up, render-only chain view, interactive dispatch. resolveSignal() carries the signal guards verbatim, including the "an assigned step is never completed through a bearer link" rule from #3608269.
The controller drops from 510 to ~330 lines and only applies outcomes: rendering, the resume plus queue drain, and grant validation. Two structural wins beyond size: title() and step() now consume the same outcome, so the page title can no longer disagree with the body (the hand-mirrored branchCurrentInteraction() is gone), and every security comment from #3608216/#3608269/#3608298 moved verbatim onto the decision it protects.
No route or token-format changes. Verified locally with the concurrent sweep: root + interaction stack + inbox + ui kernel suites (26 parallel batches, all green, ~4 minutes), the interaction/inbox/ui Functional suites in Docker (25), the inbox FunctionalJavascript test, phpcs and cspell.