Skip to content

Publish owned runtime marker atomically - #2561

Merged
ymichael merged 1 commit into
mainfrom
bb/ci-flake-fix-appimage-owned-runtime-partial-json-thr_6w9nbpi9k4
Aug 27, 2026
Merged

Publish owned runtime marker atomically#2561
ymichael merged 1 commit into
mainfrom
bb/ci-flake-fix-appimage-owned-runtime-partial-json-thr_6w9nbpi9k4

Conversation

@ymichael

@ymichael ymichael commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Human comments

What was wrong

The desktop owned-runtime supervisor wrote owned-runtime.json directly at its final pathname. writeFile makes that pathname observable after creation/truncation but before all JSON bytes are written, so the AppImage startup poll could read an empty or truncated marker and fail immediately in JSON.parse, as captured in main CI run 33093605671. Investigation and implementation started from the independently verified main SHA 47ba90e6c2f379615a76d9e4c2dc421f8da08aef.

What changed

  • Stage the complete owned-runtime marker in a unique file beside the destination, then publish it with a same-directory atomic rename.
  • Remove a failed staging file without changing the existing final marker.
  • Add a deterministic producer-boundary regression that pauses the write after file creation and asserts that owned-runtime.json is not yet visible.

The smoke reader, its assertions, and all polling intervals, deadlines, retry budgets, and wrapper clocks are unchanged. Persistent malformed JSON at the final pathname therefore remains a meaningful smoke failure instead of being treated as readiness. This is desktop-local filesystem publication only: no server/host-daemon wire contract changed, so HOST_DAEMON_PROTOCOL_VERSION remains 171.

How you verified

  • Red before the fix: the producer-boundary regression now in test/owned-runtime-supervisor.test.ts deterministically observed Unexpected end of JSON input while the base producer write was paused (it was first run as a standalone publication test before consolidation).
  • Green after the fix: pnpm exec turbo run test --filter=@bb/desktop --force -- test/owned-runtime-supervisor.test.ts (4 tests).
  • Stress: the focused Turbo test passed 25/25 consecutive runs.
  • pnpm exec turbo run typecheck --filter=@bb/desktop --force.
  • pnpm exec turbo run build --filter=@bb/desktop --force and a final cached-upstream pnpm exec turbo run typecheck build --filter=@bb/desktop.
  • pnpm exec oxfmt --check apps/desktop/src/owned-runtime-supervisor.ts apps/desktop/test/owned-runtime-supervisor.test.ts.
  • PR CI run 33097922213 passed, including Linux Smoke Linux AppImage mount lifecycle, both package-smoke jobs, and every test/check job.
  • The full desktop suite reached 37 passing files / 242 passing tests, then its existing Linux-specific bb-process AppImage supervisor test failed on this macOS host because /proc is unavailable. The changed focused suite is green; no unrelated SIGTRAP behavior was exercised or changed.

AGENT GENERATED: by GPT-5.6-Sol

Avoid exposing an empty or truncated owned-runtime.json while the producer write is still in progress.
@ymichael
ymichael merged commit 8cc94b0 into main Aug 27, 2026
15 checks passed
@ymichael
ymichael deleted the bb/ci-flake-fix-appimage-owned-runtime-partial-json-thr_6w9nbpi9k4 branch August 27, 2026 18:41
ymichael added a commit that referenced this pull request Sep 1, 2026
## Human comments

## What was wrong

The Linux AppImage lifecycle smoke treated the server listener's first
successful `/health` response as proof that the owned bb stack was
ready. The listener opens before `pluginService.start()` settles, and
the desktop returns from its initial server probe before `bb-app` has
finished starting and connecting the host daemon. The smoke could
therefore tear down the GUI while server startup work was still in
flight, then misreport a later timed-out health request as `bb became
unhealthy after the GUI mount teardown`.

Fresh Intel x86_64 validation against current main
(`2c9f82548471c7fb160ab343c7dd8ce74bbecbc1`) reproduced that exact
signature on the first real AppImage run. It also showed that the
original PR's daemon-connected condition alone was insufficient on
current main: that version reproduced the exact failure in 3/5 real
AppImage runs without synthetic load. A daemon can connect before plugin
startup settles. The separate pre-runtime `SIGTRAP` fixed by
[#2657](#2657) occurs before
`owned-runtime.json` exists and is not this failure.

## What changed

- Merged current main normally, retaining the atomic owned-runtime
publication from [#2561](#2561), the
Linux smoke sandbox/stdio fixes from
[#2657](#2657), and packaged-smoke exit
settlement from [#2663](#2663).
- Before GUI teardown, the lifecycle smoke now requires the isolated
host daemon's `/status` response to report `connected: true` for the
expected server URL.
- It then calls the existing `/api/v1/system/providers` endpoint, whose
handler waits on `providerRegistry.whenRegistrationsSettled()`. This is
the direct signal that asynchronous plugin startup has settled; plugin
startup failure also settles accurately rather than hanging readiness.
- If the GUI exits during this readiness phase, the smoke reuses the
existing bounded stdio-flush diagnostic before reporting its exit. GUI
and owned-runtime death still fail immediately.

The post-teardown contract is unchanged: the owned runtime PID must
remain live, its independent AppImage mount must remain active, and all
three health requests must succeed. No timeout, polling interval, retry
budget, protocol, product runtime, or packaged behavior changed, so no
host-daemon protocol bump is needed.

## How you verified

All reproduction, build, test, typecheck, smoke, and load work ran on
Intel host `host_nwqfteeqz4` in a temporary local x86_64 Ubuntu 24.04 VM
with working FUSE; the VM was deleted after a zero-leak
process/mount/temp-root audit.

- Red, current main: the first real x64 AppImage run reproduced `bb
became unhealthy after the GUI mount teardown`.
- Red, inherited daemon-only candidate: the exact failure reproduced in
3/5 real AppImage runs without synthetic load.
- Green, final direct-settlement candidate: 5/5 consecutive real
AppImage runs passed, then the official Turbo smoke passed after the
exact-final AppImage rebuild.
- Bounded load: 3/3 additional real AppImage runs passed while two of
four guest CPUs ran at 75%; every load process was stopped after its run
and every cleanup audit was empty.
- `pnpm exec turbo run desktop:build:linux --filter=@bb/desktop
--cache-dir=.turbo/cache --output-logs=new-only`: 13 tasks passed on the
final commit.
- `xvfb-run -a pnpm exec turbo run smoke:appimage-lifecycle
--filter=@bb/desktop --force --cache-dir=.turbo/cache
--output-logs=new-only`: passed on the final AppImage.
- `pnpm exec turbo run test --filter=@bb/desktop --force`: 38 files, 248
tests passed on Linux.
- `pnpm exec turbo run typecheck --filter=@bb/desktop`: 3 tasks passed.
- `pnpm exec turbo run build --filter=@bb/desktop`: 12 tasks passed.
- `node --check
apps/desktop/scripts/smoke-linux-appimage-lifecycle.mjs`, focused `oxfmt
--check`, and `git diff --check`: passed.

> AGENT GENERATED: by GPT-5.6-Sol
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant