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>
`cargo fmt --all --check` (ci.yml) was red on main: the client re-architecture
commits and origin's windows-shortcut commit landed with rustfmt violations
(e.g. a 104-char .with_context line in hyprland.rs, an unsorted mod block in
vdisplay.rs, the input.rs `{`-placement CI flagged). Reformat the tree so the
fmt gate passes; no functional changes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The GTK client becomes a relm4 component tree: AppModel owns the window,
trust gate (rules 1-3), and the spawned session child's lifecycle as
typed messages; the hosts page is a child component with a
FactoryVecDeque of host cards — the HostsCallbacks Rc<dyn Fn> bag, the
busy Cell, and the Rc<RefCell<HostsUi>> cross-page pokes are gone.
Trust/settings/library dialogs stay plain GTK, invoked from update with
a ComponentSender.
Deleted with the in-process presenter: ui_stream.rs, video_gl.rs,
ui_gamepad_library.rs, launch.rs, the khronos-egl dep, and the
PUNKTFUNK_LEGACY_PRESENTER hatch. Every stream (and the console library)
now runs in punktfunk-session; the shell spawns it for card connects and
exec's it for --connect/--browse so the Decky wrapper keeps working. The
request-access flow gains --connect-timeout + a CancelHandle that kills
the child. Screenshot scenes re-pointed onto the components (verified:
hosts + library self-capture; the dialog scenes present correctly and
capture under a GL renderer); the gamepad-library scene is gone with the
page.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
start_session_with routes desktop windowed connects to the spawned
Vulkan session binary (--connect --fp, --launch, --fullscreen from the
stream setting); spawn.rs bridges its stdout contract into the shell —
spinner until {"ready":true}, banner from the error/ended JSON line,
exit 3 + trust_rejected routed to the re-pair PIN ceremony, TOFU pins
the advert fingerprint only once the child proves it on a real connect.
The in-process GTK presenter stays for PUNKTFUNK_LEGACY_PRESENTER=1,
Gaming-Mode/--fullscreen and --browse launches (no second toplevel under
gamescope until phase 4 moves the console UI), the request-access flow,
and any spawn failure (silent fallback).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>