Reported: pinning a host with a profile works when you press the card, but launching a game from that card's library ignores the profile.
That reproduced on the console, and investigating the other clients turned up the same question with a different wrong answer on each. Both commits are here.
The console leak (f5fa9649)
screens/home.rs offers Y → Library on every paired+saved row, and a pinned card is both. But LibraryScreen::new() copied the row's address, port, fingerprint and mgmt port and dropped HostRow::pin, so its launch had nothing to send and hardcoded profile: None — under a comment asserting that game launches follow the host's binding. Pressing the card always passed h.pin.id as the one-off, which is exactly why connecting worked and browsing didn't.
The other four (cc8eb7df)
The library was not offered on a pinned card at all. Apple (both UIs: hasLibrary: profile == nil on the console tile, no menu item on the touch card), Android (hasLibrary required pinnedProfileId == null, and the console host options gated Library on pin == null), GTK (Connect / Copy link / Create shortcut / Unpin — no library) and Windows (pinned tiles had no menu whatsoever). Each was justified as "a pin is a shortcut, not a second host, so host-level actions stay on the host's tile" — right about wake, pair, edit and forget, wrong about the library. Browsing is not a property of the machine: it is this card's connect with a title picked first, which is what a shortcut is for. The library now joins Connect on every pinned surface; the host-level actions stay where they were.
The launch dropped the profile. GTK and Windows already carried it (the library clones the card's whole ConnectRequest / launches through the shared target the tile parks). Apple did not — the library was keyed on a bare StoredHost, so launchTitle connected .inherit. Android was worse, and not only for pins: LibraryScreen dialled connectToHost with the RAW settings, so a library launch applied no profile at all — not even the host's binding — on every host. Its host list resolves settings.effectiveFor(profileStore.resolveFor(kh, oneOff)); its library resolved nothing.
What changed
The shelf (host + profile), not the host, is now the unit both clients navigate by: Apple gets a LibraryTarget threaded through libraryTarget, the shell's screen enum, both presentations and SessionModel; Android passes the pinned id into LibraryScreen, which resolves it through the same ProfileStore.resolveFor rule the connect path uses. Falling out of that:
a game that exits returns to the shelf it launched from, pin and all, not the host's default one. Android drops a pin that was unpinned while the game ran rather than reopening a card that no longer exists.
Android's stats overlay finally names the profile a library launch used (profileName was never set on that path).
Apple's punktfunk://browse/<host> honours profile=, which it parsed and ignored — refusing an unknown or ambiguous reference exactly as the connect route does (§10.6) instead of degrading to the binding.
every shelf says which one it is, in the card's own host · profile shape.
Verification
Nothing here was taken on trust; three of the five clients compile nowhere near a default Mac build.
Two of those earned their keep: a Mac cargo test -p pf-console-ui compiles nothing (every mod is cfg(linux/windows)), and the tvOS pass alone caught navigationDestination(item:) requiring Hashable — macOS and iOS were both green with it broken. The console's new regression test was checked against a reverted fix and fails with the reported symptom (left: None, right: Some("hdr")); the iOS build was confirmed non-vacuous by planting a type error in the iOS-only screen.
Reported: pinning a host with a profile works when you press the card, but launching a game from that card's library ignores the profile.
That reproduced on the console, and investigating the other clients turned up the same question with a different wrong answer on each. Both commits are here.
## The console leak (`f5fa9649`)
`screens/home.rs` offers **Y → Library** on every paired+saved row, and a pinned card is both. But `LibraryScreen::new()` copied the row's address, port, fingerprint and mgmt port and dropped `HostRow::pin`, so its launch had nothing to send and hardcoded `profile: None` — under a comment asserting that game launches follow the host's binding. Pressing the card always passed `h.pin.id` as the one-off, which is exactly why connecting worked and browsing didn't.
## The other four (`cc8eb7df`)
**The library was not offered on a pinned card at all.** Apple (both UIs: `hasLibrary: profile == nil` on the console tile, no menu item on the touch card), Android (`hasLibrary` required `pinnedProfileId == null`, and the console host options gated Library on `pin == null`), GTK (Connect / Copy link / Create shortcut / Unpin — no library) and Windows (pinned tiles had no menu whatsoever). Each was justified as *"a pin is a shortcut, not a second host, so host-level actions stay on the host's tile"* — right about wake, pair, edit and forget, wrong about the library. Browsing is not a property of the machine: it is this card's connect with a title picked first, which is what a shortcut is for. The library now joins Connect on every pinned surface; the host-level actions stay where they were.
**The launch dropped the profile.** GTK and Windows already carried it (the library clones the card's whole `ConnectRequest` / launches through the shared target the tile parks). Apple did not — the library was keyed on a bare `StoredHost`, so `launchTitle` connected `.inherit`. **Android was worse, and not only for pins: `LibraryScreen` dialled `connectToHost` with the RAW settings, so a library launch applied no profile at all — not even the host's binding — on every host.** Its host list resolves `settings.effectiveFor(profileStore.resolveFor(kh, oneOff))`; its library resolved nothing.
## What changed
The **shelf** (host + profile), not the host, is now the unit both clients navigate by: Apple gets a `LibraryTarget` threaded through `libraryTarget`, the shell's screen enum, both presentations and `SessionModel`; Android passes the pinned id into `LibraryScreen`, which resolves it through the same `ProfileStore.resolveFor` rule the connect path uses. Falling out of that:
- a game that exits returns to the shelf it launched from, pin and all, not the host's default one. Android drops a pin that was unpinned while the game ran rather than reopening a card that no longer exists.
- Android's stats overlay finally names the profile a library launch used (`profileName` was never set on that path).
- Apple's `punktfunk://browse/<host>` honours `profile=`, which it parsed and ignored — refusing an unknown or ambiguous reference exactly as the connect route does (§10.6) instead of degrading to the binding.
- every shelf says which one it is, in the card's own `host · profile` shape.
## Verification
Nothing here was taken on trust; three of the five clients compile nowhere near a default Mac build.
| Client | Gate | Result |
| --- | --- | --- |
| console + GTK | fmt, build, clippy `-D warnings`, tests (pf-lxcheck2 container) | green, 85 tests |
| Apple | `swift build` × macOS, iOS, tvOS | green |
| Android | `compileDebugKotlin` + unit tests | green |
| Windows | `cargo check --all-targets` + clippy `-D warnings` (CI runner, cold 3m10s) | green |
Two of those earned their keep: a Mac `cargo test -p pf-console-ui` compiles **nothing** (every `mod` is `cfg(linux/windows)`), and the **tvOS** pass alone caught `navigationDestination(item:)` requiring `Hashable` — macOS and iOS were both green with it broken. The console's new regression test was checked against a reverted fix and fails with the reported symptom (`left: None`, `right: Some("hdr")`); the iOS build was confirmed non-vacuous by planting a type error in the iOS-only screen.
Pinning a profile onto a host gives it its own card on the console home, and
pressing A on that card has always connected with the pinned profile as the
one-off the resolver prefers over the host's binding. Y on the same card opens
a library — it is paired and saved, which is the only thing the hint bar asks —
and every title launched off that shelf went out with no profile at all, so the
host's default binding won. Connecting straight from the card honoured the
profile; going through its library did not, which is the shape a user reads as
"the pin works until I pick a game".
The screen was the leak: LibraryScreen copied the row's address, port,
fingerprint and mgmt port, and dropped `pin` — so its launch had nothing to
send and hardcoded `profile: None`, under a comment asserting that game
launches follow the binding. They should follow the card: a launch off a pinned
card's shelf is that card's connect with a title attached.
The screen now carries the row's pin and sends its id as the one-off. It also
says so, in the card's own `host · profile` shape: the shelf's title names the
profile, and so does the connecting card, so which of a host's cards you came
in on is legible from the screen rather than inferred from the tile you pressed
two screens ago. Off the host's primary tile there is no pin and nothing
changes — `None`, and the binding decides.
Console-only. The Apple and Android consoles keep Y off pinned cards outright
(`hasLibrary: profile == nil`), the GTK client clones the card's whole
ConnectRequest — profile included — into its library, and the Windows client
offers "Browse library" from the primary tile only. The console's own
copy-link and wake-then-connect paths already carried the pin.
Both directions are tested, and the pinned one was checked against a reverted
fix: it fails with exactly the reported symptom (left: None, right: "hdr").
The console fix before this one closed the leak on one client. The same question
has a different wrong answer on each of the others, so this closes it everywhere:
a pinned host+profile card can be browsed, and every title launched off a pinned
card's shelf streams with that card's profile.
Two shapes of bug, one per client:
**The library was not offered on a pinned card at all** — Apple (both UIs:
`hasLibrary: profile == nil` on the console tile, no menu item on the touch card),
Android (`hasLibrary` required `pinnedProfileId == null`, and the console host
options gated Library on `pin == null`), GTK (the pinned card's menu had Connect,
Copy link, Create shortcut, Unpin — no library) and Windows (pinned tiles had no
menu whatsoever). Each was justified in a comment as "a pin is a shortcut, not a
second host, so the host-level actions stay on the host's tile" — right about
wake, pair, edit and forget, wrong about the library. Browsing is not a property
of the machine: it is this card's connect with a title picked first, which is
exactly what a shortcut is for. So the library joins Connect on every pinned
surface, and the host-level actions stay where they were.
**The launch dropped the profile.** GTK already carried it (its library clones
the card's whole ConnectRequest) and Windows already carried it (its library page
launches through the shared target, which the tile parks). Apple did not: the
library was keyed on a bare `StoredHost`, so `launchTitle` connected with
`.inherit` and the host's binding won even from a pinned card. Android was worse
still, and not only for pins — `LibraryScreen` dialled `connectToHost` with the
RAW settings, so a library launch applied NO profile at all, not even the host's
binding, on every host. Its host list resolves
`settings.effectiveFor(profileStore.resolveFor(kh, oneOff))`; its library resolved
nothing.
So the shelf, not the host, is now the unit both clients navigate by. Apple gets
a `LibraryTarget` (host + `ProfileSelection`) threaded through `libraryTarget`,
the shell's screen enum, both presentations and `SessionModel`; Android passes
the pinned id into `LibraryScreen` and resolves it there through the same
`ProfileStore.resolveFor` rule the connect path uses. Falling out of that:
* a game that exits returns to the shelf it was launched from, pin and all,
rather than to the host's default one — `SessionModel.launchedShelf` on Apple,
`ActiveSession.libraryProfileId` → `LibraryReturn` on Android. Android also
drops a pin that was unpinned while the game ran, rather than reopening a card
that no longer exists.
* Android's stats overlay finally names the profile a library launch used
(`profileName` was never set on that path).
* Apple's `punktfunk://browse/<host>` honours `profile=`, which it parsed and
ignored — and refuses an unknown or ambiguous reference exactly as the connect
route does (§10.6) rather than degrading to the binding.
* every shelf says which one it is, in the card's own `host · profile` shape:
the console's title, GTK's page title, Android's ConsoleHeader, Apple's
navigation title and its console heading.
Verified per platform, none of it on trust:
* console + GTK: fmt, build, clippy `-D warnings` and 85 tests green in the
pf-lxcheck2 container (a Mac `cargo test -p pf-console-ui` compiles nothing).
* Apple: `swift build` green for macOS, iOS and tvOS. Worth all three — the tvOS
pass alone caught `navigationDestination(item:)` needing Hashable, and an
iOS-only screen was confirmed genuinely compiled by planting a type error.
* Android: `compileDebugKotlin` + unit tests, with HomeTilesTest's pin
expectation flipped to match.
* Windows: `cargo check --all-targets` + clippy `-D warnings` on the CI runner,
cold (3m10s) — that client cannot compile on a Mac.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Reported: pinning a host with a profile works when you press the card, but launching a game from that card's library ignores the profile.
That reproduced on the console, and investigating the other clients turned up the same question with a different wrong answer on each. Both commits are here.
The console leak (
f5fa9649)screens/home.rsoffers Y → Library on every paired+saved row, and a pinned card is both. ButLibraryScreen::new()copied the row's address, port, fingerprint and mgmt port and droppedHostRow::pin, so its launch had nothing to send and hardcodedprofile: None— under a comment asserting that game launches follow the host's binding. Pressing the card always passedh.pin.idas the one-off, which is exactly why connecting worked and browsing didn't.The other four (
cc8eb7df)The library was not offered on a pinned card at all. Apple (both UIs:
hasLibrary: profile == nilon the console tile, no menu item on the touch card), Android (hasLibraryrequiredpinnedProfileId == null, and the console host options gated Library onpin == null), GTK (Connect / Copy link / Create shortcut / Unpin — no library) and Windows (pinned tiles had no menu whatsoever). Each was justified as "a pin is a shortcut, not a second host, so host-level actions stay on the host's tile" — right about wake, pair, edit and forget, wrong about the library. Browsing is not a property of the machine: it is this card's connect with a title picked first, which is what a shortcut is for. The library now joins Connect on every pinned surface; the host-level actions stay where they were.The launch dropped the profile. GTK and Windows already carried it (the library clones the card's whole
ConnectRequest/ launches through the shared target the tile parks). Apple did not — the library was keyed on a bareStoredHost, solaunchTitleconnected.inherit. Android was worse, and not only for pins:LibraryScreendialledconnectToHostwith the RAW settings, so a library launch applied no profile at all — not even the host's binding — on every host. Its host list resolvessettings.effectiveFor(profileStore.resolveFor(kh, oneOff)); its library resolved nothing.What changed
The shelf (host + profile), not the host, is now the unit both clients navigate by: Apple gets a
LibraryTargetthreaded throughlibraryTarget, the shell's screen enum, both presentations andSessionModel; Android passes the pinned id intoLibraryScreen, which resolves it through the sameProfileStore.resolveForrule the connect path uses. Falling out of that:profileNamewas never set on that path).punktfunk://browse/<host>honoursprofile=, which it parsed and ignored — refusing an unknown or ambiguous reference exactly as the connect route does (§10.6) instead of degrading to the binding.host · profileshape.Verification
Nothing here was taken on trust; three of the five clients compile nowhere near a default Mac build.
-D warnings, tests (pf-lxcheck2 container)swift build× macOS, iOS, tvOScompileDebugKotlin+ unit testscargo check --all-targets+ clippy-D warnings(CI runner, cold 3m10s)Two of those earned their keep: a Mac
cargo test -p pf-console-uicompiles nothing (everymodiscfg(linux/windows)), and the tvOS pass alone caughtnavigationDestination(item:)requiringHashable— macOS and iOS were both green with it broken. The console's new regression test was checked against a reverted fix and fails with the reported symptom (left: None,right: Some("hdr")); the iOS build was confirmed non-vacuous by planting a type error in the iOS-only screen.The console fix before this one closed the leak on one client. The same question has a different wrong answer on each of the others, so this closes it everywhere: a pinned host+profile card can be browsed, and every title launched off a pinned card's shelf streams with that card's profile. Two shapes of bug, one per client: **The library was not offered on a pinned card at all** — Apple (both UIs: `hasLibrary: profile == nil` on the console tile, no menu item on the touch card), Android (`hasLibrary` required `pinnedProfileId == null`, and the console host options gated Library on `pin == null`), GTK (the pinned card's menu had Connect, Copy link, Create shortcut, Unpin — no library) and Windows (pinned tiles had no menu whatsoever). Each was justified in a comment as "a pin is a shortcut, not a second host, so the host-level actions stay on the host's tile" — right about wake, pair, edit and forget, wrong about the library. Browsing is not a property of the machine: it is this card's connect with a title picked first, which is exactly what a shortcut is for. So the library joins Connect on every pinned surface, and the host-level actions stay where they were. **The launch dropped the profile.** GTK already carried it (its library clones the card's whole ConnectRequest) and Windows already carried it (its library page launches through the shared target, which the tile parks). Apple did not: the library was keyed on a bare `StoredHost`, so `launchTitle` connected with `.inherit` and the host's binding won even from a pinned card. Android was worse still, and not only for pins — `LibraryScreen` dialled `connectToHost` with the RAW settings, so a library launch applied NO profile at all, not even the host's binding, on every host. Its host list resolves `settings.effectiveFor(profileStore.resolveFor(kh, oneOff))`; its library resolved nothing. So the shelf, not the host, is now the unit both clients navigate by. Apple gets a `LibraryTarget` (host + `ProfileSelection`) threaded through `libraryTarget`, the shell's screen enum, both presentations and `SessionModel`; Android passes the pinned id into `LibraryScreen` and resolves it there through the same `ProfileStore.resolveFor` rule the connect path uses. Falling out of that: * a game that exits returns to the shelf it was launched from, pin and all, rather than to the host's default one — `SessionModel.launchedShelf` on Apple, `ActiveSession.libraryProfileId` → `LibraryReturn` on Android. Android also drops a pin that was unpinned while the game ran, rather than reopening a card that no longer exists. * Android's stats overlay finally names the profile a library launch used (`profileName` was never set on that path). * Apple's `punktfunk://browse/<host>` honours `profile=`, which it parsed and ignored — and refuses an unknown or ambiguous reference exactly as the connect route does (§10.6) rather than degrading to the binding. * every shelf says which one it is, in the card's own `host · profile` shape: the console's title, GTK's page title, Android's ConsoleHeader, Apple's navigation title and its console heading. Verified per platform, none of it on trust: * console + GTK: fmt, build, clippy `-D warnings` and 85 tests green in the pf-lxcheck2 container (a Mac `cargo test -p pf-console-ui` compiles nothing). * Apple: `swift build` green for macOS, iOS and tvOS. Worth all three — the tvOS pass alone caught `navigationDestination(item:)` needing Hashable, and an iOS-only screen was confirmed genuinely compiled by planting a type error. * Android: `compileDebugKotlin` + unit tests, with HomeTilesTest's pin expectation flipped to match. * Windows: `cargo check --all-targets` + clippy `-D warnings` on the CI runner, cold (3m10s) — that client cannot compile on a Mac.