build(deb): ship the iced UI on the adopted GTK profile - #315
Conversation
The Linux .deb will ship the iced UI as /usr/bin/roost. For existing users to migrate transparently it has to land on the paths the GTK package already owns — same socket, same state.json, same log dir — while dev builds keep the isolated roost-iced profile for side-by-side work. A cargo feature does it. `linux-package` flips only the compiled-in *default* profile; BundleProfile::resolve still lets ROOST_BUNDLE_PROFILE win, which is what makes this safe: every dev harness (ui.py:481, screenshot/lib.sh, run-iced) pins that variable explicitly, so they stay correct no matter how the binary was compiled. Nothing in paths.rs changes — the packaged build resolves an existing profile kind, so the four hand-maintained copies of the resolver (Rust, Swift, Python, Bash) are untouched. The decision is a pure function taking bools rather than an inline #[cfg] so all four cells are testable in one build. The single-instance lock then comes out right for free: lock_path() derives from the profile's socket dir, so a packaged iced and a GTK roost contend on the same flock. An env var baked into the .desktop Exec= was rejected — it covers only the launcher, leaving shell-launched and roostctl-spawned instances on a different profile. Renaming the [[bin]] to `roost` was rejected too: it collides with roost-linux's bin name and wouldn't change profile resolution anyway. Verified on Linux with ROOST_TEST_PANIC (aborts after logging init, before the lock, so nothing binds a socket): dev build creates roost-iced/, packaged creates roost/, and both honor ROOST_BUNDLE_PROFILE overrides in both directions. CodeRabbit adversarial review — three real defects, all fixed here: - The Makefile-only gate was vacuous in CI: ci.yml never invokes check-iced, so the feature would still have first compiled during a release build. The --features pair is now mirrored into the iced job. - The feature-gated test was tautological, asserting a locally re-evaluated cfg! rather than the value main reads. main now hoists PACKAGED / PACKAGED_PLATFORM consts and the test asserts against PACKAGED, so it fails if the gate stops reaching the profile decision. (Verified the complementary protection too: a misspelled feature name is an unexpected_cfgs error under -D warnings.) - The macOS-guard comment was factually wrong: Gtk on macOS resolves Roost-gtk, the GTK *dev* profile, not the Swift app. Corrected — the hazard is colliding with the macOS GTK binary, not with Roost.app. Also noted and documented: --all-features would hand a dev binary the production profile, so Cargo.toml warns against it. Reviewer finding accepted as pre-existing, not fixed here: processes that disagree on XDG_RUNTIME_DIR take different locks over one state.json. That shape predates this change and already applied gtk-vs-gtk; it is recorded in the plan's risks and on Charlie's checklist because this commit removes the GApplication D-Bus backstop that partially covered it. Test-suite note: one run of single_instance::drop_releases_so_next_acquire _succeeds failed during a concurrent full-workspace run. It passes on a clean tree and 3/3 with this change applied, in a crate this commit does not touch — recorded as an observed flake rather than silently ignored. Plan 022 C3 (workstream B). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgxiEBQTqgNjPswKqcs12d
C3 made a packaged Linux build resolve the Gtk profile, but two identity strings stayed hardcoded to `ai.stridelabs.Roost.iced`: the winit `application_id` (WM_CLASS on X11, app_id on Wayland) and the freedesktop `desktop-entry` notification hint. Left alone, the packaged build would report `ai.stridelabs.Roost.gtk` over IPC while its window announced `ai.stridelabs.Roost.iced`. The deb installs `ai.stridelabs.Roost.gtk.desktop` with a matching StartupWMClass, so no entry would match the window: generic icon in the dock and app switcher, no window grouping, StartupNotify never resolving, and a dangling notification icon hint. That is visibly worse than the GTK package it replaces — a migration users would notice. Both now come from the resolved profile, so the shipped binary matches the shipped desktop entry with no change to packaging/ at all. This is the Linux half of #303's desktop-entry note, folded in where it was free. The notifications half needed care. `backend::show` is handed to `spawn_on` under `F: Fn(Payload) -> Fut`, a bound five unit tests implement via their own `recording_backend`. Threading an app_id parameter through would have changed the bound and all five tests, so `new` takes the id and captures it in a closure instead: the bound, the tests, and the backend shape are untouched. Verified on Linux under Xvfb, reading the real window property rather than inferring it — dev build: WM_CLASS = "ai.stridelabs.Roost.iced"; packaged build: WM_CLASS = "ai.stridelabs.Roost.gtk". Also cross-compiled to x86_64-unknown-linux-gnu so the Linux-only cfg branches are type- and lint-checked from the Mac. Gates: check-iced clean with and without the feature, e2e-iced-ci 84 passed / 3 known skips (including the walking-skeleton assertion that a dev build still identifies as .iced). Plan 022 C4 (workstream B). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgxiEBQTqgNjPswKqcs12d
The package now builds roost-iced with --features roost-iced/linux-package and stages it as dist/roost. Package name, install paths, desktop-entry basename and icons are all unchanged: apt-charliek globs roost_*.deb, and the installed ai.stridelabs.Roost.gtk.desktop is exactly the identity the packaged binary announces after C4. GTK leaves the package; its source stays in-repo until the separate retirement decision. The dependency list is the dangerous part and it is measured, not reasoned. `readelf -d` on a release roost-iced lists three NEEDED entries — libc, libgcc_s, libm — because winit, wgpu and ash dlopen the whole graphics and input stack. A conventional ldd-derived Depends would therefore have been two packages: it would have built, installed cleanly on every machine, and passed every automated gate, then failed to launch for anyone whose system didn't already carry the stack from the old GTK package. So the list comes from `strace -f -e trace=openat` on a real launch, mapped through dpkg -S: 48 packages opened, reduced to the ten the application itself loads. The rest is Mesa's transitive closure, which its own Depends pull and which must not be pinned here. Vulkan is Recommends, not Depends, on measured evidence: with /usr/share/vulkan/icd.d removed entirely the app still opens a window — wgpu reports `Available adapters: []` and uses the software renderer compiled in beside it. Recommends is on by default for ordinary apt installs, so users still get GPU acceleration; only --no-install-recommends drops to software, and that works. Proven end to end in a pristine ubuntu:24.04 container: installed with --no-install-recommends (so no libvulkan1, no ICD) and the app opened a window. desktop-file-validate passes, and the installed entry's StartupWMClass matches the WM_CLASS the packaged binary was observed to announce. Note for local builds: build-deb.sh clears dist/ but not out/, so repeated local builds at different versions accumulate .debs there and would trip release.yml's exactly-one-match upload assertion. CI always builds from a fresh checkout, so this is a local-only wart, called out rather than fixed inside a packaging swap. Plan 022 C5 (workstream B). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgxiEBQTqgNjPswKqcs12d
Two changes to the linux release job, both consequences of the package now containing the iced UI instead of GTK. The apt set drops libgtk-4-dev and libadwaita-1-dev: build-deb.sh no longer builds roost-linux, so they were pure install time. pkg-config and libclang-dev stay (bindgen for roost-vt), and the iced-release CI job's display + graphics stack joins them because the new smoke step has to actually launch the binary. The smoke step is the more important half. The mac job has validated its shipped bundle since #122; the linux job has never launched what it uploads. Nothing upstream of it can catch the two failure modes this swap introduces — staging the wrong binary, or building without the linux-package feature — because both produce a package that builds, lints and installs perfectly and only misbehaves by binding the wrong IPC namespace at runtime. So the step runs the staged dist/roost in a throwaway XDG sandbox with ROOST_BUNDLE_PROFILE deliberately UNSET, and asserts the socket lands under the production roost/ namespace with no roost-iced/ directory created. It waits on a successful `identify` round-trip rather than on the socket file appearing. That distinction is load-bearing and was learned during this plan's shed verification: a killed predecessor leaves a stale socket behind, so a file-existence check can pass against a UI that is not listening. Plan 022 C6 (workstream B). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgxiEBQTqgNjPswKqcs12d
The iced-release lane built the featureless dev configuration, so linux-package — the thing that makes a packaged build adopt the production roost profile — would have compiled for the first time during an actual release, with users on the other side of the deb upgrade. The existing build now carries the feature. Deliberately ONE build, not two: [profile.release] is lto = "thin" + codegen-units = 1, and a second cold LTO link would blow the job's 45-minute budget. The packaged binary is also the right thing to be running the e2e subset against, since it is what ships. That subset still exercises it on the isolated dev profile, because ui.py pins ROOST_BUNDLE_PROFILE=iced for every iced launch and the env override outranks the compiled-in default. That is genuinely surprising to read, so the step now says so. The new assertion step is what actually proves adoption: same already-built binary, ROOST_BUNDLE_PROFILE left unset, isolated XDG dirs, and a check that the socket lands under the production roost/ namespace with no roost-iced/ directory. It waits on an `identify` round-trip rather than the socket file, for the stale-socket reason recorded in C6. Plan 022 C7 (workstream B). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgxiEBQTqgNjPswKqcs12d
Four docs went stale the moment the deb swapped binaries. paths.md asserted the Iced profile is "always isolated" — no longer unconditionally true. Its per-binary table row and the surrounding prose now distinguish a packaged Linux build (resolves the production `roost` namespace) from dev builds and every other platform (keep `roost-iced`), and note that ROOST_BUNDLE_PROFILE still overrides either. installation.md required GTK4 + libadwaita dev packages to build the Linux UI. That is now only true for building roost-linux from source; the package itself needs neither. Split so both audiences get accurate instructions, without overclaiming — roost-linux still exists and is still built and tested in CI. linux/README.md was five lines describing an AppImage, tonic over a Unix socket, a roost-core daemon, and "Phase 0 placeholder". None of that has been true for a long time. Rewritten to describe what the directory actually does. The roadmap's M4 section records what shipped against the two decisions, with the mechanism and the evidence, and states plainly that cutting the release is a separate manual step that has not happened. It does not claim M4 is complete. Its #309 deferred list also lost the "iced release-profile CI" entry, which shipped as the iced-release job. The implementing agent flagged that it could not verify the "real apt upgrade transaction" claim from repo sources alone — correct, and worth recording: the evidence is the container transcript (`Unpacking roost (0.0.17~iced1) over (0.0.17~gtkbase)`) captured in the plan's artifacts, not anything in the tree. Plan 022 C8 (workstream B). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgxiEBQTqgNjPswKqcs12d
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 29 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe change adds a ChangesLinux Iced packaging
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant CI
participant PackagedBinary
participant Xvfb
participant roostctl
participant XDGFilesystem
CI->>Xvfb: start virtual display
CI->>PackagedBinary: launch dist/roost
PackagedBinary->>XDGFilesystem: create production roost runtime paths
CI->>roostctl: poll identify
roostctl->>PackagedBinary: request IPC identity
PackagedBinary-->>roostctl: return socket information
CI->>XDGFilesystem: verify roost namespace
CI->>XDGFilesystem: reject roost-iced namespace
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Around line 227-293: Update the “Smoke the packaged artifact” step to install
the generated out/*.deb in a clean Ubuntu 24.04 rootfs or container before
testing. Launch /usr/bin/roost and invoke /usr/bin/roostctl identify from that
installed environment, preserving the existing IPC namespace and failure checks
while validating the package file list and declared Depends closure.
In `@docs/development/iced-migration-roadmap.md`:
- Line 625: Update the wording in the iced migration roadmap around “Verified
end to end” to use the hyphenated compound modifier “Verified end-to-end,”
preserving the surrounding text.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6181a677-51cf-469e-a541-820d445189c8
📒 Files selected for processing (14)
.github/workflows/ci.yml.github/workflows/release.ymlMakefilecrates/roost-iced/Cargo.tomlcrates/roost-iced/src/app.rscrates/roost-iced/src/main.rscrates/roost-iced/src/notifications.rsdocs/development/iced-migration-roadmap.mddocs/getting-started/installation.mddocs/reference/paths.mdlinux/README.mdlinux/scripts/build-deb.shpackaging/nfpm.yamlpackaging/roost.desktop
The new `--features linux-package` test pass added in C3 immediately earned its keep: it turned ubuntu's iced-build-e2e red on this branch. panic_hook_test clears ROOST_BUNDLE_PROFILE on purpose, so the child resolves its compiled-in default — which is exactly what the packaging feature changes. The crash report stamps profile.app_label, so under `--features linux-package` on Linux it reads "Roost-gtk" while the test asserted the literal "Roost-iced". The expectation now follows the same two cfg!s main does. Integration tests compile with the crate's feature set, so they resolve identically here. Worth recording why the local gate missed it: on macOS the feature is inert by design, so `make check-iced` passes both ways on this machine and only a Linux build can fail this. Verified in a Linux VM — 2 passed with and without the feature — as well as on macOS. Plan 022, fix on workstream B. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgxiEBQTqgNjPswKqcs12d
…ayload Codex review of PR #315 — four findings, all real, all fixed. The glibc floor was wrong and would have produced exactly the failure this plan's dependency work exists to prevent. `objdump -T` on the release binary shows non-weak GLIBC_2.35 imports (hypot/hypotf) and GLIBC_2.34 (pthread_*, __libc_start_main); the 2.39 references are weak and don't raise the floor. The package advertised `>= 2.31`, inherited from the GTK package — so it would install happily on Ubuntu 20.04 or Debian 11 and then die in the dynamic loader before a window appeared. Now `>= 2.35`, which fails at install time with a comprehensible message instead. A font is now a hard dependency. Codex predicted startup could terminate on a font-less system; measured, it does not — a --no-install-recommends install into a container with zero files under /usr/share/fonts and no fontconfig still opened a window. But a terminal with no monospace glyphs isn't usable even when it boots, so fonts-dejavu-core is guaranteed rather than hoped for. (Verified the rebuilt package pulls 8 font files.) The release smoke tested the wrong artifact. It ran ./dist/roost — the binary build-deb.sh staged — which proves the binary works while leaving every packaging-layer mistake (a wrong `contents:` destination, a dropped entry, a lost exec bit) to be found by users. It now extracts the built .deb with dpkg-deb and smokes the packaged /usr/bin/roost and /usr/bin/roostctl, asserting both are present and executable first. Packaging changes could also skip CI entirely: `iced-release` gated on rust/tests/ci only, so a build-deb.sh-only PR never reached it and a nfpm.yaml-only PR matched no filter at all. `packaging/**` joins the `linux` filter and `iced-release` now gates on it — otherwise a packaging-only change compiles the linux-package feature nowhere. Finally, the roadmap's fixed guardrails still told contributors that GTK is the shipped Linux UI and roost-iced is "absent from release artifacts", directly contradicting the packaging note added in C8. Both corrected, with the M4 caveat spelled out: incompleteness in a *packaged* Linux build now does reach users, which is what the entry criteria exist to gate. Verified: deb rebuilt in a VM, control fields checked, payload extracted and both binaries confirmed executable, then installed --no-install-recommends in a pristine ubuntu:24.04 container and launched — IPC ready on /tmp/rt/roost/roost.sock, the production namespace. Plan 022, review fixes on workstream B. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgxiEBQTqgNjPswKqcs12d
Codex review — findings and dispositionsCodex reviewed this PR (no Critical/High). All four findings were real and are fixed in 3699b4b. 1. glibc floor was wrong (Medium) — fixed. 2. Font availability (Medium) — investigated, partially confirmed, fixed. The predicted failure was startup termination on a font-less host. Measured: it does not happen — a 3. Packaging changes could skip CI, and the smoke tested the wrong artifact (Medium) — both fixed.
4. Roadmap guardrails contradicted the packaging (Low) — fixed. The fixed guardrails still said GTK ships to Linux users and Verification of the fixesDeb rebuilt in a Linux VM, control fields checked, payload extracted and both binaries confirmed executable, then installed Separately, CI caught a real one
Worth noting: that test only runs in the packaged configuration because CodeRabbit's C3 review caught that the |
CodeRabbit review of PR #315. Major: the smoke step extracts the .deb, which validates the payload — file list, destinations, exec bits — but not the Depends closure. The release runner already carries the whole graphics stack from the build step, so a Depends: line that forgot a library would still launch there and ship. Missing runtime dependencies are the failure users actually hit: the package installs cleanly and then won't start. So the closure is now checked where nothing is pre-installed — a clean ubuntu:24.04 container, installed with --no-install-recommends so only what Depends names is present. That also continuously proves the Recommends-vs-Depends split, since the container gets no Vulkan loader at all and the software fallback has to carry it. The extraction-based step stays: it is fast, needs no container, and covers the packaging-layer mistakes the install cannot distinguish from a build problem. The two check different things. Minor: hyphenated "end-to-end" in the roadmap. This is the same shape as the shed verification already recorded for this plan, promoted into the release path so it keeps holding. Plan 022, review fixes on workstream B. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgxiEBQTqgNjPswKqcs12d
What this is
Workstream B of plan 022: the Linux
.debnow ships the iced UI as/usr/bin/roost, adopting the GTK profile in place. Existing users upgradewith no migration step — same socket, same
state.json, same log dir — soroostctland the Claude hooks keep working untouched.Per the recorded M4 decisions: in-place profile adoption, clean swap, no
beta/opt-in phase. GTK leaves the package; its source stays in the repo and
stays built and tested in CI until the separate retirement decision.
This does not cut a release. The packaging is built so the next tag simply
has it.
Commits
feat(iced)linux-packagecargo feature flips the compiled-in default profile toGtkon Linux only, via a pure function unit-tested over all four cellsfix(iced)application_idand the notificationdesktop-entryhint derive from the resolved profile (the Linux half of #303)build(deb)build-deb.shbuilds iced with the feature and stages it asroost;Depends:derived from a runtime traceci(release)ci(iced)docslinux/README.md/ roadmap M4The design, and why
ROOST_BUNDLE_PROFILEstill overrides the compiled-in default, and every devharness (
ui.py:481,screenshot/lib.sh,run-iced) pins it explicitly — sodev and test behavior cannot break, by construction rather than by convention.
Nothing in
paths.rschanged: the packaged build resolves an existingprofile kind, so the four hand-maintained copies of the resolver (Rust, Swift,
Python, Bash) are untouched. The single-instance lock then comes out right for
free, because
lock_path()derives from the profile's socket dir.Rejected alternatives: an env var in the
.desktopExec=(covers only thelauncher — shell-launched and
roostctl-spawned instances would land on adifferent profile, which is the worst possible failure shape); renaming the
[[bin]]toroost(collides withroost-linux's bin name, and wouldn'tchange profile resolution anyway).
The dependency set is measured, not reasoned
This is the part that would have shipped broken.
readelf -don a releaseroost-icedlists threeNEEDEDentries —libc,libgcc_s,libm—because winit, wgpu and ash
dlopenthe entire graphics and input stack. Aconventional
ldd-derivedDepends:would therefore have been two packages:it would have built, installed cleanly on every machine, passed every
automated gate, and then failed to launch for anyone whose system didn't
already carry the stack from the old GTK package.
So the list comes from
strace -f -e trace=openatof a real launch, mappedthrough
dpkg -S: 48 packages opened, reduced to the ten the applicationitself loads. Mesa's ~30-package closure comes via its own
Dependsand isdeliberately not pinned.
Vulkan is
Recommends:, on evidence. With/usr/share/vulkan/icd.dremoved entirely the app still opens a window, and separately in a container
with no
libvulkan1at all it opened a window withiced_wgpuloggingAvailable adapters: []— the software renderer compiled in beside wgpu.Ordinary
apt installstill pulls Vulkan (recommends are on by default);--no-install-recommendsdegrades to software instead of failing.Verification
Full evidence:
~/.claude/plans/roost/022-deb-swap/shed-deb/adoption-proof.md.A real
aptupgrade transaction, not a hand-seeded state file. The GTK debbuilt from
main@ac472cewas installed and run, authoring a 3-project /4-tab layout with the shipping GTK binary. The iced deb then installed over it:
No file conflicts, no prompts.
/usr/bin/roost: 7,815,104 → 25,924,712 bytes.Post-upgrade, with an unmodified
roostctl(no flags, no env):…and the GTK-authored layout restored intact, cwds preserved (tab ids are new
because relaunch re-opens tabs as fresh shells — documented behavior).
roostctl tab openthrough the adopted socket returned a new tab id.Cross-toolkit single-instance: the old GTK binary launched against the
running packaged iced UI dialed the shared socket, sent
app.activateto it,and exited — one lock, one writer, across two toolkits. A
SIGKILL'd instanceleaves a stale socket and lock, and the next launch recovers cleanly.
Desktop identity, read from the real window property under Xvfb:
matching the installed
ai.stridelabs.Roost.gtk.desktopand itsStartupWMClass.desktop-file-validatepasses.Regressions — dev profiles untouched (shed, X11):
tools/roosttest)GTK's pass count is identical to plan 021's baseline; the skip delta (11→16)
is the five IME tests PR #313 added, which skip on GTK where
tab.feed_imeisdeliberately not-implemented.
Mac loop per commit:
check-icedclean with and without the feature,cargo test --workspace,e2e-iced-ci84 passed / 3 known skips.Review findings and dispositions
CodeRabbit's adversarial pass on C3 found three real defects, all fixed before
commit:
ci.ymlnever invokesmake check-iced, so the packaging feature would still have first compiled duringa release build, which is exactly what the gate claimed to prevent. The
--featurespair is mirrored into the iced job.re-evaluated
cfg!rather than the valuemainreads.mainnow hoistsPACKAGED/PACKAGED_PLATFORMconsts and the test asserts against those.(Verified the complementary protection: a misspelled feature name is an
unexpected_cfgserror under-D warnings.)adopting the Swift
Roost.appprofile; on macOSGtkresolvesRoost-gtk,the GTK dev profile. Corrected in code and plan.
Also applied from review:
Makefileadded to thetestspath filter (thelane's test list lives there, so editing it must retrigger the lane).
Accepted risks
XDG_RUNTIME_DIR→ two locks over onestate.json.lock_path()keys off
$XDG_RUNTIME_DIRwhilestate_dirkeys off$XDG_DATA_HOME, sotwo processes disagreeing on only the former take different flocks over the
same state file (desktop session vs. an ssh/cron context where
XDG_RUNTIME_DIRis unset). Pre-existing and already applied gtk-vs-gtk,but noted because this PR makes iced and GTK share a state namespace for the
first time and removes the partial backstop — GApplication's D-Bus
uniqueness is a GTK construct a packaged iced never registers. A real fix
keys the lock to the state dir: architectural, not packaging. Flagged for
Charlie's decision before the release.
--all-featureswould hand a dev binary the production profile. No suchinvocation exists in the repo; documented next to the feature.
Ubuntu with older Mesa.
build-deb.shclearsdist/but notout/, so repeated local buildsaccumulate
.debs and would trip release.yml's exactly-one-match uploadassertion. CI always builds fresh; called out rather than fixed inside a
packaging swap.
Impact
No dependency added to any Rust crate. No privacy or secret impact. The
package's
Depends:changes substantially (GTK out, iced runtime stack in) —that is the point, and it is measured. Package name, install paths, desktop
entry basename and icons are all unchanged, so
apt-charliek'sroost_*.debglob and users' pinned launcher items keep working.
Plan 022 § D1–D3 — the pinned design
D1 — Profile adoption: a cargo feature on
roost-iced.linux-packageflips the compiled-in default via
default_profile_kind(packaged, linux),called with
cfg!(feature = "linux-package")andcfg!(target_os = "linux").Parameterized rather than a bare
#[cfg]so all four cells are unit-testablein a single build.
ROOST_BUNDLE_PROFILEstill wins. Thelinuxguard keepsit inert on macOS, where
Gtkresolves the GTK dev profile and wouldcollide with the macOS GTK binary CLAUDE.md supports running side by side.
D2 — Identity strings derive from the resolved profile. Packaged builds
announce
ai.stridelabs.Roost.gtk, matching the already-installed desktopentry, so taskbar grouping and notification identity are correct with no
.desktopchange. The.gtksuffix is deliberately kept: renaming it is auser-visible migration (pinned launcher items keyed on
StartupWMClass).D3 — Package contents. Name stays
roost;dist/roost→/usr/bin/roostis the existing contract.
Depends:derived fromreadelf+strace+validated by a
--no-install-recommendsinstall in a minimal environment.Plan:
~/.claude/plans/roost/022-deb-swap.md. Sibling: #314 (W-A, merged).W-C (#311 badge) follows separately.
🤖 Generated with Claude Code
https://claude.ai/code/session_01SgxiEBQTqgNjPswKqcs12d
Summary by CodeRabbit
New Features
.debpackages now use the Iced desktop interface.Documentation
Bug Fixes