forked from unom/punktfunk
C0 of design/client-architecture-split.md. Wake-then-connect exists three times today — GTK's `WakeConnect`/`wake_fallback`, the WinUI shell's `wake_and_connect` (whose comment says it "mirrors the Apple HostWaker"), and Apple's `HostWaker` itself — and the deep-link and profile work was about to make it five. `pf-client-core` already held the ingredients (wol, discovery, trust, library, session); what was missing was the layer that composes them, so every front-end composed them itself. `ConnectPlan` is a resolved intent: which host, which pin, which launch id, which profile, the effective settings, whether to wake. One constructor per door — a card click, a CLI verb, a URL — one type out. `ConnectPlan::resolve` is pure (stores in, plan out), which is what lets the URL router be tested without a config directory; `for_host` is the convenience that loads them. `plan_from_link` is where the deep-link security rules actually live, rather than in each shell: a contradicted fingerprint refuses and says which host it was about, an ambiguous name refuses instead of picking the first, a profile the catalog can't honor refuses BEFORE anything is dialled, and an unknown — or known-but-never-pinned — host becomes a confirmation sheet carrying the claimed name and expected pin, so the first connect is verified rather than blind TOFU. Preempting a live session stays with the caller: only the front-end knows one is running. `WakeWait` is Apple's `HostWaker` cadence as a pure step function — packet at 0 s and every 6 s, presence polled every second, 90 s budget, and a PARK (not an error) at the end, because "it didn't wake in 90 s" is usually "give it 10 more". As a step function each front-end drives it from its own loop and they still agree, and the whole cadence is testable without waiting 90 seconds. Session spawn, its argv and its stdout contract move here too, and the GTK shell now spawns through them — so "which flags does a stream get" and "what does ready mean" have one answer for the shells, the console and the coming CLI. `--profile` deliberately does NOT ride a plain card click: the session resolves the host's binding through the same helper, and passing it would be a second source of truth for one decision. Not yet adopted: the two shells' wake paths. That swap changes the most-used path in the product and the handoff wants it verified on glass with a genuinely sleeping host, which this session couldn't do — so the state machine lands, the duplication stays until it can be verified away. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>