Skip to content

fix(spidermonkey-node): show npm install spinner#596

Open
brandonpayton wants to merge 871 commits into
mainfrom
emdash/missing-npm-install-spinner-02xd0
Open

fix(spidermonkey-node): show npm install spinner#596
brandonpayton wants to merge 871 commits into
mainfrom
emdash/missing-npm-install-spinner-02xd0

Conversation

@brandonpayton
Copy link
Copy Markdown
Member

Summary

  • re-enable npm progress in the Node and SpiderMonkey Node browser demo environments
  • run standalone Node demo commands through a PTY so npm can render spinner frames
  • add due-time timer pumping for the SpiderMonkey Node adapter and support timer.refresh() for npm display timers
  • cover npm install spinner output in the SpiderMonkey Node compatibility test

Verification

  • git diff --check
  • generated SpiderMonkey Node bootstrap syntax check
  • node --check on touched JS/TS files and extracted NPM_RUNNER strings

Notes

  • npm --prefix apps/browser-demos run build is blocked in this worktree because @vitejs/plugin-react is not installed
  • tests/sortix/os-test was already dirty and is not part of this commit

…244)

## Summary
- Rewrote `sys_select` to use stack-allocated arrays instead of `Vec`
allocations that leak with the kernel's bump allocator
- Vim (and any program using `select()` in a retry loop) would exhaust
the 1GB kernel memory limit and crash with `RuntimeError: unreachable`
- Zero heap allocations in the select hot path: fd_set copies use `[0u8;
128]` stack buffers, readiness checks use a single stack `WasmPollFd`

## Root Cause
The kernel's wasm bump allocator has a no-op `dealloc`. `sys_select`
created `Vec<WasmPollFd>` and `Vec<(usize, bool, bool, bool)>` on every
call. In centralized mode, `pselect6` retries via `setImmediate` in a
tight loop — each retry leaked ~64KB (one wasm page), reaching the
16384-page (1GB) limit and crashing.

## Test plan
- [x] `cargo test` — 700 passed
- [x] `vitest` — 266 passed (1 pre-existing unrelated failure)
- [x] `libc-test` — 0 FAIL, 22 XFAIL
- [x] `posix-tests` — 0 FAIL, 9 XFAIL
- [x] Browser shell demo: `vim /etc/gitconfig` opens, accepts input
(`:q` works), exits cleanly
## Summary
- esbuild's minifier drops `let` declarations from TypeScript `const
enum` IIFEs in `@xterm/xterm`'s pre-built ESM bundle
- Produces `(void 0||(i={}))` where `i` has no declaration — throws
`ReferenceError: assignment to undeclared variable i` in Firefox (ESM
strict mode)
- Crashes xterm's terminal parser when vim sends DECRQM (request mode)
escape sequences, hanging the session
- Switch to terser which correctly preserves variable declarations

## Test plan
- [x] `vite build` succeeds
- [x] Verified `requestMode` in terser output has proper `let i;`
declaration
- [ ] Vim in browser shell demo works on Firefox after deploy
## Summary
- Include vim doc/*.txt and doc/tags in the runtime bundle so `:help`
works in the browser shell demo
- Excludes version history files (version4-9.txt, ~4.7MB) to keep bundle
size reasonable
- Bundle: 1.2MB → 8.4MB raw (357KB → 2.8MB gzip)
- 147 help files + tags file added (was 55 runtime files, now 203)

## Test plan
- [ ] Run `vim` in browser shell, type `:help` — should open help.txt
- [ ] `:help motion` — should navigate to motion.txt
- [ ] `:help :quit` — should navigate to relevant help topic
## Summary
- Move cross-origin fetch proxying to the service worker for production
deployments (GitHub Pages). The SW detects cross-origin requests and
routes them through a configurable CORS proxy, adding CORP headers for
COEP compliance.
- CORS proxy URL is injected at build time via `VITE_CORS_PROXY_URL` env
var. Deploy script defaults to
`https://wordpress-playground-cors-proxy.com/?`.
- In dev mode, the kernel worker uses vite's `/cors-proxy?url=`
middleware directly (SW isn't active in dev).
- Removes `corsProxyUrl` from `BrowserKernelOptions` — no longer a
user-facing option.

## How it works
- **Production**: Service worker intercepts cross-origin fetches ->
routes through CORS proxy -> adds CORP headers -> COEP satisfied
- **Dev**: Kernel worker catches fetch failures -> retries through vite
middleware at `/cors-proxy?url=` -> same-origin response

## Usage
```bash
# Deploy with default proxy
bash scripts/deploy-gh-pages.sh

# Deploy with custom proxy
VITE_CORS_PROXY_URL="https://my-proxy.example.com/?" bash scripts/deploy-gh-pages.sh
```

## Test plan
- [x] Dev mode: curl https://example.com works via vite CORS proxy
middleware
- [x] Production build: CORS_PROXY_URL correctly injected into
service-worker.js
- [x] Build without env var: CORS_PROXY_URL is empty string (proxy
disabled)
- [x] Cargo tests: 700 passed
- [x] Vitest: 267 passed
- [x] libc-test: 0 FAIL
- [x] POSIX: 0 FAIL
## Summary
- `resolve_path(".", "/dev")` returned `"/dev/."` which didn't match
devfs patterns like `b"/dev"`, causing `ls` to fail with "No such file
or directory" when CWD is `/dev` (while `ls /dev` worked fine)
- Root cause: `resolve_path` concatenated CWD + relative path without
normalizing `.`/`..` components
- Fix: call `normalize_path` on the result in `resolve_path` so all
callers benefit
- Also fixes the same issue for `/proc`, `/dev/pts`, `/dev/shm`, etc.

## Test plan
- [x] Cargo tests pass (703/703, 3 new path tests)
- [x] Vitest pass (267/267)
- [x] libc-test: 0 FAIL, 22 XFAIL
- [x] POSIX: 0 FAIL, 9 XFAIL
- [ ] Shell demo: `cd /dev && ls` works

🤖 Generated with [Claude Code](https://claude.com/claude-code)
The WordPress Playground CORS proxy is at .net not .com, and it
expects raw URLs after the ? delimiter — encodeURIComponent produces
percent-encoded URLs that return HTTP 400.
## Summary
- `/proc` wasn't visible in `ls /` or tab completion because root
directory listings came purely from the host VFS, which has no `/proc`
entry
- Direct access (`ls /proc`, `cat /proc/self/stat`) always worked since
the kernel intercepts all `/proc` paths in stat/open/read
- Fix: `sys_getdents64` now injects a synthetic `proc` entry when
listing `/` after host entries are exhausted
- Handles buffer-full edge case with `-3` dir_handle state for
resumption

## Test plan
- [x] Cargo tests pass (703/703)
- [x] Vitest pass (267/267)
- [x] libc-test: 0 FAIL
- [x] POSIX: 0 FAIL
- [ ] Shell demo: `ls /` shows `proc`, `ls /proc` works, `cd /proc &&
ls` works

🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- `cd /proc` failed with "can't cd to /proc" because `sys_chdir`
validated paths only via `host.host_stat()`, which has no `/proc` entry
- Now checks procfs and devfs virtual filesystems before falling through
to the host VFS
- Combined with PR #249 (path normalization) and #250 (/proc in root
listing), `/proc` is now fully navigable in the shell demo

## Test plan
- [x] Cargo tests pass (703/703)
- [x] Vitest pass (267/267)
- [x] libc-test: 0 FAIL
- [x] POSIX: 0 FAIL
- [ ] Shell demo: `cd /proc && ls` works

🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- In dev mode, the `__CORS_PROXY_URL__` placeholder in
`service-worker.js` is served as-is (the vite build-time replacement
only runs during `vite build`). The SW was treating this literal string
as a proxy URL, causing broken cross-origin fetch attempts.
- Added a guard that detects the unreplaced placeholder and treats it as
empty, so the SW correctly falls back to no-proxy behavior in dev mode.

## Test plan
- [x] Verified `wget https://automattic.com/...` works on deployed
GitHub Pages shell demo (496KB JPEG downloaded successfully over HTTPS
via CORS proxy)
- [x] Dev mode unaffected (TLS backend's own vite middleware proxy
handles cross-origin fetches)
## Summary

- Build `mysqltest` (mariadb-test) binary for wasm32 — extends the
existing MariaDB cross-compilation pipeline to produce a 3.8MB
mysqltest.wasm plus all 1,184 upstream mysql-test suite test files
- TypeScript test harness (`examples/mariadb-test/run-tests.ts`) manages
the full lifecycle: bootstrap system tables, start mariadbd in server
mode, run individual mysqltest processes per-test, handle timeouts with
automatic server restart
- Shell wrapper (`scripts/run-mariadb-tests.sh`) with a curated set of
**89 passing tests** and comprehensive XFAIL categorization covering
~600 known-failing tests

### Infrastructure fixes discovered during triage

- **Root TCP grants**: `FLUSH PRIVILEGES` re-enables grant checking;
setup SQL inserts `root@127.0.0.1` and `root@%` with full passwordless
access
- **Stored procedure definer**: `CREATE DEFINER='root'@'localhost'`
prevents "definer does not exist" errors after `FLUSH PRIVILEGES`
- **Aria crash recovery**: server restart cleans `aria_log_control` to
avoid checksum mismatch
- **`--protocol=tcp`**: forces TCP connections to avoid unix socket
attempts

### Failure categories (XFAIL)

| Category | Count | Reason |
|----------|-------|--------|
| Multi-connection | ~111 | `connect` command deadlocks with
`--thread-handling=no-threads` |
| InnoDB | ~228 | Storage engine not available (Aria only) |
| exec/system | ~109 | Shell commands not supported in wasm |
| Debug | ~78 | Requires debug build (`debug_dbug`, `debug_sync`) |
| Timeout | ~27 | Tests too slow for 60s on wasm |
| Grants/SSL/binlog/plugins | ~50+ | Various unsupported features |

## Test plan

- [ ] `scripts/run-mariadb-tests.sh` runs 89 curated tests with 0
unexpected failures
- [ ] `scripts/run-mariadb-tests.sh --all` runs all 1,184 tests (slow;
failures are XFAIL)
- [ ] Existing test suites unaffected (cargo, vitest, libc-test, POSIX,
sortix)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary

- Add `mariadb` test suite to `run.sh` dispatcher — run via `./run.sh
test mariadb`
- Register `mysqltest.wasm` in `builtinPrograms` map in `run-example.ts`
for exec resolution

Follow-up to #253 (MariaDB mysql-test suite infrastructure).

## Test plan

- [ ] `./run.sh test mariadb` runs the curated 89-test suite
- [ ] No impact on existing test suites

🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Port Erlang/OTP 28.2 BEAM emulator to wasm-posix-kernel
- BEAM boots with single scheduler + dirty schedulers (8 threads total),
loads kernel/stdlib applications, and executes Erlang code
- Full support for process spawning, message passing, ETS, list
comprehensions, maps, binaries, pattern matching

## Platform fixes
- **musl qsort replacement**: Heapsort replaces smoothsort which has
shift-by-32 UB on wasm32
- **handleReadv chunked reads**: Large readv calls (up to 253KB .beam
files) now chunk data to avoid 64KB scratch buffer overflow
- **Driver signature fix**: Added 3rd parameter to inet/ram_file driver
start functions for wasm indirect call table type checking
- **ESTACK → local stack**: `db_is_fully_bound` uses fixed local stack
to avoid allocator corruption during ETS operations
- **BEAM loader insertion sort**: Replaced qsort in select_val/map_key
sorting to fix non-deterministic loader corruption
- **THE_NON_VALUE hash guard**: `erts_internal_hash(0x0)` returns 0
instead of crashing
- **Code path fix**: Add kernel/stdlib ebin directories to `-pa` for
dynamic module loading after boot

## Build & Run
```bash
# Build (requires host Erlang OTP 28)
bash examples/libs/erlang/build-erlang.sh

# Run
npx tsx examples/erlang/serve.ts -eval 'io:format("Hello from BEAM!~n"), halt().'
```

## Output
```
=== Erlang/OTP on wasm-posix-kernel ===
OTP release: 28
ERTS version: 16.1.2
Word size: 4
Schedulers: 1
Atoms: 10164
Processes: 43
ETS tables: 19

List comprehension: [1,4,9,16,25,36,49,64,81,100]
Map: #{hello => world,beam => wasm}
Binary: <<1,2,3,4,5>>
```

## Test plan
- [x] Cargo tests: 703 passed, 0 failed
- [x] Vitest: 199 passed, 0 failed
- [x] libc-test: 0 FAIL, 14 XFAIL
- [x] POSIX tests: 0 FAIL, 9 XFAIL
- [x] BEAM boots and prints "Hello from BEAM!"
- [x] Erlang process spawning and message passing works
- [x] System info queries (OTP release, ERTS version, word size) return
correct values

🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary

- Adds browser variant of MariaDB mysql-test infrastructure
- Full triage of all 1184 upstream tests in headless Chromium
- **185 tests pass** in the browser (up from 88 Node.js-derived curated
set)
- Auto-reload on server hang for robust large-scale triage runs

## Files

- `scripts/build-mariadb-test-bundle.sh` — packages test files as JSON
bundle for browser VFS (`--all` for full triage)
- `examples/browser/pages/mariadb-test/` — boots MariaDB via SystemInit,
exposes `window.__runMariadbTest()`
- `scripts/browser-mariadb-test-runner.ts` — Playwright runner with
batch support and hang recovery
- `scripts/run-browser-mariadb-tests.sh` — shell wrapper with 185
curated tests
- `run.sh` — integrated as `browser-mariadb` test suite

## Full triage results (1184 tests)

| Status | Count |
|--------|-------|
| PASS | 185 |
| SKIP | 143 |
| TIMEOUT | 339 |
| CONNECT-DEADLOCK | 230 |
| OTHER FAIL | 287 |

## Test plan
- [x] Full triage of all 1184 tests in browser
- [x] Verification pass: 185/185 curated tests pass on fresh boot
- [x] `./run.sh test browser-mariadb` runs successfully

🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary

- **nginx test suite**: Drop-in nginx wrapper for the upstream
Test::Nginx Perl framework. Runs nginx.wasm in CentralizedKernelWorker
with fork support. Full triage of 484 tests: 32 pass, 410 skip (missing
modules like rewrite/gzip/SSL/stream), 40 expected failures (proxy
backends, timing), 2 timeouts.
- **SQLite test suite**: Builds sqlite3 CLI (1.4MB) with FTS5, JSON1,
and math functions. 17 comprehensive SQL test scripts covering all major
SQLite features (CRUD, types, expressions, aggregates, joins, indexes,
transactions, constraints, views, CTEs, window functions, triggers,
subqueries, date/time, JSON, FTS, schema DDL, complex analytics).
- **stdin piping fix**: `run-example.ts` now reads piped stdin data and
forwards to wasm processes (previously set empty stdin, breaking
`sqlite3 < test.sql` pattern).
- **run.sh integration**: Added `nginx` and `sqlite` test suite targets.

## Test plan

- [x] `./run.sh test nginx` — 32/32 pass
- [x] `./run.sh test sqlite` — 17/17 pass
- [x] `scripts/run-nginx-tests.sh --all` — 32 PASS, 410 SKIP, 40 XFAIL,
2 TIME (0 unexpected)
- Rewrite ring.erl with correct ring topology (self as counter,
  spawned processes as forwarders). Old version had a deadlock bug
  where set_next broke the chain.

- Work around BEAM wasm32 ~f format bug: float_to_list/1 uses broken
  ryu/snprintf path, use float_to_list/2 with {decimals,N} instead.

- Track thread workers in serve.ts for proper cleanup on exit. BEAM's
  halt(0) hangs in pthread_join because threads are blocked on futex
  waits. Add halt detection: if stdout goes idle for 2s after output,
  force clean exit.

- Remove unused threadChannelOffsets array and keepalive timer.

Ring benchmark results: 1000 processes, 100 rounds = 100K messages
in ~54ms (~1.85M messages/sec).
## Summary
- Port Erlang/OTP 28.2 BEAM emulator to wasm-posix-kernel with threading
support (dirty schedulers via clone/futex)
- Fix musl's qsort (smoothsort shift-by-32 UB on wasm32), handleReadv
for large reads, driver signature mismatches, ETS ESTACK crash
- Add ring benchmark (1000 processes x 100 rounds, ~54ms for 100K
messages) with halt detection workaround for BEAM's pthread_join hang

## Test plan
- [x] cargo tests: 703 passed
- [x] vitest: 199 passed
- [x] libc-test: 0 FAIL / 22 XFAIL
- [x] POSIX tests: 0 FAIL / 9 XFAIL
- [x] BEAM boots and runs `io:format("Hello from BEAM!~n"), halt().`
- [x] Ring benchmark completes: 1000 processes, 100 rounds, 100K
messages

🤖 Generated with [Claude Code](https://claude.com/claude-code)
LLVM's wasm32 backend miscompiles aggregate initialization of structs
containing pointers to shadow-stack local arrays at -O2. This causes
BEAM's ESTACK macros and iodata traversal in erl_unicode.c to produce
garbage, breaking unicode:characters_to_binary/2 and filename conversion.

Two fixes:
- global.h: explicit field-by-field ESTACK/WSTACK init on wasm32
- Makefile: compile erl_unicode.c at -O1 to avoid the codegen bug

Both patches are applied automatically by build-erlang.sh.
## Summary
- Fix LLVM wasm32 backend miscompiling aggregate struct initialization
with shadow-stack pointers at -O2
- Add `global.h` patch: explicit field-by-field ESTACK/WSTACK init under
`#ifdef __wasm32__`
- Add Makefile patch: compile `erl_unicode.c` at `-O1` to avoid iodata
traversal codegen bug
- Both patches applied automatically by `build-erlang.sh`

## Details
BEAM's `unicode:characters_to_binary/2` BIF and
`erts_native_filename_need` traverse iodata using ESTACK macros
(stack-based term traversal). At `-O2`, LLVM's wasm32 backend
miscompiles the aggregate initialization of `ErtsEStack`/`ErtsWStack`
structs that contain pointers to shadow-stack local arrays. This
manifests as:
- `{error, <<>>, {}}` from `unicode:characters_to_binary([[]])`
- EISDIR error opening `start_clean.boot` (filename conversion fails)

The bug is a classic heisenbug — adding `fprintf(stderr, ...)` debug
calls changes code layout enough to prevent miscompilation.

**Root cause**: LLVM optimization passes at `-O2` incorrectly handle
struct fields pointing to `__stack_pointer`-relative local arrays on
wasm32.

**Fix**: Two-pronged approach:
1. `global.h`: Replace aggregate initialization with explicit
field-by-field assignment under `#ifdef __wasm32__` (defensive)
2. `Makefile`: Compile `erl_unicode.c` at `-O1` (fixes the actual
codegen bug)

## Test plan
- [x] BEAM boots and runs: `io:format("Hello from BEAM!~n"), halt().`
- [x] Arithmetic, string, list comprehension, map, binary, atom
operations work
- [x] Cargo tests: 703 passed
- [x] Vitest: 199 passed
- [x] musl libc-test: 0 FAIL (19 XFAIL)
- [x] POSIX test suite: 0 FAIL (1 XFAIL)
- Add build/run/clean/list targets for Erlang in run.sh
- ./run.sh build erlang  — builds BEAM via build-erlang.sh
- ./run.sh run erlang    — starts BEAM emulator
- Add 5 vitest integration tests: hello world, arithmetic, lists,
  process spawning, and ring benchmark message passing
## Summary
- Add `erlang` build/run/clean/list targets to `run.sh`
- `./run.sh build erlang` builds BEAM VM via build-erlang.sh
- `./run.sh run erlang -eval 'Expr'` starts the BEAM emulator
- Add 5 vitest integration tests covering hello world, arithmetic, list
operations, lightweight process spawning, and ring benchmark message
passing

## Test plan
- [x] `./run.sh list` shows Erlang with status indicator
- [x] `./run.sh run erlang -eval 'io:format("Hello!~n"), halt().'` works
- [x] Vitest: 204 passed (5 new Erlang tests), 0 failures
- [x] Cargo: 703 passed
- [x] libc-test: 0 FAIL
- [x] POSIX: 0 FAIL
Remove unconditional console.error for signal delivery in
kernel-worker.ts that polluted stderr, causing 15 sortix test
failures (signal output comparison mismatch).

Also clean up verbose patchWasmForThread and Erlang serve.ts
debug logging.
aio_read requires pthreads (musl's AIO uses helper threads) which
are not supported for guest-initiated pthread_create. Same reason
as aio_error and aio_fsync.
## Summary
- Remove unconditional `console.error` for signal delivery in
`kernel-worker.ts` that polluted stderr output
- Clean up verbose `patchWasmForThread` debug logging in
`worker-main.ts`
- Remove noisy clone/fork/thread/exit debug logs from Erlang `serve.ts`

## Details
The Erlang/OTP PR (#255) introduced an unconditional `console.error` log
line in `kernel-worker.ts` that fired on every signal delivery. Since
sortix signal tests capture stderr+stdout (`2>&1`) and do exact string
comparison against expected output, the extra `[signal] pid=100
signum=10 handler=3 flags=0x...` line caused all 15 signal-related tests
to fail.

## Test plan
- [x] Vitest: 204 passed, 0 failures
- [x] Sortix signal tests: 32/32 PASS (was 15 FAIL)
- [x] Erlang BEAM still works with cleaned-up output
- [ ] Full sortix suite running (waiting for confirmation of 0 FAIL)
- Remove unused imports (S_IFCHR, S_IFLNK) from devfs.rs
- Prefix unused test variables with underscore in memory.rs
- Remove unused any_interested variable in select() syscall
- Suppress dead_code warnings on POSIX constants kept for reference
  (FUTEX_FD, CLONE_CHILD_SETTID, EPOLLRDHUP, MqQueue fields)
## Summary
- Remove unused imports (`S_IFCHR`, `S_IFLNK`) from devfs.rs
- Prefix unused test variables with underscore in memory.rs
- Remove unused `any_interested` variable in select() syscall
- Suppress dead_code warnings on POSIX constants kept for API reference

Eliminates all 10 compiler warnings from `cargo test`.

## Test plan
- [x] Cargo: 703 passed, 0 warnings
- [x] Zero warnings in build output (excluding expected unstable feature
note)
Added a warning about .wasm binary builds in the repository history and future history rewrite.
brandonpayton and others added 30 commits May 21, 2026 21:44
## Summary
- add missing browser deploy path filters for rootfs, benchmark,
example, and mkrootfs inputs
- include host package metadata and rootfs/overlay helper scripts that
can affect prepare-browser
- bump checkout, setup-node, and gh-pages deploy actions to tagged Node
24-compatible releases

## Validation
- clean checkout simulation: fetched musl submodule, installed npm deps,
ran `bash scripts/dev-shell.sh ./run.sh prepare-browser --allow-stale`
- clean checkout simulation: ran `VITE_BASE=/kandelo/ npm run build` in
`apps/browser-demos`
- main deploy after #541 passed:
https://github.com/brandonpayton/kandelo/actions/runs/26261916751
- `ruby -e 'require \"yaml\";
YAML.load_file(\".github/workflows/browser-demos-pages.yml\")'`
- `git diff --check origin/main..HEAD`
Summary
- Inject the configured CORS proxy URL into production service-worker.js
by default.
- Pass VITE_CORS_PROXY_URL in the GitHub Pages deployment workflow.
- Add a deploy-script verification that fails if the proxy URL is not
injected.
- Harden the service worker cross-origin proxy path so the page sees a
synthetic CORS/CORP/COEP-compatible response; the external CORS proxy
only needs to provide ordinary CORS for the service worker fetch.
- Add a Kandelo startup gate so live boot, web bridge setup, and
software-gallery fetches wait until the unified service worker is
installed, active, controlling the app, and the page is cross-origin
isolated.
- Make the isolation gate visible: the boot log reports the activation
reload, and a failed post-reload isolation check becomes a normal
Kandelo boot error instead of hanging quietly.

Verification
- git diff --check
- bash -n scripts/deploy-gh-pages.sh
- node --check apps/browser-demos/public/service-worker.js
- Focused Vite plugin check emitted `var CORS_PROXY_URL =
"https://wordpress-playground-cors-proxy.net/?";`
- Confirmed the proxy returns CORS headers for the Doom WAD from
`https://brandonpayton.github.io`
- Synthetic service-worker/proxy smoke test passed in Playwright
Chromium, Firefox, and WebKit with `crossOriginIsolated: true` and
controlled cross-origin fetches

Note
- Full `npm run build` could not complete in this worktree because
generated kernel artifacts are missing (`local-binaries/kernel.wasm` and
`binaries/kernel.wasm`). CI prepares these before building.
- Full `tsc -p apps/browser-demos/tsconfig.json --noEmit` currently
reports existing project-wide type errors outside this change set.
Summary
- Add forwarded host/proto/prefix headers when the service worker sends
app requests through the HTTP bridge.
- Rewrite same-host absolute `http://...` URLs in bridged text responses
to the browser-visible app URL, preserving `/kandelo/app` and avoiding
HTTPS mixed-content blocks.
- Handle both plain URLs and escaped URL strings emitted in
HTML/JS/JSON/CSS-like text responses.

Verification
- node --check apps/browser-demos/public/service-worker.js
- git diff --check origin/main..HEAD
- Focused rewrite check covers WordPress-style
`http://brandonpayton.github.io/wp-includes/...`, escaped
`http:\/\/...`, and already-prefixed
`http://brandonpayton.github.io/kandelo/app/...` URLs.
Summary:
- Adds a generic Kandelo demo guide pane driven by
/etc/kandelo/demo.json metadata from the VFS image.
- Supports approved terminal actions, raw REPL input, editable scripts,
and sandboxed companion HTML action requests.
- Moves built-in guide metadata into VFS builders for shell, Node,
nginx, and nginx+PHP; Doom and WordPress omit guides.
- Documents the metadata format for kandelo-software REPL demos.

Verification:
- npm --prefix apps/browser-demos run build
- npx vitest run web-libs/kandelo-session/test/kandelo-session.test.ts
- host/node_modules/.bin/tsc --noEmit ... images/vfs/scripts/*kandelo*
and VFS builders
- Playwright smoke: shell guide/action ok, Doom no guide, WordPress
SQLite no guide
## Summary

- patch WordPress runtime config before boot in Kandelo, WordPress
SQLite, and LAMP demos so WordPress generates `https://.../kandelo/app`
URLs and forces `home`/`siteurl` through an MU plugin
- route `vim.zip` and `nethack.zip` lazy archives through Vite-emitted
asset URLs instead of assuming root-level `/kandelo/*.zip` files exist
- make lazy binary size probing fall back from `HEAD` to a one-byte
range request, so shell utilities are not silently skipped when
`Content-Length` is unavailable
- expand service-worker rewriting for same-host `http://` URLs in
bridged text, JSON/XML-ish responses, encoded URL values, and `Link`
headers

## Verification

- `node --check apps/browser-demos/public/service-worker.js`
- `git diff --check origin/main..HEAD`
- `npx --prefix apps/browser-demos tsc
apps/browser-demos/lib/init/fetch-size.ts
apps/browser-demos/lib/init/lazy-archives.ts
apps/browser-demos/lib/init/wordpress-runtime-config.ts --noEmit
--target ES2022 --module ESNext --moduleResolution bundler --lib
ES2022,DOM --types vite/client --skipLibCheck`
- `npm --prefix apps/browser-demos run build` still cannot complete in
this worktree because `kernel.wasm` is not present at
`local-binaries/kernel.wasm` or `binaries/kernel.wasm`
## Summary
- add agent-facing guidance that Kandelo demo presentation metadata
belongs in VFS images
- document that missing demo.json should use generic defaults, not
demo-specific app fallbacks
- point agents to the existing human-facing browser support docs

## Testing
- git diff --check origin/main...HEAD
## Summary
- initialize a temporary git repository before cleanup jobs delete
remote PR branches
- document that PRs merged by GITHUB_TOKEN-backed workflows may miss the
close-event cleanup path

## Why
The scheduled/manual staging cleanup sweep was finding stale merged
ready-to-ship PR branches, but remote deletion failed with `fatal: not a
git repository` because the job never checked out or initialized a repo
before `git push <remote> :refs/heads/<branch>`.

## Validation
- `ruby -e 'require "yaml";
YAML.load_file(".github/workflows/staging-cleanup.yml")'`\n- `git diff
--check -- .github/workflows/staging-cleanup.yml`
## Summary
- Add `skip-staging-tests` handling to staging-build, including
`labeled`/`unlabeled` triggers so label changes refresh checks and
cancel older runs.
- Keep staging preflight, toolchain cache, package matrix builds, and
binary materialization intact while skipping only the expensive
post-materialization test suites.
- Prevent staging from posting merge-gate success when staging tests are
skipped; `prepare-merge.yml` is unchanged.

## Validation
- `ruby -e 'require "yaml"; YAML.load_file(ARGV.fetch(0)); puts "YAML
parsed"' .github/workflows/staging-build.yml`\n- `git diff --check --
.github/workflows/staging-build.yml`\n- Confirmed workflow-only changes
do not match the package staging path filter
(`package_staging_required=false`).\n- `actionlint` is not installed
locally.
## Summary
- capture one current base SHA at the start of prepare-merge
- synthesize and share a PR-head-into-base merge commit through
downstream jobs
- switch package preflight/build/promote/test-gate paths off GitHub's
refs/pull/*/merge
- keep final base-drift checks before posting merge-gate

## Validation
- ruby -e 'require "yaml";
YAML.load_file(".github/workflows/prepare-merge.yml")'\n- git diff
--check -- .github/workflows/prepare-merge.yml\n- rg scan for stale
github.ref/github.sha/PR merge-ref usage in prepare-merge.yml\n\nNote:
actionlint was not installed locally, and npx actionlint did not expose
an executable.
## Summary
- Keep staging-build label refreshes scoped to the `skip-staging-tests`
label.
- Put unrelated label events, including `ready-to-ship`, in an isolated
concurrency group so they cannot cancel active staging runs.
- Skip the staging job graph for unrelated label/unlabel events.

## Validation
- `ruby -e 'require "yaml"; YAML.load_file(ARGV.fetch(0)); puts "YAML
parsed"' .github/workflows/staging-build.yml`\n- `git diff --check --
.github/workflows/staging-build.yml`\n- `actionlint` skipped: not
installed locally, and `go` is not installed to run it via `go
run`.\n\n## Note\nThe broad label trigger was introduced by `f05c61cf` /
#556 when `skip-staging-tests` support was added.
## Summary
- Adds `kernel.fetchInKernel(port, request)` on both `NodeKernelHost`
and `BrowserKernel` for sending HTTP requests directly to in-kernel
servers, bypassing real TCP.
- Consolidates the existing browser SW-bridge connection pump into one
public method `CentralizedKernelWorker.sendHttpRequest`, deleting ~165
lines of duplicate inline pump/parse code from `kernel-worker-entry.ts`.
- Migrates the WordPress + LAMP browser demos to the new API via a
`setupServiceWorkerFetchBridge` helper. End-to-end verified in a browser
— the WordPress install page renders.
- Migrates `examples/wordpress/test/wordpress-server.test.ts` from
manual kernel boilerplate + real-port allocation to
`NodeKernelHost.fetchInKernel`. Test runs in <1s vs ~30s.

See
[docs/plans/2026-04-30-external-kernel-http-request-interface.md](docs/plans/2026-04-30-external-kernel-http-request-interface.md)
for the full design and migration findings.

## What's new
- `host/src/networking/in-kernel-http.ts` — shared
`HttpRequest`/`HttpResponse` types, raw HTTP/1.1 framing helpers
(request build + response parse with chunked decoding + multi-Set-Cookie
merging).
- `CentralizedKernelWorker.sendHttpRequest` — picks listener target,
calls `kernel_inject_connection`, writes the request, pumps the
response, parses it. Configurable timeout; returns 504 on expiry.
`pickListenerTarget` is now public.
- `NodeKernelHost.fetchInKernel` + `BrowserKernel.fetchInKernel` — thin
host-side proxies plumbed through a new `http_request` worker-protocol
message.
- `examples/browser/lib/init/sw-bridge-fetch.ts` —
`setupServiceWorkerFetchBridge` helper that wires a service-worker
`HttpBridgeHost` to `fetchInKernel` and reinstalls itself when the SW
restarts.
- `programs/tiny-http-server.c` — minimal single-shot HTTP/1.1 server
used as a test fixture.
- `examples/run-fetch-in-kernel.ts` — small CLI demo script.

## Tests
- `host/test/in-kernel-http.test.ts` — 5 tests: 1 integration round-trip
through `NodeKernelHost.fetchInKernel`, 1 no-listener-error, 3 codec
unit tests.
- `host/test/browser-kernel.test.ts` — 7 tests: mocks
`globalThis.Worker` to verify BrowserKernel's message-protocol contract
(boot, fetchInKernel emission/timeout/error/concurrent IDs, destroy).
Required a small Vite-URL stub plugin in `host/vitest.config.ts` to make
`BrowserKernel` loadable in vitest.
- `examples/wordpress/test/wordpress-server.test.ts` — rewritten to use
`fetchInKernel`; ~80 lines vs ~240, runs in <1s.
- All 351 host vitest tests pass (only the pre-existing `wasm64.test.ts`
skip remains, unrelated to this work).

## Out of scope (intentional)
- The `serve.ts` / `serve-nginx.ts` Node scripts don't migrate — they
expose a real localhost port for outside clients, which is the opposite
contract from `fetchInKernel`. The design doc explains.
- Streaming bodies, HTTPS, HTTP/2, WebSocket upgrade, HTTP/1.1
pipelining — prototype scope.
- The `nginx` / `nginx-php` browser demos still use the old
`sendBridgePort` path. Consolidating them onto the new helper is a
straightforward follow-up.

## Test plan
- [x] `cd host && npx vitest run --exclude '**/wasm64.test.ts'` — 351
passed
- [x] `npx vitest run test/in-kernel-http.test.ts` — 5 passed
- [x] `npx vitest run test/browser-kernel.test.ts` — 7 passed
- [x] `npx vitest run
../examples/wordpress/test/wordpress-server.test.ts` — passes
- [x] `npx tsx examples/run-fetch-in-kernel.ts` — round-trips a request
to the in-kernel C server
- [x] `npx vitest run test/nginx.test.ts` — pre-existing real-TCP path
still works
- [x] Browser end-to-end — booted the WordPress demo via `vite`, clicked
Start, watched the WordPress install page render inside the iframe via
the new `fetchInKernel` bridge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
- move the host syscall pointer-marshalling descriptor table into
`wasm_posix_shared::host_abi`
- generate `SYSCALL_ARGS` TypeScript bindings and
`syscall_arg_descriptors` ABI snapshot coverage from Rust metadata
- migrate `kernel-worker.ts` to consume generated descriptors and remove
TS-owned syscall size switches for poll/ppoll, SysV msg prefix handling,
semop sizing, and msgrcv copy-back adjustment
- keep platform-bound memory copies, Atomics scheduling, Worker/Wasmtime
boot, scatter/gather nested-pointer handling, epoll interception, and
host I/O in TypeScript

## ABI notes
- no `ABI_VERSION` bump: the ABI checker classifies the new snapshot
section as additive-compatible coverage
- changes to existing syscall arg descriptors are now classified as
breaking/incompatible

## Tests
- `nix develop -c cargo test -p wasm-posix-shared --target
aarch64-apple-darwin --lib host_abi`
- `nix develop -c bash -lc 'HOST_TARGET=$(rustc -vV | awk "/host:/
{print \\$2}"); cargo test -p xtask --target "$HOST_TARGET"
dump_abi'`\n- `nix develop -c bash scripts/check-abi-version.sh`\n-
focused generated ABI vitest: `cd host && npx vitest run --config <temp
generated-abi config>`\n- `nix develop -c npm --prefix host run
build`\n- `nix develop -c cargo test -p wasm-posix-kernel --target
aarch64-apple-darwin --lib`\n- `git diff --check`\n\n## Blocked check\n-
`nix develop -c npm --prefix host test -- --run` is blocked in this
worktree before test collection because `test/global-setup.ts` tries to
compile `examples/putenv_test.c`, but `sysroot` is missing and asks to
run `scripts/build-musl.sh`.\n
Summary:
- add versioned image-level metadata to VFS images with optional
kernelAbi declarations
- expose metadata read/write and ABI assertion helpers on
MemoryFileSystem
- stamp mkrootfs/rootfs builds with ABI metadata and add inspect
--metadata output

Verification:
- cd host && npx vitest run test/vfs-image.test.ts --config /dev/null
- cd tools/mkrootfs && npm test -- test/builder.test.ts test/cli.test.ts
- cd host && npm run build
- git diff --check
## Summary
- add package-system browser bundle entries for `vim.zip` and
`nethack.zip`, so publishing can produce resolver-visible bare zip
outputs
- route `run.sh` Vim zip generation through the shared helper and add
`nethack-zip` to browser asset prep
- make Vite's `@binaries/` resolver apply the same implicit
`programs/wasm32/` arch rule as the shell/host resolver
- fix the zip helpers to write generated bundles to
`apps/browser-demos/public/` before installing them into
`local-binaries/`

## Verification
- `bash -n run.sh
packages/registry/vim-browser-bundle/build-vim-browser-bundle.sh
packages/registry/nethack-browser-bundle/build-nethack-browser-bundle.sh`
- `bash scripts/dev-shell.sh cargo run -p xtask --target "$HOST_TARGET"
--quiet -- build-deps output-path vim-browser-bundle vim.zip`
- `bash scripts/dev-shell.sh cargo run -p xtask --target "$HOST_TARGET"
--quiet -- build-deps output-path nethack-browser-bundle nethack.zip`
- `bash scripts/dev-shell.sh cargo run -p xtask --target "$HOST_TARGET"
--quiet -- compute-cache-key-sha --package
packages/registry/vim-browser-bundle --arch wasm32`
- `bash scripts/dev-shell.sh cargo run -p xtask --target "$HOST_TARGET"
--quiet -- compute-cache-key-sha --package
packages/registry/nethack-browser-bundle --arch wasm32`
- `bash scripts/dev-shell.sh ./run.sh build vim-zip nethack-zip`
- `bash scripts/dev-shell.sh cargo run -p xtask --target "$HOST_TARGET"
--quiet -- archive-stage --package packages/registry/vim-browser-bundle
--arch wasm32 --out /tmp/kandelo-vim-browser-bundle-stage
--build-timestamp 2026-05-22T00:00:00Z --build-host local-test`
- `bash scripts/dev-shell.sh cargo run -p xtask --target "$HOST_TARGET"
--quiet -- archive-stage --package
packages/registry/nethack-browser-bundle --arch wasm32 --out
/tmp/kandelo-nethack-browser-bundle-stage --build-timestamp
2026-05-22T00:00:00Z --build-host local-test`
- `bash scripts/dev-shell.sh ./run.sh prepare-browser --allow-stale`
- `VITE_BASE=/kandelo/
VITE_CORS_PROXY_URL=https://wordpress-playground-cors-proxy.net/? npm
run build`
## Summary
- Bake shell utility lazy-file stubs, placeholder URLs, and actual wasm
sizes into the shell and node VFS images.
- Rewrite those placeholders to Vite asset URLs at browser startup and
forward baked lazy-file metadata from `BrowserKernel.init()`.
- Remove shell/Kandelo boot-time utility `HEAD` size probes; update
shell/node-vfs package deps and revisions so stale VFS images are not
reused.

## Verification
- `npx vitest --run --config host/.vitest-lazy-temp.mjs` with
`lazy-vfs.test.ts` and `vfs-image.test.ts`: 40 tests passed.
- `nix develop -c bash packages/registry/shell/build-shell.sh`: passed;
inspected shell VFS image has 21 lazy entries with real target wasm
sizes.
- `nix develop -c bash packages/registry/node-vfs/build-node-vfs.sh`:
passed; inspected node VFS image has `/usr/bin/node` lazy entry with
real target wasm size.
- `npm --prefix apps/browser-demos run build`: still blocked in this
worktree by missing unrelated artifact
`programs/wasm32/nginx-php-vfs.vfs.zst`.
## Summary
- add a core `kandelo-sdk` package that builds the in-session SDK VFS
image
- add guest SDK wrappers under `sdk/kandelo/bin` for `wasm32posix-cc`,
`wasm32posix-c++`, binutils wrappers, configure, and pkg-config
- add the SDK VFS image builder under `images/vfs/scripts`, including
sysroot, C/C++ libraries, syscall glue, precompiled glue objects, clang
resource headers, and license notices

## Package Boundary
This keeps the SDK/sysroot image in the core Kandelo repo. The larger
Clang toolchain payload is maintained separately in
`brandonpayton/kandelo-software`, where it can be staged lazily or as a
lazy archive-backed payload.

## Validation
- `bash -n packages/registry/kandelo-sdk/build-kandelo-sdk.sh
images/vfs/scripts/build-kandelo-sdk-vfs-image.sh
sdk/kandelo/bin/wasm32posix-*`
- `git diff --check`
- `cargo run --target <host> -p xtask -- build-deps parse
packages/registry/kandelo-sdk/package.toml`
- `cargo run --target <host> -p xtask -- build-deps check`
- `cargo run --target <host> -p xtask -- compute-cache-key-sha --package
packages/registry/kandelo-sdk --arch wasm32`
- direct SDK VFS build using a temporary sysroot copied from the
existing local sysroot; output was 256.0 MB raw and 3.3 MB zstd

The direct build installed only ignored local artifacts in this isolated
worktree (`local-binaries/`,
`packages/registry/kandelo-sdk/kandelo-sdk.vfs.zst`, and glue object
output).
## Summary

- Add a `spidermonkey` registry package pinned to Firefox ESR
`140.11.0esr`, producing the standalone SpiderMonkey shell as `js.wasm`.
- Add the Kandelo/wasm32 integration patches needed for the Mozilla JS
shell build, including wasm target mapping, randomness, stack/crash
handling, and wasm-compatible configure/runtime paths.
- Fix supporting platform issues uncovered by the port: SDK
linker/configure flag handling, cbindgen in the dev shell, ABI parsing
without invoking wasm constructors, fork-instrumented constructor/thread
startup handling, and musl overlay fixes.
- Update the SpiderMonkey Node runtime design doc to mark the standalone
engine shell gate green and leave the Node-compatible SpiderMonkey
embedding for the follow-up PR.

## Validation

- `cargo run -p xtask --target "$HOST_TRIPLE" -- build-deps resolve
spidermonkey`
- Direct kernel run of `js -e "print(1 + 1)"`: stdout `2`, exit `0`,
empty stderr
- `npx vitest run
packages/registry/spidermonkey/test/spidermonkey.test.ts
host/test/wasm-binary-parse.test.ts host/test/exec.test.ts
host/test/wasm-trap.test.ts host/test/thread-allocator.test.ts`
- `cd sdk && npm test`
- `cd host && npm run build`
- Fresh Firefox ESR source patch-application check for
`packages/registry/spidermonkey/patches/*.patch`

## Follow-up

The Node.js-compatible environment using SpiderMonkey is intentionally
out of scope here. This PR lands the engine shell package and the
platform fixes needed to run JavaScript on SpiderMonkey under Kandelo.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
- remove the Kandelo MockKernelHost runtime, `?mock=1` entry path, and
mock-only fixture data
- keep live gallery presets in a dedicated `presets.ts` module used by
the real boot path
- update Kandelo/session tests, comments, docs, and the COI spec to
reflect live-only UI behavior

## Testing
- `./host/node_modules/.bin/vitest run
web-libs/kandelo-session/test/kandelo-session.test.ts`
- `git diff --check`
- `npm --prefix apps/browser-demos run build` *(blocked: missing
`kernel.wasm` in `local-binaries/` or `binaries/` in this worktree)*

Note: `tests/sortix/os-test` was dirty before the commit and is not
included.
## Summary
- preserve Kandelo gallery-selected VFS image URLs in browser state
- fix WordPress + MariaDB Kandelo boot settings to match the working
LAMP profile
- prevent service worker app-readiness probes from capturing the Kandelo
shell page and redirecting later gallery launches under /app/

## Verification
- `KANDELO_TEST_BASE_URL=http://127.0.0.1:5328 npm test --
--project=chromium test/kandelo-url.spec.ts`
- manually booted WordPress MariaDB in Kandelo to HTTP preview ready
with nginx, php-fpm, and mariadb listening

## Note
- `npm run build` still fails before Kandelo on missing
`examples/gencat.wasm` from `pages/test-runner/main.ts`.
## Summary
- forward focused framebuffer keydown/keyup events as Linux MEDIUMRAW
input, with Ctrl+Shift+Esc as the host escape
- update Doom and Kandelo framebuffer panes to use the shared keyboard
helper
- fix Doom saves by using the user HOME directory, with a compatibility
alias for older fbDOOM builds
- add a Doom-only Vite config for focused demo builds

## Testing
- npx vitest run test/framebuffer-browser-controls.test.ts --config
/dev/null --root .
- npm run build
- npm run build -- --config vite.doom.config.ts
- Playwright smoke test for Doom save name `wasd` with no save errors
- git diff --check
## Summary
- classify `staging-build` runs before prepare-merge decides whether to
wait, block, or continue
- allow prepare-merge to continue to its own synthetic-merge package
validation when the only staging run is the intentionally skipped
label-event run
- keep real failed/in-progress/successful staging runs authoritative
when they exist
- add a small jq selector fixture test for the run-state cases

## Verification
- `.github/scripts/test-select-staging-build-run.sh`
- `ruby -e 'require "yaml"; ARGV.each { |f| YAML.load_file(f); puts "ok
#{f}" }' .github/workflows/prepare-merge.yml
.github/workflows/staging-build.yml`
- `git diff --check`
- confirmed PR #564 and #566 staging-build run lists classify as
`only_skipped`
## Summary
- start demo/user processes with explicit uid/gid, `/home/user`, and
root processes with `/root`
- enforce kernel DAC checks for host-backed filesystem syscalls,
including create/open, unlink/rename/link/symlink, readlink/realpath,
exec, chmod/chown, access, sticky-directory deletion, and Unix socket
bind
- set created file/dir/socket ownership from the creator's effective
uid/gid
- tighten privilege-sensitive user syscalls (`setresuid`, `setresgid`,
`setgroups`, hard rlimit raises, nice priority raises) and SysV IPC
permissions
- make default `/home/user` scratch mounts owned by uid/gid 1000

## Verification
- `cargo fmt -p wasm-posix-kernel`
- `cargo check -Zbuild-std=core,alloc -p wasm-posix-kernel`
- `cargo build --release -p wasm-posix-kernel -Zbuild-std=core,alloc`
- `npx vitest run test/vfs/default-mounts.test.ts
test/vfs/browser-mount-layering.test.ts --config /dev/null`
- `git diff --check`
- Browser shell demo at `http://127.0.0.1:5175/pages/shell/` confirms
uid 1000 cannot create in `/`, can create under `/home/user`, and cannot
`cd /root`.

Note: the normal host Vitest config still requires `wasm32posix-cc` for
global setup; I used `--config /dev/null` for the VFS-only tests to
avoid unrelated SDK setup.
## Summary
- Make fork-path discovery table-aware for indirect calls, including
active/passive/declared element handling and conservative dynamic table
writes.
- Bound transitive indirect-call closure to two dispatch hops while
still propagating direct callers, preventing dynamic-runtime cascades
from instrumenting unrelated callbacks.
- Add WAT regressions for wrong-table, declared/passive element,
table.init, and over-cascading indirect-call cases; document the
remaining static-analysis limit.

## Root Cause
The QuickJS/Node-shaped build uses one large LLVM function table. The
previous analyzer treated indirect targets too broadly and let
same-table/same-signature indirect closure recurse without a resource
boundary, so generic interpreter dispatchers pulled in unrelated
QuickJS/OpenSSL callbacks. This was an over-broad call-graph discovery
issue, not an npm/demo workaround or a transform correctness change.

## Verification
- `scripts/dev-shell.sh bash -lc 'HOST_TARGET=$(rustc -vV | awk "/^host/
{print \\$2}"); cargo test -p fork-instrument --target "$HOST_TARGET"'`
- Fork-capable QuickJS/Node-shaped discover-only: optimized temp binary
reduced `5516 -> 876`; unoptimized temp binary reduced `7269 -> 978`.
- Unoptimized discover-only no longer pulls OpenSSL-family names: `SSL=0
EVP=0 CRYPTO=0 ossl=0`; still includes `JS_CallInternal`,
`js_call_c_function`, and `js_os_exec`.
- Rebuilt kernel/rootfs/tooling with `bash build.sh` and rebuilt
QuickJS/Node with the final instrumenter; `node.wasm` is `5,387,796`
bytes.
- Host runtime checks using `NodePlatformIO`: `node npm-cli.js
--version` prints `10.9.2`; `require(.../npm/lib/npm.js)` prints
`{"ctor":"function"}`.
- `scripts/dev-shell.sh bash -lc 'cd host && npx vitest run
../tests/package-system/fetch-binaries-allow-stale.test.ts'`

## Notes
- `scripts/test-allow-stale.sh` is stale in this checkout: it expects
`revision = ...` in `packages/registry/bzip2/package.toml`, but that
manifest no longer has the field. It restored the file before exiting;
the maintained package-system artifact policy Vitest above passes.
- The targeted npm Vitest file is not runnable as-is against absolute
host-side npm paths under the default rootfs-backed worker harness in
this checkout; the same npm/module-loading checks pass via the host
filesystem harness.
## Summary
- restore Kandelo live demo panes from VFS demo guide metadata and keep
demo pane state across tab switches
- rebuild demo/runtime config paths for WordPress, MariaDB/LAMP, Node,
and related VFS images
- add generalized fork-instrumentation artifact guards, cargo-backed
instrumenter wrapper, and reject stale Asyncify artifacts
- fix Node/npm browser startup by removing fork-only qjs:os.exec from
node.wasm while keeping standalone qjs fork-capable

## Verification
- `git diff --check`
- `scripts/dev-shell.sh bash -lc 'npx vitest run
host/test/wasm-binary-parse.test.ts -t "built node.wasm artifact
policy"'`\n- `scripts/dev-shell.sh bash -lc 'npx vitest run
packages/registry/quickjs/test/quickjs-node-npm.test.ts -t
"require|process.emit|console|fs/promises|module cache|prints
10.9.2"'`\n- `cd apps/browser-demos && npx playwright test
test/kandelo-node.spec.ts --project=chromium`\n- `scripts/dev-shell.sh
bash -lc 'npm --prefix apps/browser-demos run build'`\n\n## Notes\n-
Follow-up investigation is still needed for the fork-instrumentation
resource-safety issue with broad indirect-call closure in dynamic
runtimes. This PR keeps node.wasm on the no-fork runtime path and
documents the instrumentation policy.
## Summary
- keep web demo presentation on syslog while the HTTP preview is still
starting
- migrate built-in nginx/WordPress demo metadata at runtime so older VFS
images do not briefly focus Terminal
- mark unexpected init exits as errors so the focused surface remains
syslog instead of a stale preview

## Testing
- `git diff --check origin/main..HEAD`
- transpiled changed TS/TSX files with TypeScript (`transpileModule`)
## Summary
- make the canonical `node` package resolve to the SpiderMonkey-backed
Node-compatible runtime while keeping `spidermonkey-node` as an explicit
alias
- route the Kandelo Node.js demo through the SpiderMonkey Node host with
npm, Intl, SharedArrayBuffer, and worker_threads demo actions
- remove the QuickJS registry package/build/tests now that Node
compatibility is backed by SpiderMonkey
- move the shared Node compatibility bootstrap to
`packages/registry/node-compat/` and have SpiderMonkey consume it from
there
- add SpiderMonkey Node compatibility/parity tests, including npm cowsay
install and EMFILE errno coverage

## Testing
- `bash scripts/dev-shell.sh npm --prefix host test --
packages/registry/spidermonkey/test/spidermonkey-node-compat.test.ts
packages/registry/spidermonkey/test/spidermonkey-node-parity.test.ts
packages/registry/spidermonkey/test/spidermonkey.test.ts`
- `bash -n packages/registry/spidermonkey/build-spidermonkey.sh
packages/registry/spidermonkey-node/build-spidermonkey-node.sh run.sh
packages/registry/npm/fetch-npm.sh`
- verified active source has no `quickjs` / `packages/registry/quickjs`
references outside historical docs and ABI metadata
- `build-deps parse node`, `build-deps parse spidermonkey`, `build-deps
parse spidermonkey-node`, and confirmed `build-deps parse quickjs` fails
because the package is gone
- Playwright smoke against
`http://127.0.0.1:5199/pages/kandelo/?demo=node`: demo pane loads; prior
smoke also verified `Install cowsay` installs and runs cowsay

## Notes
- `npm --prefix apps/browser-demos run build` is still blocked locally
by a missing unrelated `nginx-vfs.vfs.zst` binary artifact; Vite dev
compiled and ran the Node demo path successfully.
## Summary

Batch PR collecting the committed changes currently on
`emdash/try-batch-pr-5eqga`.

## Included PRs

- #579
- #576
- #570
- #568
- #560
- #522
- #567

## Changes

- #579: Restore Open POSIX test discovery against the checked-in
`tests/posix/open-posix-testsuite` tree, split shared CI setup into
`test-gate-prepare`, and fan out cargo, fork-instrument, vitest,
libc-test, Open POSIX, and Sortix suites into concurrent test-suite jobs
across staging, prepare-merge, and force-rebuild.
- #576: Fix the host declaration-file typecheck failure by keeping the
wasm ABI parser cursor typed as a concrete number, add a host
`typecheck` script, and run host type checking in the browser demo,
docs, staging, prepare-merge, and force-rebuild workflows.
- #570: Preserve process-group leaders after `waitpid` reaping by
keeping resource-free limbo records until their process groups empty,
and add focused Sortix expectation overrides plus process lifecycle
coverage.
- #568: Fix browser demo deploy fallback paths for LAMP/MariaDB assets,
align MariaDB resolver metadata and diagnostics, and move the deploy
workflow to GitHub's Node 24 action runtime.
- #560: Add SMTP capture support for WordPress/LAMP demos with a new
`msmtpd` package, VFS/dinit wiring, WordPress mail configuration, and
Playwright coverage for captured `.eml` output.
- #522: Add the lazy POSIX utility rootfs package plan and
implementation path: `images/rootfs/PACKAGES.toml`, generated mkrootfs
manifest fragments, lazy URL metadata, rootfs package build support,
resolver fallback handling, shell VFS composition, and lazy exec
materialization in browser/Node hosts.
- #567: Add the Rust-owned host adapter manifest with ABI and host
adapter metadata, generated TypeScript bindings/snapshot data, kernel
wasm manifest exports, worker-side manifest validation, tests, and ABI
documentation updates.

## Testing

Not rerun as a single batch after creating this PR. The individual
commits and linked PRs include their focused validation commands and
notes.
## Summary
- add a prepare-merge merge-mode selector based on the `batched-changes`
label
- use rebase auto-merge for labeled PRs while preserving squash
auto-merge by default
- include the selected merge method in prepare-merge log/comment text

## Tests
- `ruby -e 'require "yaml";
YAML.load_file(".github/workflows/prepare-merge.yml"); puts "yaml
ok"'`\n- `git diff --check -- .github/workflows/prepare-merge.yml`
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.

2 participants