From d84491c0ececbf2217e092a9e76ba19397cb913b Mon Sep 17 00:00:00 2001 From: SAPHID Date: Mon, 26 Oct 2026 08:23:50 +0000 Subject: [PATCH] fix(framey): project installed metadata for Junk-Store About --- .../framey-installed-context-2026-10-26.json | 140 ++++++++++++++++++ .../framey-installed-context-2026-10-26.md | 62 ++++++++ docs/framey-compatibility.md | 4 +- scripts/framey-compatibility-doc.py | 2 +- tests/page/framey_actions.mjs | 15 +- tests/test_framey_panel.py | 14 +- ui/framey-compatibility.json | 9 +- ui/framey-host.js | 11 +- ui/framey_panel.py | 3 +- 9 files changed, 248 insertions(+), 12 deletions(-) create mode 100644 docs/evidence/framey-installed-context-2026-10-26.json create mode 100644 docs/evidence/framey-installed-context-2026-10-26.md diff --git a/docs/evidence/framey-installed-context-2026-10-26.json b/docs/evidence/framey-installed-context-2026-10-26.json new file mode 100644 index 0000000..8a91c78 --- /dev/null +++ b/docs/evidence/framey-installed-context-2026-10-26.json @@ -0,0 +1,140 @@ +{ + "checkedAtUtc": "2026-10-26T08:14:37.777131+00:00", + "plugin": { + "id": 84, + "name": "Junk-Store", + "version": "1.1.13", + "sha256": "f7ad2a0103c85bf308929b2e96ec9aeeb0d76bcc6ae97eb1e4b5f31ffba0cbb1" + }, + "projection": { + "field": "local_per_client_data.installed", + "wire": "optional ninth scalar: boolean or null", + "unknown": "null creates an empty detached metadata object; installed stays undefined", + "privateFieldsCopied": false, + "oldEightScalarRows": "accepted; running updates preserve existing library metadata", + "liveObservation": false + }, + "realFrame": { + "os": "Frame OS 0.5.6", + "arch": "aarch64", + "uid": 1000, + "decky": { + "version": "v3.3.0", + "sha256": "8ef5dcf36cbbbb6e97201a6baed5d79c4cc44ec27789fc9af1c5b6ae8f0b25b6" + }, + "transport": "trusted SSH through lxso2", + "sharedLock": true, + "runtime": "private bounded removable user service; not enabled", + "library": { + "apps": 1106, + "games": 895, + "installedTrue": 41, + "installedFalse": 0, + "installedUnknown": 1065 + }, + "protonRuntimeInstalledFields": "undefined for both EasyAntiCheat and BattlEye; no false value invented", + "legendaryInstalled": false, + "allowedMethods": [ + "execute_action", + "get_websocket_port" + ], + "staticActions": [ + "MainMenu/GetContent", + "init/InitActions" + ], + "noMutationActions": true + }, + "localBrowser": { + "host": "actual headset.html/framey-host.js, vendored React, opaque iframe", + "backend": "exact official ARM64 plugin through production receipt-bound bridge", + "library": "production receipt-bound snapshot from real Frame CEF", + "outerStatusInstallCheck": "fixture responses", + "passed": [ + "main menu", + "About content and tab list", + "Dependencies tab controls render; no buttons invoked", + "Back to main menu", + "close/reopen and About again" + ], + "limits": [ + "optional localhost websocket blocked by test relay CSP", + "RSS calls denied", + "running-context relay returns fixture/unavailable data; no lifetime claims", + "outer extension/status fixture is incomplete; unrelated outer status/extension errors excluded from plugin claim", + "deployed headset HTTP endpoint unverified", + "worn VR unverified" + ] + }, + "cleanup": { + "initialSshStop": "timed out while test unit deactivating", + "recovery": "SIGKILL to exact owned unit only after matching lock owner; private files removed", + "result": { + "baseAbsent": true, + "unitAbsent": true, + "archiveAbsent": true, + "state": "MainPID=0\nLoadState=not-found\nActiveState=inactive\n", + "normal": { + "frame-control-decky.service": "inactive", + "frame-control-headset.service": "inactive" + }, + "battery": [ + 99 + ] + }, + "freshLockedAudit": { + "services": 1, + "allArtifactsAbsent": true, + "allUnloadedInactivePidZero": true + }, + "lockReleased": true, + "batteryBeforeAfter": [ + [ + 99 + ], + [ + 99 + ] + ], + "powerSettingChanged": false, + "localRelayAndBrowserClosed": true + }, + "verification": { + "python": { + "run": 1628, + "skipped": 16, + "failures": 0 + }, + "panelFocused": { + "run": 22, + "failures": 0 + }, + "nodeContracts": [ + "framey_actions.mjs", + "framey_running.mjs" + ], + "siteTestFiles": { + "passed": 2, + "failures": 0 + }, + "fakeFrame": { + "blocked": 56, + "installed-removed": 60 + }, + "officialStore": { + "plugins": 116, + "errors": 0, + "identitySetUnchanged": true, + "checkedAtUtc": "2026-10-26T08:21:01.653768+00:00" + } + }, + "status": "partial", + "unverified": [ + "dependency/runtime installation", + "login", + "game listing/download/launch", + "RSS", + "optional websocket operations", + "Steam shortcut mutation", + "worn VR" + ] +} diff --git a/docs/evidence/framey-installed-context-2026-10-26.md b/docs/evidence/framey-installed-context-2026-10-26.md new file mode 100644 index 0000000..1ca688f --- /dev/null +++ b/docs/evidence/framey-installed-context-2026-10-26.md @@ -0,0 +1,62 @@ +# Junk-Store About and read-only installed metadata + +Junk-Store 1.1.13 (official ID 84) now renders its About and Dependencies tabs +without the `local_per_client_data.installed` crash recorded in the +[script-entrypoint audit](framey-script-entrypoints-2026-10-26.md). It remains +**partial**. The catalog and both store views use the same updated reason. + +The library snapshot omitted the entire `local_per_client_data` object. The +official frontend dereferences it to display its dependency controls. Under the +shared headset lock, a read-only probe confirmed that the real Frame exposes +this object for the two Proton runtimes, but neither has an `installed` boolean. +Across 1,106 app overviews, 41 had `installed: true`, none had `false`, and 1,065 +had no installed boolean. Those absent values must not be relabelled as false. + +The snapshot adds one bounded scalar per app: a Steam-supplied boolean or null. +Framey's detached overview creates a minimal metadata object, copying `installed` +only for a boolean. Null leaves that property undefined. No client IDs, paths, +other metadata, live Steam objects or methods cross the iframe boundary. Eight +scalar rows remain accepted, and running-app updates preserve library metadata. +The snapshot still refreshes at panel open; it is not a live installation feed. + +## Actual ARM64 backend and local Chromium + +The unmodified, hash-verified official archive ran in published Decky 3.3.0 ARM64 +on Frame OS 0.5.6 using a private, bounded, removable user service. Legendary was +absent; no dependency installer, runtime install, login, game operation or custom +backend download was invoked. The exact plugin and loader digests are in the +[redacted results](framey-installed-context-2026-10-26.json). + +Local Chromium ran the actual Framey host and official frontend in an opaque +iframe. The production receipt-bound bridges read the real Steam library and +called the official ARM64 backend's static menu actions. About content and its +tab list, Dependencies controls, Back, close/reopen, and About again passed. +No dependency buttons were pressed. The real library counts matched the probe. + +The optional localhost websocket was blocked by the test relay CSP; RSS was +deliberately denied. Outer status/install/check replies were fixtures; incomplete +outer extension/status and running-context replies are excluded from the plugin +claim. The screenshot still used the catalog reason from before this follow-up. +Dependency installation, login, game operations, RSS, websocket operations, +shortcut changes, deployed headset HTTP and worn VR remain unverified. + +## Cleanup and regression checks + +The initial cleanup SSH timed out while the private Decky unit was deactivating. +Recovery checked the matching lock owner, killed only that exact test unit, and +removed its private base, unit file and archive. A fresh locked audit independently +confirmed unloaded/inactive/PID 0 and no artifacts, then released its lock. Normal +managed Decky/panel services stayed inactive; battery was 99 → 99. No power-setting +action ran. Local relay and owned browser tab are closed. + +- Full Python suite: 1,628 tests, 16 skipped, no failures; focused panel: 22 passed. +- Framey action/running Node contracts passed, including true/false/unknown, + malformed metadata, detached values, old rows and running-update preservation. +- Both site test files passed. Generated docs and diff checks passed. +- All 116 exact official archives passed the fake-Frame audit: 60 installed/removed, + 56 expected blocks; disposable project removed. Backend execution is stubbed in + that harness. The fresh official store has 116 entries, no audit errors, and an + unchanged ID/version/digest set. + +No new works claim is made. Catalog totals remain **4 works / 49 partial / 52 broken +/ 11 N/A**; only Junk-Store's verified scope and remaining limits change. diff --git a/docs/framey-compatibility.md b/docs/framey-compatibility.md index bbee6a4..873c3f0 100644 --- a/docs/framey-compatibility.md +++ b/docs/framey-compatibility.md @@ -27,7 +27,7 @@ Open the browser audit URL in local Chromium, run `runAudit()` and inspect/downl The current published digests are pinned in the JSON; if the store changed, retain/download those exact archives for reproduction. Audit downloads permit larger ZIPs for inspection; production installer limits remain 50 MiB ZIP / 150 MiB unpacked / 30 MiB per member. -After the VR job released the shared lock, this audit acquired it and used a private temporary Decky 3.3.0 ARM64 user service. Read-only ProtonDB `get_setting` and Steam Status `get_settings` calls succeeded through the live Steam CEF websocket. The service and private base were removed and the lock released; normal managed services stayed inactive, battery was 100 → 99, and no power setting was changed. [Redacted results](evidence/framey-official-backend-2026-10-25.json) record exactly what ran. A [2026-10-26 follow-up](evidence/framey-events-backend-2026-10-26.json) verified default Notebook drawing/save/reopen across a real backend restart and Simple Timer start/cancel, countdown/completion events, a visible toast and persisted recents/subtle mode. Only private test settings were written. Simple Timer remains partial because its own expired-timer startup raises a missing `timer_task` error; sound/suspend are unsupported. A [local audio follow-up](evidence/framey-audio-backends-2026-10-26.md) exercised Volume Mixer, Volume Boost and Mixer through real Decky 3.3.0 amd64 backends on private silent virtual audio targets. Sliders, numeric volume entry, independent server readbacks and Mixer filter persistence across a runtime restart passed. An [ARM64 audio follow-up](evidence/framey-audio-arm64-2026-10-26.md) verified the unmodified official backends in actual Decky 3.3.0 on the Frame user audio session. Volume Mixer and Mixer now qualify as works for per-app volume: Chromium controls changed only a named silent stream, with independent Pulse readbacks. That run left Volume Boost partial because sink writes were denied. A [Volume Boost follow-up](evidence/framey-volume-boost-arm64-2026-10-26.md) now verifies linked/separate channel controls, re-link and reopen through the actual ARM64 backend. Independent Pulse reads confirmed 125%/150% gain writes while the default sink stayed muted; exact original volume/mute was restored. Volume Boost qualifies as works for sink controls. Audible output, Bluetooth playback and worn VR remain unverified. A [running-context follow-up](evidence/framey-running-context-2026-10-26.md) supplies detached running-app/main-app reads and relays actual Steam lifetime callbacks while a panel is open. Real Frame checks verified an empty list, registration/cleanup and the exact Discord Status ARM64 backend reporting no IPC connection. Nonempty games and transitions were synthetic fixtures; real game events, Discord activity and worn VR remain unverified. Discord Status moves to partial; no new works claim is made. A [library context follow-up](evidence/framey-library-context-2026-10-26.md) read 1,106 app overviews, 895 game IDs and the current public Steam ID from real Frame CEF under the shared lock. Local Chromium replayed this detached snapshot to render Deck Settings game selection, Steamback's empty save panel and PlayTime statistics/settings. Steamback and PlayTime used actual Decky 3.3.0 amd64 backends in an isolated container; their ARM64 backends and automatic game hooks remain unverified. Shotty's frontend now starts, but its published backend fails on missing `click`; it stays broken. A [chunked frontend follow-up](evidence/framey-deck-shelves-browser-2026-10-26.md) loaded Deck Shelves 2.0.0 through a managed JavaScript module route and rendered its settings in an opaque local Chromium panel. Its backend was denied; Steam Home shelves and worn VR remain unverified. A [local module lookup follow-up](evidence/framey-module-lookup-2026-10-26.md) supplies a finite registry of Framey-local controls and CSS. TabMaster QAM/docs now render; Steam library-tab editing remains unavailable. That pass exposed Junk-Store executable-script extraction failures; the follow-up below repairs them. SteamGridDB still requires Steam context-menu internals and DeckSP still fails its published MobX fallback. That pass tested TabMaster/Junk-Store in actual isolated amd64 Decky, without ARM64 or worn-VR coverage. A [script entrypoint follow-up](evidence/framey-script-entrypoints-2026-10-26.md) grants owner execute to shebang scripts and ZIP-declared executables without copying archive privilege/write bits. Junk-Store now passes static menu/backend calls in actual Decky 3.3.0 ARM64 and local Chromium Games/Epic navigation, back and reopen. It moves to partial: About still crashes on missing installed-app metadata, Legendary is absent, and login/game operations remain unverified. Existing installations require update/reinstall to repair permissions. Beyond the documented checks, official backends remain unexecuted. The separate VR job proves Frame Control/sample compositor rendering, not any official plugin. No worn-VR official-plugin result is claimed. A [local UI follow-up](evidence/framey-ui-fallbacks-2026-10-26.json) rendered Pause Games and Tailscale Control settings with all backend/network actions denied; it does not verify process pausing or Tailscale changes. A [read-only Steam bridge follow-up](evidence/framey-steam-reads-2026-10-26.json) verified actual Frame country, install folders and system information, plus IsThereAnyDeal's country dropdown in local Chromium. The installed-plugin status was fixture-shaped in that relay; plugin backends stayed denied and the deployed headset HTTP endpoint remains unverified. A [Web Browser frontend follow-up](evidence/framey-web-browser-2026-10-26.md) verified its exact archive now initializes with an inert null Steam React-root lookup. Its GamepadUI CDP/browser backend path remains unavailable, so it stays broken. Earlier focused checks cover [ProtonDB](evidence/framey-browser-2026-10-25.md), [Steam Status/cache](evidence/framey-cache-browser-2026-10-25.md), [Storage Cleaner](evidence/framey-api-v1-browser-2026-10-25.md), and [legacy MusicControl](evidence/framey-legacy-2026-10-25.md). +After the VR job released the shared lock, this audit acquired it and used a private temporary Decky 3.3.0 ARM64 user service. Read-only ProtonDB `get_setting` and Steam Status `get_settings` calls succeeded through the live Steam CEF websocket. The service and private base were removed and the lock released; normal managed services stayed inactive, battery was 100 → 99, and no power setting was changed. [Redacted results](evidence/framey-official-backend-2026-10-25.json) record exactly what ran. A [2026-10-26 follow-up](evidence/framey-events-backend-2026-10-26.json) verified default Notebook drawing/save/reopen across a real backend restart and Simple Timer start/cancel, countdown/completion events, a visible toast and persisted recents/subtle mode. Only private test settings were written. Simple Timer remains partial because its own expired-timer startup raises a missing `timer_task` error; sound/suspend are unsupported. A [local audio follow-up](evidence/framey-audio-backends-2026-10-26.md) exercised Volume Mixer, Volume Boost and Mixer through real Decky 3.3.0 amd64 backends on private silent virtual audio targets. Sliders, numeric volume entry, independent server readbacks and Mixer filter persistence across a runtime restart passed. An [ARM64 audio follow-up](evidence/framey-audio-arm64-2026-10-26.md) verified the unmodified official backends in actual Decky 3.3.0 on the Frame user audio session. Volume Mixer and Mixer now qualify as works for per-app volume: Chromium controls changed only a named silent stream, with independent Pulse readbacks. That run left Volume Boost partial because sink writes were denied. A [Volume Boost follow-up](evidence/framey-volume-boost-arm64-2026-10-26.md) now verifies linked/separate channel controls, re-link and reopen through the actual ARM64 backend. Independent Pulse reads confirmed 125%/150% gain writes while the default sink stayed muted; exact original volume/mute was restored. Volume Boost qualifies as works for sink controls. Audible output, Bluetooth playback and worn VR remain unverified. A [running-context follow-up](evidence/framey-running-context-2026-10-26.md) supplies detached running-app/main-app reads and relays actual Steam lifetime callbacks while a panel is open. Real Frame checks verified an empty list, registration/cleanup and the exact Discord Status ARM64 backend reporting no IPC connection. Nonempty games and transitions were synthetic fixtures; real game events, Discord activity and worn VR remain unverified. Discord Status moves to partial; no new works claim is made. A [library context follow-up](evidence/framey-library-context-2026-10-26.md) read 1,106 app overviews, 895 game IDs and the current public Steam ID from real Frame CEF under the shared lock. Local Chromium replayed this detached snapshot to render Deck Settings game selection, Steamback's empty save panel and PlayTime statistics/settings. Steamback and PlayTime used actual Decky 3.3.0 amd64 backends in an isolated container; their ARM64 backends and automatic game hooks remain unverified. Shotty's frontend now starts, but its published backend fails on missing `click`; it stays broken. A [chunked frontend follow-up](evidence/framey-deck-shelves-browser-2026-10-26.md) loaded Deck Shelves 2.0.0 through a managed JavaScript module route and rendered its settings in an opaque local Chromium panel. Its backend was denied; Steam Home shelves and worn VR remain unverified. A [local module lookup follow-up](evidence/framey-module-lookup-2026-10-26.md) supplies a finite registry of Framey-local controls and CSS. TabMaster QAM/docs now render; Steam library-tab editing remains unavailable. That pass exposed Junk-Store executable-script extraction failures; the follow-up below repairs them. SteamGridDB still requires Steam context-menu internals and DeckSP still fails its published MobX fallback. That pass tested TabMaster/Junk-Store in actual isolated amd64 Decky, without ARM64 or worn-VR coverage. A [script entrypoint follow-up](evidence/framey-script-entrypoints-2026-10-26.md) grants owner execute to shebang scripts and ZIP-declared executables without copying archive privilege/write bits. Junk-Store now passes static menu/backend calls in actual Decky 3.3.0 ARM64 and local Chromium Games/Epic navigation, back and reopen. It moves to partial: About still crashes on missing installed-app metadata, Legendary is absent, and login/game operations remain unverified. Existing installations require update/reinstall to repair permissions. A [read-only installed-metadata follow-up](evidence/framey-installed-context-2026-10-26.md) now repairs the About crash. The library snapshot copies only Steam-supplied installed booleans into detached metadata, preserving unknown values as undefined. Junk-Store About/Dependencies, Back and reopen render in local Chromium with actual Frame library data and the official ARM64 backend. It remains partial: dependency/login/game operations, RSS, the optional websocket and worn VR are unverified. Beyond the documented checks, official backends remain unexecuted. The separate VR job proves Frame Control/sample compositor rendering, not any official plugin. No worn-VR official-plugin result is claimed. A [local UI follow-up](evidence/framey-ui-fallbacks-2026-10-26.json) rendered Pause Games and Tailscale Control settings with all backend/network actions denied; it does not verify process pausing or Tailscale changes. A [read-only Steam bridge follow-up](evidence/framey-steam-reads-2026-10-26.json) verified actual Frame country, install folders and system information, plus IsThereAnyDeal's country dropdown in local Chromium. The installed-plugin status was fixture-shaped in that relay; plugin backends stayed denied and the deployed headset HTTP endpoint remains unverified. A [Web Browser frontend follow-up](evidence/framey-web-browser-2026-10-26.md) verified its exact archive now initializes with an inert null Steam React-root lookup. Its GamepadUI CDP/browser backend path remains unavailable, so it stays broken. Earlier focused checks cover [ProtonDB](evidence/framey-browser-2026-10-25.md), [Steam Status/cache](evidence/framey-cache-browser-2026-10-25.md), [Storage Cleaner](evidence/framey-api-v1-browser-2026-10-25.md), and [legacy MusicControl](evidence/framey-legacy-2026-10-25.md). ## Every current plugin @@ -91,7 +91,7 @@ The API column shows manifest / actual bundled frontend API. Many manifests say | 80: MagicBlack 2.0.2 | 1 / 2 | N/A | Not supported on Frame: Targets a global Steam Deck screen-black overlay; the isolated panel cannot black out the Frame compositor. | static archive, local browser, fake-Frame | | 81: IsThereAnyDeal for Deck 1.1.1 | legacy / legacy | partial | Country settings render and select the real Frame Steam country through the read-only bridge. Settings writes, deal lookups and Steam page integration remain unverified; plugin backend calls were denied. | static archive, local browser, fake-Frame, real Frame SSH | | 82: MagicPods 2.0.21 | 1 / 2 | broken | Plugin contains a non-ARM64 Linux binary: MagicPodsDecky/bin/magicpodscore. Frontend audit failed: Audit blocks WebSocket. | static archive, local browser, fake-Frame | -| 84: Junk-Store 1.1.13 | legacy / legacy | partial | Executable script extraction repaired. Official Decky 3.3.0 ARM64 static menu/backend calls and local Chromium Games/Epic navigation, back and reopen pass. About still fails on missing installed-app metadata; Legendary is absent. Dependency install, login, game operations, RSS and worn VR remain unverified. Existing installs need update/reinstall. | static archive, local browser, fake-Frame, isolated Linux backend, real Frame SSH | +| 84: Junk-Store 1.1.13 | legacy / legacy | partial | Official Decky 3.3.0 ARM64 static menu/backend calls pass; local Chromium Games/Epic navigation, About/Dependencies tabs, Back and reopen render with real Frame library metadata. Legendary is absent; dependency install, login, game operations, RSS, optional websocket and worn VR remain unverified. Existing installs need update/reinstall for script permissions. | static archive, local browser, fake-Frame, isolated Linux backend, real Frame SSH | | 85: Decky-Undervolt 1.0.8 | 1 / 2 | N/A | Not supported on Frame: RyzenAdj Curve Optimizer requires an AMD CPU; Frame is ARM64. | static archive, local browser, fake-Frame | | 87: ScreenSaver 1.1.2 | legacy / legacy | partial | Initializer succeeds; global screen overlay and idle/suspend integration have no verified VR target. | static archive, local browser, fake-Frame | | 88: steamdeck-input-disabler 1.0.2 | legacy / legacy | N/A | Not supported on Frame: Targets the built-in Steam Deck USB gamepad; Frame has different controllers. | static archive, local browser, fake-Frame | diff --git a/scripts/framey-compatibility-doc.py b/scripts/framey-compatibility-doc.py index 39e0dbc..e95c25c 100644 --- a/scripts/framey-compatibility-doc.py +++ b/scripts/framey-compatibility-doc.py @@ -64,7 +64,7 @@ def render(data): 'A [chunked frontend follow-up](evidence/framey-deck-shelves-browser-2026-10-26.md) loaded Deck Shelves 2.0.0 ' 'through a managed JavaScript module route and rendered its settings in an opaque local Chromium panel. ' 'Its backend was denied; Steam Home shelves and worn VR remain unverified. ' - 'A [local module lookup follow-up](evidence/framey-module-lookup-2026-10-26.md) supplies a finite registry of Framey-local controls and CSS. TabMaster QAM/docs now render; Steam library-tab editing remains unavailable. That pass exposed Junk-Store executable-script extraction failures; the follow-up below repairs them. SteamGridDB still requires Steam context-menu internals and DeckSP still fails its published MobX fallback. That pass tested TabMaster/Junk-Store in actual isolated amd64 Decky, without ARM64 or worn-VR coverage. A [script entrypoint follow-up](evidence/framey-script-entrypoints-2026-10-26.md) grants owner execute to shebang scripts and ZIP-declared executables without copying archive privilege/write bits. Junk-Store now passes static menu/backend calls in actual Decky 3.3.0 ARM64 and local Chromium Games/Epic navigation, back and reopen. It moves to partial: About still crashes on missing installed-app metadata, Legendary is absent, and login/game operations remain unverified. Existing installations require update/reinstall to repair permissions. Beyond the documented checks, official backends remain unexecuted. ' + 'A [local module lookup follow-up](evidence/framey-module-lookup-2026-10-26.md) supplies a finite registry of Framey-local controls and CSS. TabMaster QAM/docs now render; Steam library-tab editing remains unavailable. That pass exposed Junk-Store executable-script extraction failures; the follow-up below repairs them. SteamGridDB still requires Steam context-menu internals and DeckSP still fails its published MobX fallback. That pass tested TabMaster/Junk-Store in actual isolated amd64 Decky, without ARM64 or worn-VR coverage. A [script entrypoint follow-up](evidence/framey-script-entrypoints-2026-10-26.md) grants owner execute to shebang scripts and ZIP-declared executables without copying archive privilege/write bits. Junk-Store now passes static menu/backend calls in actual Decky 3.3.0 ARM64 and local Chromium Games/Epic navigation, back and reopen. It moves to partial: About still crashes on missing installed-app metadata, Legendary is absent, and login/game operations remain unverified. Existing installations require update/reinstall to repair permissions. A [read-only installed-metadata follow-up](evidence/framey-installed-context-2026-10-26.md) now repairs the About crash. The library snapshot copies only Steam-supplied installed booleans into detached metadata, preserving unknown values as undefined. Junk-Store About/Dependencies, Back and reopen render in local Chromium with actual Frame library data and the official ARM64 backend. It remains partial: dependency/login/game operations, RSS, the optional websocket and worn VR are unverified. Beyond the documented checks, official backends remain unexecuted. ' 'The separate VR job proves Frame Control/sample compositor rendering, not any official plugin. ' 'No worn-VR official-plugin result is claimed. ' 'A [local UI follow-up](evidence/framey-ui-fallbacks-2026-10-26.json) rendered Pause Games and Tailscale Control settings ' diff --git a/tests/page/framey_actions.mjs b/tests/page/framey_actions.mjs index b5b7150..d2d3fd1 100644 --- a/tests/page/framey_actions.mjs +++ b/tests/page/framey_actions.mjs @@ -265,6 +265,17 @@ for(const name of ['App','appStore','appInfoStore','collectionStore'])assert.equ const snapshot={apps:[[10,'Game Ten','ten',1,'10',180,60,1234], [20,'Shortcut','shortcut',1073741824,'85899345920',0,0,0]],gameIds:[10]}; window.__library(snapshot); +assert.equal(window.appStore.GetAppOverviewByAppID(10).local_per_client_data,undefined); +snapshot.apps[0].push(true);snapshot.apps[1].push(null); +window.__library(snapshot); +assert.equal(window.appStore.GetAppOverviewByAppID(10).local_per_client_data.installed,true); +assert.deepEqual(Object.keys(window.appStore.GetAppOverviewByAppID(10).local_per_client_data),['installed']); +assert.equal(window.appStore.GetAppOverviewByAppID(20).local_per_client_data.installed,undefined); +window.appStore.GetAppOverviewByAppID(10).local_per_client_data.installed=false; +assert.equal(snapshot.apps[0][8],true); // Detached from the wire data. +window.__library({...snapshot,apps:[snapshot.apps[0].slice(0,8).concat(false),snapshot.apps[1]]}); +assert.equal(window.appStore.GetAppOverviewByAppID(10).local_per_client_data.installed,false); +window.__library(snapshot); assert.equal(window.appStore.allApps.length,2); assert.equal(window.appStore.GetAppOverviewByAppID('10').display_name,'Game Ten'); assert.equal(window.appStore.GetAppOverviewByGameID('85899345920').appid,20); @@ -283,11 +294,12 @@ for(const invalid of [null,{apps:[],gameIds:[10]}, {...snapshot,apps:[snapshot.apps[0],snapshot.apps[0]]}, {...snapshot,apps:[[10,'Name','sort',1,'undefined',0,0,0]]}, {...snapshot,apps:[[10,'Name','sort',1,'10',-1,0,0]]}, + ...['true',1,{},undefined].map(installed=>({...snapshot,apps:[[10,'Name','sort',1,'10',0,0,0,installed]]})), {apps:Array(10001).fill(snapshot.apps[0]),gameIds:[]}]){ assert.throws(()=>window.__library(invalid),/Invalid Steam/); assert.equal(window.appStore.allApps[0].display_name,'Plugin-local patch'); } -const running={artwork:[[10,'https://shared.steamstatic.com/steam/apps/10/library.jpg',[]]],apps:[snapshot.apps[0]],runningIds:[10],mainAppId:10,events:[],cursor:0,lifetimeAvailable:true}; +const running={artwork:[[10,'https://shared.steamstatic.com/steam/apps/10/library.jpg',[]]],apps:[snapshot.apps[0].slice(0,8)],runningIds:[10],mainAppId:10,events:[],cursor:0,lifetimeAvailable:true}; const lifetimes=[]; const lifetimeHandle=window.SteamClient.GameSessions.RegisterForAppLifetimeNotifications(event=>{ lifetimes.push({...event,running:Array.from(window.DFL.Router.RunningApps,app=>app.appid)}); @@ -296,6 +308,7 @@ window.__running(running);await Promise.resolve(); assert.equal(lifetimes.length,0); // Initial snapshots are not launch events. assert.equal(window.SteamUIStore.FrameyRunningAvailable,true); assert.equal(window.DFL.Router.MainRunningApp.display_name,'Game Ten'); +assert.equal(window.appStore.GetAppOverviewByAppID(10).local_per_client_data.installed,true); assert.equal(window.appStore.GetVerticalCapsuleURLForApp({appid:10}),'https://shared.steamstatic.com/steam/apps/10/library.jpg'); assert.equal(window.appStore.GetCustomVerticalCapsuleURLs({appid:10}).length,0); assert.equal(window.DFL.Router.RunningApps,window.SteamUIStore.RunningApps); diff --git a/tests/test_framey_panel.py b/tests/test_framey_panel.py index 5239138..502048c 100644 --- a/tests/test_framey_panel.py +++ b/tests/test_framey_panel.py @@ -112,12 +112,22 @@ class Panel(unittest.TestCase): script = '''const assert=require('node:assert/strict'); const app={appid:10,display_name:'Game',sort_as:'game',app_type:1,gameid:'10', minutes_playtime_forever:60,minutes_playtime_last_two_weeks:20,rt_last_time_played:123, -owner_account_id:'PRIVATE',per_client_data:{path:'PRIVATE'}}; +owner_account_id:'PRIVATE',per_client_data:{path:'PRIVATE'}, +local_per_client_data:{installed:true,clientid:'PRIVATE',install_path:'PRIVATE'}}; global.window=global;global.appStore={allApps:[app]}; global.App={m_CurrentUser:{strSteamID:'76561198000000001',strAccountBalance:'PRIVATE'}}; global.collectionStore={allGamesCollection:{allApps:[app]}}; const value=eval(EXPRESSION); -assert.deepEqual(value,{apps:[[10,'Game','game',1,'10',60,20,123]],gameIds:[10],steamId:'76561198000000001'}); +assert.deepEqual(value,{apps:[[10,'Game','game',1,'10',60,20,123,true]],gameIds:[10],steamId:'76561198000000001'}); +assert.equal(JSON.stringify(value).includes('PRIVATE'),false); +app.local_per_client_data.installed=false; +assert.equal(eval(EXPRESSION).apps[0][8],false); +delete app.local_per_client_data.installed; +assert.equal(eval(EXPRESSION).apps[0][8],null); +app.local_per_client_data=undefined; +assert.equal(eval(EXPRESSION).apps[0][8],null); +app.local_per_client_data={installed:'true'}; +assert.equal(eval(EXPRESSION).apps[0][8],null); appStore.allApps=Array(10001).fill(app); assert.throws(()=>eval(EXPRESSION),/snapshot limit/); appStore=undefined; diff --git a/ui/framey-compatibility.json b/ui/framey-compatibility.json index f8b80d8..c7b0b4d 100644 --- a/ui/framey-compatibility.json +++ b/ui/framey-compatibility.json @@ -6518,7 +6518,7 @@ "https://github.com/ebenbruyns/junkstore.git" ], "status": "partial", - "reason": "Executable script extraction repaired. Official Decky 3.3.0 ARM64 static menu/backend calls and local Chromium Games/Epic navigation, back and reopen pass. About still fails on missing installed-app metadata; Legendary is absent. Dependency install, login, game operations, RSS and worn VR remain unverified. Existing installs need update/reinstall.", + "reason": "Official Decky 3.3.0 ARM64 static menu/backend calls pass; local Chromium Games/Epic navigation, About/Dependencies tabs, Back and reopen render with real Frame library metadata. Legendary is absent; dependency install, login, game operations, RSS, optional websocket and worn VR remain unverified. Existing installs need update/reinstall for script permissions.", "blockInstall": false, "verification": [ "static archive", @@ -6575,7 +6575,12 @@ "init/InitActions" ], "browser": "main menu; Games/Epic static tab; back; close/reopen", - "remainingFailure": "About: Cannot read properties of undefined (reading 'installed'); detached app overview lacks local_per_client_data.installed" + "installedContext": { + "evidence": "docs/evidence/framey-installed-context-2026-10-26.json", + "field": "local_per_client_data.installed", + "browser": "About/Dependencies, Back, close/reopen; real library projection; static ARM64 backend calls", + "installedUnknown": "preserved as undefined; no fabricated false" + } } }, { diff --git a/ui/framey-host.js b/ui/framey-host.js index 34a232a..ed43dc6 100644 --- a/ui/framey-host.js +++ b/ui/framey-host.js @@ -563,16 +563,21 @@ function projectApps(rows, limit) { if (!Array.isArray(rows) || rows.length > limit) throw Error('Invalid Steam app overview'); return rows.map(row => { - if (!Array.isArray(row) || row.length !== 8 || !Number.isSafeInteger(row[0]) || row[0] <= 0 || + if (!Array.isArray(row) || (row.length !== 8 && row.length !== 9) || !Number.isSafeInteger(row[0]) || row[0] <= 0 || typeof row[1] !== 'string' || row[1].length > 2048 || (row[2] != null && typeof row[2] !== 'string') || !Number.isSafeInteger(row[3]) || typeof row[4] !== 'string' || !/^\d{1,20}$/.test(row[4]) || - row.slice(5).some(value=>value != null && (!Number.isFinite(value) || value < 0))) + row.slice(5,8).some(value=>value != null && (!Number.isFinite(value) || value < 0)) || + (row.length === 9 && row[8] !== null && typeof row[8] !== 'boolean')) throw Error('Invalid Steam app overview'); const [appid,display_name,sort_as,app_type,gameid, minutes_playtime_forever,minutes_playtime_last_two_weeks,rt_last_time_played] = row; return {appid,display_name,sort_as,app_type,gameid, - minutes_playtime_forever,minutes_playtime_last_two_weeks,rt_last_time_played}; + minutes_playtime_forever,minutes_playtime_last_two_weeks,rt_last_time_played, + // null means Steam supplied no installed boolean, not false. Keep the + // detached metadata object for legacy readers; never copy paths/client IDs. + // Older/running snapshots omit this field and must not erase library data. + ...(row.length === 9 ? {local_per_client_data:row[8] === null ? {} : {installed:row[8]}} : {})}; }); } function setLibrarySnapshot(snapshot) { diff --git a/ui/framey_panel.py b/ui/framey_panel.py index 005cd5e..a5bfde7 100644 --- a/ui/framey_panel.py +++ b/ui/framey_panel.py @@ -183,7 +183,8 @@ STEAM_READS = { const apps = Array.from(appStore.allApps); if (apps.length > 10000) throw Error('Steam library exceeds the Framey snapshot limit'); return {apps:apps.map(a=>[a.appid,a.display_name,a.sort_as,a.app_type,String(a.gameid), - a.minutes_playtime_forever,a.minutes_playtime_last_two_weeks,a.rt_last_time_played]), + a.minutes_playtime_forever,a.minutes_playtime_last_two_weeks,a.rt_last_time_played, + typeof a.local_per_client_data?.installed === 'boolean' ? a.local_per_client_data.installed : null]), gameIds:Array.from(collectionStore.allGamesCollection.allApps,a=>a.appid), steamId:window.App?.m_CurrentUser?.strSteamID || null}; })()''',