Files
saphid--frame-control/scripts/framey-compatibility-doc.py
T

135 lines
14 KiB
Python

#!/usr/bin/env python3
"""Render the documentation from the same digest-scoped data used by the store."""
import argparse
from collections import Counter
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def cell(value):
if isinstance(value, list):
value = ', '.join(str(v) for v in value) or 'none detected'
return str(value).replace('|', '&#124;').replace('\n', ' ').replace('<', '&lt;')
def render(data):
counts = Counter(p['status'] for p in data['plugins'])
lines = ['# Official Decky store compatibility with Framey', '',
f"Snapshot: {data['checkedAtUtc']}; {len(data['plugins'])} current visible official plugins from {data['source']}.", '',
'This table and both store views use [`ui/framey-compatibility.json`](../ui/framey-compatibility.json). '
'Results apply only to the exact store ID, version and SHA-256. A changed digest/version is **unverified**. '
'Regenerate this document with `python3 scripts/framey-compatibility-doc.py`; use `--check` in review.', '',
f"**Works: {counts['works']} · Partial: {counts['partial']} · Broken: {counts['broken']} · N/A: {counts['na']}.**", '',
'- **Works** requires verified useful plugin behavior with its real backend. Read the reason for the verified scope and remaining limits; it does not imply worn-VR verification.',
'- **Partial** means only a subset or a local fixture scope was verified, often just frontend/settings initialization. The remaining backend/device functionality is unverified; read the reason for the exact scope.',
'- **Broken** means a concrete archive, frontend or Steam dependency blocks the audited version. An artificial backend/network denial alone is not evidence that the real backend is broken.',
'- **N/A** means the plugin targets hardware or a display/UI surface that Framey does not provide. The store disables installation for these versions.', '',
'## Verification limits', '',
f"All exact archives were hash-checked and statically inspected. The local Chromium host used Framey commit `{data['hostCommit']}`, React 18.3.1 and an opaque `allow-scripts` iframe. "
'Each frontend was attempted for 1.2 seconds, including React effects. All plugin backend calls, fetches and websockets were denied. '
'Some plugins attempt mutating actions on mount; none reached a backend. Initialization (`ready`) does not guarantee React rendered, and delayed errors/actions may occur after the observation window. '
'The JSON preserves initializer errors, capped console observations and attempted method names.', '',
'The optional full-catalog fake-Frame audit exercises the real HTTP/SSH installer, receipt, frontend bytes and removal, '
'or expected rejection. It uses a private disposable Compose project with all plugin backend execution stubbed. '
'It checks archive transport and safeguards, not Python/native execution. Run it with:', '',
'```sh',
'python3 scripts/framey-audit-store.py --cache /tmp/framey-archives --output /tmp/framey-observations.json',
'python3 scripts/framey-audit-fake.py --cache /tmp/framey-archives --output /tmp/framey-fake/results.json',
'python3 scripts/framey-audit-browser.py --cache /tmp/framey-archives',
'```', '',
'Open the browser audit URL in local Chromium, run `runAudit()` and inspect/download `results`. All backend/network actions remain denied.', '',
'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. 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', '',
'The API column shows manifest / actual bundled frontend API. Many manifests say v1 while their bundle connects API2. '
'Missing manifest API is labelled legacy; an unrecognized bundle is unknown. Globals and hardware are static signals, not exhaustive dependencies.', '',
'| Plugin / version | Manifest / bundle API | Status | Reason | Verified by |',
'| --- | --- | --- | --- | --- |']
for p in data['plugins']:
lines.append('| '+ ' | '.join(map(cell, [f"{p['id']}: {p['name']} {p['version']}",
f"{p['api']} / {p['frontendApi']}", 'N/A' if p['status']=='na' else p['status'], p['reason'], p['verification']]))+' |')
lines += ['', '## API, UI and backend inventory', '',
'DFL includes exports imported through decky-frontend-lib / @decky/ui. Steam stores and router/toaster/QAM signals are listed separately. '
'No signal means the static scan did not detect one, not that no dependency exists. Native binaries are classified by ELF machine or Windows MZ header; '
'Python files and source links are preserved in the JSON.', '',
'| Plugin | SteamClient methods | DFL / @decky/ui exports | Other Steam globals / UI surfaces | Backend | Hardware / OS signals |',
'| --- | --- | --- | --- | --- | --- |']
for p in data['plugins']:
native = sorted({n['arch'] for n in p['native']})
lines.append('| '+' | '.join(map(cell, [p['name'],p['steamClient'],p['dfl'],p['globals']+p.get('uiSurfaces',[]),
p['backend']+('; '+', '.join(native) if native else ''),p['hardware']]))+' |')
lines += ['', '## Native payload portability', '',
'All published Linux native payloads in this snapshot are x86_64; no published aarch64 payload was found. '
'The three Windows DLLs belong to Decky-Framegen. The installer still rejects these archives. '
'Apart from the explicitly described LocalSend experiment, ARM64 builds were not attempted: the constraints below remain unresolved.', '',
'[LocalSend ARM64 build/runtime evidence](evidence/framey-localsend-arm64-2026-10-26.md) '
'covers a reproducible MIT helper build and its isolated real-Frame protocol 2.0 info response. '
'The official x86 archive remains blocked; full plugin integration and file transfers are unverified.', '',
'| Plugin | Native payloads | ARM64 result / remaining blocker |', '| --- | --- | --- |']
for p in data['plugins']:
if p['native']:
binaries = [n['file'].removeprefix(p['folder']+'/') for n in p['native']]
if len(binaries)>5:
binaries = binaries[:5]+[f"… {len(binaries)} total; full list in JSON"]
lines.append('| '+' | '.join(map(cell,[p['name'],binaries,p['nativePort']]))+' |')
lines += ['', '## Evidence and maintenance', '',
'The canonical JSON contains full archive hashes, native paths, source URLs, frontend observations and fake-Frame outcomes. '
'Store compatibility is advisory except known installation/hardware blockers; removal always remains available. '
'A panel retains its compatibility reason after the initializer reports ready. Upstream fixes require a new digest-scoped audit. '
'Do not mark a plugin works based on archive installation, fake backend replies or initializer completion.', '']
return '\n'.join(lines)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--check', action='store_true')
args = parser.parse_args()
output = ROOT/'docs/framey-compatibility.md'
expected = render(json.loads((ROOT/'ui/framey-compatibility.json').read_text(encoding='utf-8')))
if args.check:
if not output.exists() or output.read_text(encoding='utf-8') != expected:
raise SystemExit('Compatibility documentation differs from canonical store data')
else:
output.write_text(expected, encoding='utf-8')
if __name__ == '__main__':
main()