Compare commits

..
Author SHA1 Message Date
SAPHID b39905f7cf fix(release): preserve published update manifests 2026-10-28 13:17:48 +00:00
Alex Southwell bf56d21eca Merge pull request #368 from saphid/ci/release-preview-assets-20261028
CI: require every expected installer preview output
2026-10-28 12:35:29 +00:00
SAPHID addd37467d ci: fail installer previews when expected outputs are missing 2026-10-28 12:26:24 +00:00
Alex Southwell 9776cca4d3 Merge pull request #367 from saphid/ci/actions-node24-runtime-20261028
CI: pin Node 24 GitHub Actions releases
2026-10-28 12:18:05 +00:00
Alex Southwell fb8d939a28 Merge pull request #366 from saphid/docs/framey-static-action-limits-20261028
Clarify AutoSuspend and CheatDeck compatibility limits
2026-10-28 12:07:07 +00:00
SAPHID 59ad05b323 ci: pin Node 24 GitHub Actions for workflow compatibility 2026-10-28 12:01:18 +00:00
SAPHID 155a3199f8 Clarify AutoSuspend and CheatDeck compatibility limits 2026-10-28 11:51:16 +00:00
Alex Southwell 41e5f0048b Merge pull request #341 from saphid/fleet/a83rq2046k
Document standing stability goal and coordinator takeover gates
2026-10-28 11:36:14 +00:00
SAPHID 25d0cb41ab Keep stability handoff links and snapshot scope current 2026-10-27 05:20:24 +00:00
SAPHID 9e77a35e40 Merge remote-tracking branch 'origin/main' into coord/stability-goal-reconcile 2026-10-27 05:20:07 +00:00
Alex Southwell bc02974b49 Merge pull request #365 from saphid/docs/v0451-release-verification
Record v0.4.51 release verification
2026-10-27 05:19:50 +00:00
SAPHID 5b448bc922 Update stability handoff for current release authority and v0.4.51 2026-10-27 05:17:24 +00:00
SAPHID b345921385 Merge remote-tracking branch 'origin/main' into coord/stability-goal-reconcile 2026-10-27 05:16:40 +00:00
SAPHID de3d7d4fbc Document standing stability goal and coordinator takeover gates 2026-10-26 22:01:06 +00:00
14 changed files with 266 additions and 23 deletions

No files matched your search

+11 -11
View File
@@ -14,11 +14,11 @@ jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.9" # the oldest python3 the Mac app may pick up (Xcode CLT)
- uses: actions/setup-node@v4
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: "24"
- name: Install zsh
@@ -67,11 +67,11 @@ jobs:
python: "3.13"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: ${{ matrix.python }}
- uses: actions/setup-node@v4
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: "24"
- name: OBS lifecycle tests
@@ -84,11 +84,11 @@ jobs:
runs-on: windows-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.12"
- uses: actions/setup-node@v4
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: "24"
- name: Wireless doctor fixtures, UI and live read-only Windows inventory
@@ -98,7 +98,7 @@ jobs:
ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Generate the project
run: brew install xcodegen && cd ios && xcodegen generate
- name: Build and test
@@ -113,7 +113,7 @@ jobs:
runs-on: ubuntu-24.04-arm
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install zsh
# The runner image's third-party apt sources (packages.microsoft.com) sometimes
# fail to refresh; Ubuntu's own lists still update, and the install fails if not.
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Actual Decky 3.3.0 discovery and safe backend probes
run: python3 scripts/framey-test-decky-runtime.py --output "$RUNNER_TEMP/decky-runtime.json"
- uses: actions/upload-artifact@v4
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
os: [ubuntu-latest, ubuntu-24.04-arm]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Native libraries
run: |
sudo apt-get update -qq
@@ -31,7 +31,7 @@ jobs:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
+16 -4
View File
@@ -81,8 +81,8 @@ jobs:
files: app/dist/*.AppImage app/dist/*.deb
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: "24"
# PC in the headset (Experimental, Windows only): our capture adapter and
@@ -113,6 +113,18 @@ jobs:
run: npm ci && npm run ${{ matrix.script }}
env:
CSC_IDENTITY_AUTO_DISCOVERY: "false"
- name: Check installer outputs before upload
shell: bash
run: |
# Every platform must produce each advertised file type. An artifact
# upload can otherwise succeed with just one file from the path list.
read -r -a patterns <<< '${{ matrix.files }}'
for pattern in "${patterns[@]}"; do
if ! compgen -G "$pattern" >/dev/null; then
echo "missing installer output matching $pattern" >&2
exit 1
fi
done
- name: Check the Mac bundle matches its runner
if: runner.os == 'macOS'
shell: bash
@@ -189,14 +201,14 @@ jobs:
app/dist/*.zip
app/dist/*.AppImage
app/dist/*.deb
if-no-files-found: ignore
if-no-files-found: error
checksums:
needs: build
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Attach SHA256SUMS after all installers upload
env:
GH_TOKEN: ${{ github.token }}
@@ -0,0 +1,35 @@
# AutoSuspend and CheatDeck static action limits, 28 October 2026
This is a read-only follow-up to the exact official Decky compatibility
catalog. The two archives were downloaded from the official version CDN and
SHA-256 checked before inspection. Neither plugin was imported, installed,
opened in a live panel, or run on the Frame.
## AutoSuspend 2.2.0, store ID 27
Archive SHA-256:
`20ceb9aa5327226326531d947e4a7a5318ba78b73f503266996a81877cffa536`.
The Python backend exposes settings reads and writes. The frontend contains a
`SteamClient.System.ShutdownPC()` call and Steam suspend callbacks. Its existing
opaque Chromium audit fails during initialization with `Cannot read properties
of null (reading '6')`. Static presence of those actions does not prove that
opening the panel would call them, but a settings-only initialization check
would not establish safe threshold or suspend behavior on Frame. The plugin
remains **broken**; no suspend, shutdown, notification, or live setting was
exercised.
## CheatDeck 1.1.6, store ID 76
Archive SHA-256:
`6dfe1f821b0cfe2a61c95e49efd6cb2587d79ff3d1e101e14fa80c12c7f6d89f`.
The published README directs users to a Steam game context-menu item to choose
a trainer and save launch options. The frontend references
`SteamClient.Apps.SetAppLaunchOptions`; the Python backend provides settings
RPCs. The existing opaque Chromium audit fails with `Cannot convert undefined
or null to object`. The isolated Framey panel has no Steam game context-menu
target, so repairing that initializer alone would not verify the advertised
trainer workflow. The plugin remains **broken**; no launch-option write, game,
trainer, real headset, or worn-VR action was exercised.
These observations only refine the reasons for the two existing catalog rows.
The exact-version totals stay **6 works / 52 partial / 47 broken / 11 N/A**.
@@ -0,0 +1,5 @@
# Published release manifest guard
The publisher previously accepted an already published release, deleted its existing `update.json`, then attempted to upload a replacement. If that upload failed, running copies would lose the live update manifest. The publisher now refuses a non-draft release before any asset deletion or upload.
The stand-in GitHub API regression provides a published release with an existing `update.json` and verifies that publication fails without any mutating API call. On 2026-10-28, `python3 -m unittest discover -s tests -p test_publish_release.py -q` passed all 12 tests; `git diff --check` passed. Hosted CI remains the merge gate. No GitHub release or update channel was changed by this test.
+4 -2
View File
@@ -27,6 +27,8 @@ Open the browser audit URL in local Chromium, run `runAudit()` and inspect/downl
A [Free Loader duplicate-entry follow-up](evidence/framey-free-loader-duplicates-2026-10-26.md) accepts only byte-identical duplicate ZIP members whose modes differ at most in execute bits. The exact archive passed private local and focused fake-Frame install/remove; its frontend rendered in opaque Chromium and isolated Python settings/empty-deals reads passed with a Decky stub. The earlier full 116-plugin fake-Frame audit blocked this archive before the fix. Actual Decky, feed updates and worn VR remain unverified.
A [2026-10-28 static action-limit review](evidence/framey-static-action-limits-2026-10-28.md) hash-checked the exact AutoSuspend and CheatDeck archives. AutoSuspend contains Steam suspend/shutdown paths, and CheatDeck's advertised trainer flow needs a Steam game context menu and launch-option writes. Neither plugin was executed; both retain their broken labels.
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. That initial pass left Simple Timer 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, RSSLine truncated
@@ -52,7 +54,7 @@ The API column shows manifest / actual bundled frontend API. Many manifests say
| 23: Animation Changer 1.3.2 | legacy / legacy | partial | Initializer succeeds; forced backend failure prevents loading animations. Boot-video changes untested. | static archive, local browser, fake-Frame |
| 24: RadiYo! 1.3 | legacy / legacy | partial | Initializer succeeds; radio discovery and playback remain unverified. | static archive, local browser, fake-Frame |
| 26: HLTB for Deck 2.0.12 | 1 / 2 | partial | Settings render without the prior IndexedDB SecurityError; Steam library badges have no patch target, and live lookup is unverified. | static archive, local browser, fake-Frame |
| 27: AutoSuspend 2.2.0 | legacy / legacy | broken | Frontend audit failed: Cannot read properties of null (reading '6'). | static archive, local browser, fake-Frame |
| 27: AutoSuspend 2.2.0 | legacy / legacy | broken | Frontend audit failed: Cannot read properties of null (reading '6'). The exact frontend also contains SteamOS suspend and shutdown paths; threshold behavior and safe Frame operation are unverified. | static archive, local browser, fake-Frame |
| 28: Emuchievements 2.0.5 | legacy / legacy | broken | Plugin contains a non-ARM64 Linux binary: Emuchievements/bin/hash. | static archive, local browser, fake-Frame |
| 30: TunnelDeck 1.0.4 | legacy / legacy | broken | This plugin needs root and is not supported by the Frame user service. | static archive, local browser, fake-Frame |
| 32: AutoFlatpaks 1.6.8 | legacy / legacy | broken | This plugin needs root and is not supported by the Frame user service. Shared Toggle value rendering is repaired; other listed blockers remain. | static archive, local browser, fake-Frame |
@@ -86,7 +88,7 @@ The API column shows manifest / actual bundled frontend API. Many manifests say
| 73: EmuDecky 1.0.2-1 | legacy / legacy | broken | Plugin contains a non-ARM64 Linux binary: EmuDecky/bin/hello. | static archive, local browser, fake-Frame |
| 74: KDE Connect 0.1.0-1 | legacy / legacy | partial | Initializer succeeds; KDE Connect installation/pairing/file transfers unverified. | static archive, local browser, fake-Frame |
| 75: Reshadeck 0.2.0 | legacy / legacy | partial | Initializer succeeds; ReShade deployment into Proton games is unverified. | static archive, local browser, fake-Frame |
| 76: CheatDeck 1.1.6 | 1 / 2 | broken | Frontend audit failed: Cannot convert undefined or null to object. | static archive, local browser, fake-Frame |
| 76: CheatDeck 1.1.6 | 1 / 2 | broken | Frontend audit failed: Cannot convert undefined or null to object. Its primary trainer workflow uses a Steam game context menu and launch-option writes, with no target in the isolated Framey panel; no trainer launch was verified. | static archive, local browser, fake-Frame |
| 77: BT Wake Control 1.2.0 | 1 / 2 | partial | Initializer succeeds; backend uses DMI board paths and Bluetooth wake permissions, unverified on ARM64 Frame. | static archive, local browser, fake-Frame |
| 78: Brightness Bar 1.0.4 | legacy / legacy | N/A | Not supported on Frame: Targets Steam/QAM brightness shortcuts and a global Steam overlay; neither is attached to the Framey panel. | static archive, local browser, fake-Frame |
| 79: Decky Notifications 1.0.1 | legacy / legacy | partial | Initializer succeeds; device/account setup and notification delivery unverified. | static archive, local browser, fake-Frame |
+153
View File
@@ -0,0 +1,153 @@
# Frame Control standing goal and historical recovery handoff
Owner: Alex. This records the 27 October 2026 AEDT recovery handoff; status
checks below use UTC. Alex subsequently authorized the existing coordinator to
continue the roadmap and publish releases when every gate passes. This document
does not authorize a second coordinator to run concurrently.
## Alex's top priority
> Make Frame Control's released version always stable and running cleanly. Never release something that isn't working correctly. I don't want to break things for people.
Keep the released app stable and main green. Investigate failures before
shipping; a version number or an uploaded artifact is not proof of readiness.
## Standing roadmap
Continue official Decky compatibility, test gaps, fixes and useful features.
The existing owner is T3 job `mppkd9qczt` on lxso2. Preserve its work and avoid
competing coordinator or release jobs. Verify its current state before any
takeover; this historical handoff is not a takeover instruction.
## Mandatory release gates
- CI must be green on the **exact release commit**, not merely an earlier
commit, another branch, or a previous successful workflow attempt.
- End-to-end tests and release verification must pass for that candidate.
- Every required installer build must succeed, including Linux, Windows,
macOS Intel and macOS Apple Silicon.
- Never publish a release with failed or cancelled required builds. A retry
must actually finish successfully; a cancelled build is not a pass.
- Keep main green. Resolve required failures before merging or releasing.
- Preserve evidence linking the release tag/commit, successful workflow run,
installer assets/checksums, e2e results and real-device verification.
- Use `scripts/publish-release.sh --dry-run TAG` before publishing. Alex's
standing full-auto instruction authorizes the existing coordinator to tag and
publish after every gate passes. Stop for secrets, money, destructive Git
actions or wiping the headset; never bypass a failed release gate.
## Current release state, checked 27 October 2026 at 05:15 UTC
- The latest public release is
[v0.4.51](https://github.com/saphid/frame-control/releases/tag/v0.4.51),
published at 05:10:05 UTC from main commit
`4da7220660ba345bc46cdb525b6c512bc6a9d8bd`.
- [Exact-commit checks](https://github.com/saphid/frame-control/actions/runs/40451752952),
[native Decky runtime](https://github.com/saphid/frame-control/actions/runs/40451752953),
and the [tagged installer release](https://github.com/saphid/frame-control/actions/runs/40452627862)
succeeded. The tag release built Linux, Windows, Intel Mac and Apple Silicon
Mac installers and checksums. The published update manifest reports 0.4.51.
- The locked candidate smoke passed 12 checks, skipped one optional x86_64
launch, and failed none. SteamVR was in standby; worn display and physical
controller use remain unverified. The hourly shared-lock worn check remains
enabled. See [candidate evidence](../evidence/v0451-candidate-2026-10-27.md)
and [release evidence](../evidence/v0451-release-2026-10-27.md) for the exact
asset audit and publication record.
- Main matched the release commit and its checks were green at this check.
Revalidate before another merge or release. Disk capacity on lxso2 remains
critical; do not delete pre-existing data to make space without Alex's OK.
## Incident snapshot supplied by Alex
At the time of the recovery request, latest published was **v0.4.47** with a
passed release run; **v0.4.48 RC was not published**, after a failed run with a
cancelled macOS build. PRs **#337 and #338 were open**. Preserve this as the
historical incident snapshot, not a claim that remote state stays unchanged.
## Verified update during recovery
Checked 26 October 2026 around 21:58 UTC:
- v0.4.47 was published at 15:07:28 UTC.
- A subsequent v0.4.48 release run
[40406781383](https://github.com/saphid/frame-control/actions/runs/40406781383)
completed successfully on commit
`5438e1d9d05ca472f200dcde50799c8ae882fafb`. Its Linux, Windows, Intel macOS
and Apple Silicon macOS installer jobs, draft and checksum jobs all succeeded.
- GitHub now reports **v0.4.48 published at 21:53:29 UTC**. This publication
happened outside the recovery task. Recheck the exact-commit checks and
release evidence before drawing conclusions about all release gates.
- [#337](https://github.com/saphid/frame-control/pull/337) is merged; lxso1's
clean main was fast-forwarded to
`1ddb6278b8ae0a98ecc79a181bba4d3cc67a52b6`.
- [#338](https://github.com/saphid/frame-control/pull/338) was open at that
time; it has since merged.
- lxso2 T3 recovered at **21:42:54 UTC**; port 3773/local HTTP and existing
HTTPS returned 200 by 21:43:58 UTC. Root disk capacity remains critical;
recovery is not a capacity fix.
- Fleet guards, probe units, safety tests and incident evidence are proposed
in [fleet #38](https://github.com/saphid/fleet/pull/38); units were installed
under the owner's incident authorization. Disk critical alerts use the
existing fleet Prometheus/watchdog pipeline.
## Historical lxso1 readiness
Main checkout: `/home/saphid/projects/steam-frame` (GitHub repository
`saphid/frame-control`). Shell default Node may be older; use
`~/.nvm/versions/node/v24.22.1/bin` first in PATH for Node 24 CI commands.
Python 3.9.25 and 3.13.14 were installed through uv for the CI matrix; system
Python 3.12.3 already existed. Workflows at that time required no Rust/Go
toolchain.
Readiness commands, per-gate logs and exact-commit JSON results live at
`/tmp/frame-control-readiness-20261027/` on lxso1. These are local readiness
checks, not a substitute for GitHub CI on a future release commit. The Linux
installer command uses `--publish never`; no release was published by these
checks. Native ARM64, Windows/macOS platform gates, Xcode/iOS and real-headset
checks still require their proper runners/hardware.
Completed on main commit `1ddb6278b8ae0a98ecc79a181bba4d3cc67a52b6`,
26 October 2026 at 22:00 UTC:
| Gate | Result on lxso1 |
| --- | --- |
| Shell syntax, Python compile | Passed |
| Python 3.9 / 3.12 / 3.13 server suites | 1,639 tests each; passed with platform/optional skips |
| Linux wireless doctor, app syntax/tests, website tests/syntax | Passed |
| `npm ci`, `npm run dist:linux` | Passed; AppImage and deb built without publishing |
| AppImage launch verification | Passed |
| `scripts/e2e.sh` on x86_64 Docker | Passed: 50 tests, one optional catalog audit skipped; not native ARM64 hardware proof |
| Publish dry runs for v0.4.47 and v0.4.48 | Passed, including all ten installer digests and SHA256SUMS; no mutation |
| Native PC host build and required-native tests | Blocked/failed: GStreamer/GLib development packages absent; required native adapter unavailable |
The workflows name system GStreamer packages but do not pin package versions.
They were left uninstalled under the owner's pinned-versions-only constraint.
Resolve that dependency policy before claiming full Linux native readiness.
Main was clean and matched origin/main on the final 21:59 UTC fetch.
## How a takeover should resume
1. Read this file, `docs/testing.md`, the current roadmap/evidence and the
existing coordinator's recorded work through supported read-only tooling.
Verify remote releases, PR states and workflow conclusions again.
2. Check lxso2 health and disk headroom. Disk-full recovery requires capacity
decisions; do not delete protected data or alter T3 state databases.
3. Confirm that the existing coordinator is no longer active and that the
takeover is authorized by the current instructions. Preserve uncommitted
changes and active worktrees; do not run competing coordinator/release jobs.
4. Fetch origin and inspect local status before updating main. Resume the
roadmap from recorded gaps and existing PRs, not from assumptions about
what an interrupted worker completed.
5. Work on an isolated branch and use PRs. Validate the actual candidate
commit with every release gate above; record hardware/platform blockers.
6. Publish only after all required gates pass under Alex's current full-auto
instruction or a newer explicit instruction.
The lxso1 five-minute `fleet-t3-lxso2-health.timer` only checks and alerts; it
does not start takeover threads. Automatic stand-in launch is not configured
or proven by this recovery deployment. It would need supported gateway
capability and authorized credentials; do not copy or mint gateway secrets.
The original handoff copy was placed at
`/home/saphid/frame-control-handoff/STANDING_GOAL.md` on lxso1. This repository
document may be newer.
+2
View File
@@ -59,6 +59,8 @@ count. So a build reaches people only when you publish it, after testing it.
installer digests. It attaches
`update.json` (the version, the notes, the release page and each
installer's digest), then publishes the release and marks it latest. It
refuses an already published release before replacing its live update
manifest; use a new tested release for changes after publication. It
uses only the REST API: `gh release view` can't find a draft whose
`tag_name` still reads `untagged-…`, and GraphQL is often rate-limited.
Such a draft is found by its exact title (`Frame Control 0.4.0`, or that
+1
View File
@@ -41,6 +41,7 @@ def render(data):
'```', '',
'Open the browser audit URL in local Chromium, run `runAudit()` and inspect/download `results`. All backend/network actions remain denied.', '',
'A [Free Loader duplicate-entry follow-up](evidence/framey-free-loader-duplicates-2026-10-26.md) accepts only byte-identical duplicate ZIP members whose modes differ at most in execute bits. The exact archive passed private local and focused fake-Frame install/remove; its frontend rendered in opaque Chromium and isolated Python settings/empty-deals reads passed with a Decky stub. The earlier full 116-plugin fake-Frame audit blocked this archive before the fix. Actual Decky, feed updates and worn VR remain unverified.', '',
'A [2026-10-28 static action-limit review](evidence/framey-static-action-limits-2026-10-28.md) hash-checked the exact AutoSuspend and CheatDeck archives. AutoSuspend contains Steam suspend/shutdown paths, and CheatDeck\'s advertised trainer flow needs a Steam game context menu and launch-option writes. Neither plugin was executed; both retain their broken labels.', '',
'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. '
+1 -1
View File
@@ -56,7 +56,7 @@ if len(hits) != 1:
sys.exit("found %s for %s; expected one draft" % (why, tag))
r = hits[0]
if not r.get("draft"):
print("note: %s is already published; refreshing update.json and marking it latest" % tag, file=sys.stderr)
sys.exit("not publishing: %s is already published; leave its live update manifest intact" % tag)
json.dump(r, sys.stdout)
EOF
id=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["id"])' "$tmp/release.json")
+10
View File
@@ -76,6 +76,16 @@ class PublishRelease(unittest.TestCase):
self.assertEqual(p.returncode, 0, p.stderr + p.stdout)
self.assertEqual(next(c for c in log if "PATCH" in c)[3], "repos/saphid/frame-control/releases/2")
def test_already_published_release_keeps_its_live_update_manifest(self):
old = {"id": 7, "name": "update.json", "size": 100,
"digest": "sha256:" + "a" * 64}
rel = dict(draft(tag_name="v9.8.7", assets=draft()["assets"] + [old]), draft=False)
p, log, upload = self.run_script([rel], "v9.8.7")
self.assertNotEqual(p.returncode, 0)
self.assertIn("already published", p.stderr)
self.assertIsNone(upload)
self.assertFalse(any("-X" in call for call in log))
def test_refuses_missing_or_unhashed_installers(self):
assets = draft()["assets"][1:]
assets[0] = dict(assets[0], digest=None)
+23
View File
@@ -22,6 +22,7 @@ WORKFLOW = ROOT / ".github" / "workflows" / "release.yml"
FAKEGH = ROOT / "tests" / "fakegh"
STEP = "- name: Create the draft release"
UPLOAD_STEP = "- name: Upload to the release"
OUTPUTS_STEP = "- name: Check installer outputs before upload"
def step_script(step=STEP):
@@ -116,6 +117,28 @@ class DraftStep(unittest.TestCase):
self.assertEqual(self.patches(log), [])
@unittest.skipUnless(os.name != "nt" and shutil.which("bash"),
"needs a POSIX bash; Actions uses Git Bash on Windows")
class InstallerOutputsStep(unittest.TestCase):
def run_step(self, files):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
for name in files:
(root / name).write_bytes(b"installer")
script = step_script(OUTPUTS_STEP).replace("${{ matrix.files }}", "*.dmg *.zip")
return subprocess.run(["bash", "--noprofile", "--norc", "-eo", "pipefail", "-c", script],
cwd=root, capture_output=True, text=True, timeout=10)
def test_requires_each_advertised_installer_type(self):
result = self.run_step(["Frame-Control-mac.dmg"])
self.assertNotEqual(result.returncode, 0)
self.assertIn("missing installer output matching *.zip", result.stderr)
def test_accepts_all_advertised_installer_types(self):
result = self.run_step(["Frame-Control-mac.dmg", "Frame-Control-mac.zip"])
self.assertEqual(result.returncode, 0, result.stderr)
@unittest.skipUnless(os.name != "nt" and shutil.which("bash") and shutil.which("jq"),
"needs a POSIX bash and jq; Actions runs this step under bash")
class UploadStep(unittest.TestCase):
+2 -2
View File
@@ -1672,7 +1672,7 @@
"https://github.com/jurassicplayer/decky-autosuspend.git"
],
"status": "broken",
"reason": "Frontend audit failed: Cannot read properties of null (reading '6').",
"reason": "Frontend audit failed: Cannot read properties of null (reading '6'). The exact frontend also contains SteamOS suspend and shutdown paths; threshold behavior and safe Frame operation are unverified.",
"blockInstall": false,
"verification": [
"static archive",
@@ -5854,7 +5854,7 @@
"https://github.com/jurassicplayer/decky-autosuspend"
],
"status": "broken",
"reason": "Frontend audit failed: Cannot convert undefined or null to object.",
"reason": "Frontend audit failed: Cannot convert undefined or null to object. Its primary trainer workflow uses a Steam game context menu and launch-option writes, with no target in the isolated Framey panel; no trainer launch was verified.",
"blockInstall": false,
"verification": [
"static archive",