The Decky plugin's "update the client" has never once detected an update #128

Merged
enricobuehler merged 1 commits from worktree-decky-client-update into main 2026-08-08 22:21:13 +00:00
Owner

Field report: "the decky plugin still seems to only update itself, and not the actual client."

The code has been on main since 21787ca6 (07-31, shipped in v0.25.0). What was broken is the detection — the QAM has offered to update the client since 0.24 and has answered "up to date" on every Deck, every time, including on a Deck whose client was a day out of date.

Root cause

The check compares the installed OSTree commit against the remote's, and it asked for the remote one with no branch:

flatpak remote-info --user punktfunk-origin io.unom.Punktfunk
→ error: Multiple branches available for io.unom.Punktfunk,
         you must specify one of: …/stable, …/canary

remote-info resolves against the remote, and the punktfunk remote publishes both stable and canary, so the ref is ambiguous and flatpak refuses it rather than picking one. ⚠ Having only one branch installed does not help — the ambiguity is on the remote side. The Deck this was found on has only canary and still hit it.

Then _client_update_state treated the non-zero exit as "remote unreachable — treat as up to date, retry next check", so a call that failed 100 % of the time rendered as good news.

The tell was in the plugin's own log all along, and is easy to read past:

updates: plugin 0.25.11835->0.26.12264 (avail=True), client-> (avail=False)

client-> is empty — no remote commit ever arrived. A genuine up-to-date has a commit there.

By contrast info <id> and update <id> resolve against installed refs, so those were fine with one branch installed and ambiguous only with two (the 08-05 stale-branch trap). This change qualifies all three, so both cases are covered.

What changed

  • New _flatpak_ref() resolves scope + branch once, off the exported tree (~/.local/share/flatpak/app/<id>/x86_64/<branch>/active, then /var/lib/flatpak). No subprocess — _client_argv is on the path of every headless call. The active symlink is what separates an install from the leftovers of one.
  • info / remote-info / update all take <scope> + io.unom.Punktfunk//<branch>.
  • The scope was hardcoded --user: a system-wide install was invisible to the check, and update_client would run a user-scope update that touched nothing and report "already up to date". Both scopes work now.
  • The launcher pins the same --branch= (app id stays last, so _cli_argv's --command= rewrite still holds) ⇒ the client we start is the client we check and the client we update. That also closes the stale-branch trap where a leftover stable silently won the launch.
  • A check that cannot run now reports client_error instead of "up to date" — the flatpak leg finally matches the contract the native leg already had. Dressing that failure up as good news is the entire reason this went unnoticed.
  • The button no longer promises "+ client" when the client is manual-only and the tap can only print a command.

Verification

Both code paths run against the real install on the Deck (192.168.1.253, canary, user scope), minutes apart:

available remote
pre-fix (ships today) False ''
post-fix True ca010668 (installed e22af40f)

flatpak {info,remote-info,update} all accept the id//branch form there. 37 backend checks pass, 6 of them new and about exactly this (python3.13 clients/decky/scripts/test-backend.py — macOS 3.9 chokes on X | None). Frontend tsc + rollup build green.

Not in scope, but found while checking

  • The native one-tap path (apt/dnf/pacman/rpm-ostree via the packaged root helper) has still never been exercised on real hardware — owed since the feature was built, not affected by this change. No box available has a packaged client.
  • pf_update_check::detect::update_command(Flatpak) emits flatpak update --user io.unom.Punktfunk, advertised in its own doc comment as "copy-pastable"; it breaks in the two-branches-installed case. Low severity (only reachable for a flatpak user not going through the plugin). /.flatpak-info carries branch=, so it can be fixed properly rather than by guessing a default.

Still owed

On-glass one-click run, which needs the fixed plugin installed on a Deck — ~/homebrew/plugins is root-owned and deck sudo wants a password.

Field report: *"the decky plugin still seems to only update itself, and not the actual client."* The code has been on main since `21787ca6` (07-31, shipped in v0.25.0). What was broken is the **detection** — the QAM has offered to update the client since 0.24 and has answered "up to date" on every Deck, every time, including on a Deck whose client was a day out of date. ## Root cause The check compares the installed OSTree commit against the remote's, and it asked for the remote one with no branch: ``` flatpak remote-info --user punktfunk-origin io.unom.Punktfunk → error: Multiple branches available for io.unom.Punktfunk, you must specify one of: …/stable, …/canary ``` `remote-info` resolves against the **remote**, and the punktfunk remote publishes both `stable` and `canary`, so the ref is ambiguous and flatpak refuses it rather than picking one. ⚠ **Having only one branch installed does not help — the ambiguity is on the remote side.** The Deck this was found on has only `canary` and still hit it. Then `_client_update_state` treated the non-zero exit as *"remote unreachable — treat as up to date, retry next check"*, so a call that failed 100 % of the time rendered as good news. The tell was in the plugin's own log all along, and is easy to read past: ``` updates: plugin 0.25.11835->0.26.12264 (avail=True), client-> (avail=False) ``` `client->` is **empty** — no remote commit ever arrived. A genuine up-to-date has a commit there. By contrast `info <id>` and `update <id>` resolve against *installed* refs, so those were fine with one branch installed and ambiguous only with two (the 08-05 stale-branch trap). This change qualifies all three, so both cases are covered. ## What changed - New `_flatpak_ref()` resolves **scope + branch** once, off the exported tree (`~/.local/share/flatpak/app/<id>/x86_64/<branch>/active`, then `/var/lib/flatpak`). No subprocess — `_client_argv` is on the path of every headless call. The `active` symlink is what separates an install from the leftovers of one. - `info` / `remote-info` / `update` all take `<scope>` + `io.unom.Punktfunk//<branch>`. - The **scope** was hardcoded `--user`: a system-wide install was invisible to the check, *and* `update_client` would run a user-scope update that touched nothing and report "already up to date". Both scopes work now. - The launcher pins the same `--branch=` (app id stays last, so `_cli_argv`'s `--command=` rewrite still holds) ⇒ the client we start is the client we check and the client we update. That also closes the stale-branch trap where a leftover `stable` silently won the launch. - A check that cannot run now reports `client_error` instead of "up to date" — the flatpak leg finally matches the contract the native leg already had. Dressing that failure up as good news is the entire reason this went unnoticed. - The button no longer promises "+ client" when the client is manual-only and the tap can only print a command. ## Verification Both code paths run against the real install on the Deck (`192.168.1.253`, canary, user scope), minutes apart: | | `available` | `remote` | |---|---|---| | pre-fix (ships today) | `False` | `''` | | post-fix | `True` | `ca010668` (installed `e22af40f`) | `flatpak {info,remote-info,update}` all accept the `id//branch` form there. 37 backend checks pass, 6 of them new and about exactly this (`python3.13 clients/decky/scripts/test-backend.py` — macOS 3.9 chokes on `X | None`). Frontend `tsc` + rollup build green. ## Not in scope, but found while checking - The **native one-tap** path (apt/dnf/pacman/rpm-ostree via the packaged root helper) has still never been exercised on real hardware — owed since the feature was built, not affected by this change. No box available has a packaged client. - `pf_update_check::detect::update_command(Flatpak)` emits `flatpak update --user io.unom.Punktfunk`, advertised in its own doc comment as "copy-pastable"; it breaks in the two-branches-installed case. Low severity (only reachable for a flatpak user not going through the plugin). `/.flatpak-info` carries `branch=`, so it can be fixed properly rather than by guessing a default. ## Still owed On-glass one-click run, which needs the fixed plugin installed on a Deck — `~/homebrew/plugins` is root-owned and `deck` sudo wants a password.
enricobuehler added 1 commit 2026-08-08 22:18:59 +00:00
fix(decky): the client update the plugin offers was never once detected
ci / bun-nix (pull_request) Successful in 21s
ci / web (pull_request) Successful in 1m19s
ci / docs-site (pull_request) Successful in 1m22s
ci / rust-arm64 (pull_request) Successful in 2m22s
ci / rust (pull_request) Successful in 5m23s
bd86598d97
The QAM has offered to update the client since 0.24, and on every Deck it has
answered "up to date" — including right now, with a client a day out of date.

The check asks flatpak for the remote's commit and compares it to the installed
one, and it named the app id with no branch: `flatpak remote-info punktfunk-origin
io.unom.Punktfunk`. The punktfunk remote publishes `stable` AND `canary`, so that
ref is ambiguous and flatpak refuses it — "Multiple branches available" — rather
than picking one. One branch INSTALLED does not help; the ambiguity is on the
remote. The call failed on every box, every time, and the failure returned
`available=False`, which the panel renders as good news. Hence: the plugin
appeared to update only itself.

Every query now names the ref in full, resolved once by `_flatpak_ref()` off the
exported tree (no subprocess — `_client_argv` is on the path of every headless
call). That resolution also carries the SCOPE, so a system-wide install is no
longer invisible to a check that hardcoded `--user`, and the launcher pins the
same `--branch=`, so the client we start is the client we check and update.

A check that cannot run now says so instead of reporting up-to-date: the flatpak
leg reports `client_error` exactly as the native leg already did. Dressing that
failure up as good news is the whole reason this went a week unnoticed.

Also: the button no longer promises "+ client" when the client is manual-only and
the tap can only print a command.

Verified on the Deck (192.168.1.253, canary, user scope) by running both code
paths against the real install, minutes apart:

  pre-fix   available=False  remote=''
  post-fix  available=True   remote=ca010668  (installed e22af40f)

and `flatpak {info,remote-info,update}` all accept the `id//branch` form there.
37 backend checks pass, 6 of them new and about exactly this.
enricobuehler merged commit fd98406868 into main 2026-08-08 22:21:13 +00:00
enricobuehler deleted branch worktree-decky-client-update 2026-08-08 22:21:17 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#128