# Compatibility implementation plan

## Decisions and evidence

Keep the owned JavaScript runtime and CPython WebAssembly distribution. No
Nodepod, Pyodide, v86, or replacement VM is required for the first milestones.
The timeout, missing Node APIs and Python syscall deadlock are separate issues.

The extension acceptance suite serves local wheels using node:http. In the
restricted development environment listen(127.0.0.1) fails with EPERM. Its
setup promise previously had no error handler, so setup waited for the hook
timeout. The test now rejects immediately and teardown handles failed startup.
With loopback listening permitted, all 12 extension acceptance tests passed in
34.19 seconds of test execution (37.26 seconds total). This includes C, Cython,
Rust/PyO3, Meson and Pydantic's compiled core. This establishes the Node-hosted
fixture path, not browser acceptance or arbitrary PyPI compatibility.

The Python build uses PROXY_TO_PTHREAD. backend.ts sets SBX_SERIAL_HOST_CALLS
and refuses asyncio.to_thread because proxied blocking syscalls serialize the
filesystem-owning thread. Removing the refusal alone is not a fix.

## Alternatives

| Approach | Useful for | Main constraint | Decision |
| --- | --- | --- | --- |
| Owned JavaScript builtins | Node utility and lifecycle APIs | Must reproduce observable semantics | First choice |
| Compile source to Wasm | C/C++/Rust extensions and tools | ABI, dependencies and host calls must match | Preferred native-code route |
| JSPI suspension | Freeing the worker event loop during a blocking host call | Runtime support, suspension boundaries, dynamic modules and pthread interaction | Prototype before adopting |
| Asyncify | Suspension where JSPI is unavailable | Instrumentation cost and dynamic-module compatibility | Compare as fallback |
| Per-thread syscall channels | Concurrent Python I/O | Must also remove synchronous proxy bottleneck and preserve fd semantics | Prototype alongside suspension |
| WasmFS integration | Moving filesystem work away from JS-only ownership | Existing virtual FS needs a backend; not a flag-only change | Assess with thread prototype |
| Host-native executables | Compatible tools on Node servers | Host OS/architecture, process permissions, unavailable in browsers | Optional explicit host capability |
| Owned instruction interpreter | Selected binaries without source | ISA, loader, syscalls, linking, signals and threads | Defer until a concrete binary needs it |
| Binary-to-Wasm translation | Repeated execution of supported binaries | Same ABI needs plus translation correctness and cache invalidation | Later, reuse existing backend interface |

## Ordered implementation milestones

### 1. Make extension acceptance trustworthy

Run the complete local wheel suite with working loopback networking. Separate
setup, download, install, link, import and execution failures. Fix each observed
failure with a focused regression. Preserve failures; do not raise timeouts to
make deadlocks disappear. Repeat in the browser using static fixture hosting.

Exit: ordinary imports execute compiled C/C++/Rust fixtures; missing/incompatible
artifacts produce actionable failures, and shutdown releases all resources.

### 2. Replace Node stubs in dependency order

Inventory actual package imports. Start with diagnostics_channel and utility
APIs, followed by worker_threads backed by guest workers, then virtual network
APIs. Separate virtual TCP from external browser networking. cluster/domain,
http2, tls, inspector, v8 and vm each need an explicit supported surface rather
than a catch-all function that pretends to implement them. A JS vm substitute
must not claim a security boundary it does not provide.

Exit per module: native-Node differential tests, error/cancellation tests,
cross-guest isolation, and a real package that consumes that API in Node and
browser hosts. Mark unsupported operations explicitly.

### 3. Prove concurrent Python I/O before rebuilding production

Build a minimal C/pthread fixture against the same Emscripten toolchain. One
thread blocks on a host read while another writes; add cancellation and a
timer on the owning JS worker. Compare an asynchronous proxy/suspension path
with unproxied per-thread host calls. Channels alone cannot repair serialized
proxy dispatch. Feature-detect JSPI; test fallback behavior explicitly.

Measure idle CPU, wakeup latency, memory, download size and throughput for each
variant. Select only after the fixture passes. Then rebuild CPython and all
affected extension fixtures, preserving one shared descriptor authority.

Exit: asyncio.to_thread, run_in_executor, concurrent socket read/write,
cancellation, subprocess interaction and dynamic extensions work together.
Remove the current guard only after these pass in supported browsers and Node.

### 4. Expand portable binaries

Use source builds targeting the existing Wasm ABI first. Track toolchain and
ABI fingerprints in artifacts. If an essential source-unavailable binary
remains, define one ISA and a static executable subset for an owned interpreter;
reject unsupported instructions and syscalls deterministically. Bound memory,
execution and cancellation. Do not start with dynamic Linux package parity.

Exit: selected real binaries have reproducible correctness tests and measured
startup, memory and throughput. Translation caching follows correctness.

## Sources for the suspension prototypes

- Emscripten pthread proxying and blocking rules:
  https://emscripten.org/docs/porting/pthreads.html
- Emscripten Asyncify and JSPI integration:
  https://emscripten.org/docs/porting/asyncify.html
- V8 JSPI boundary model:
  https://v8.dev/blog/jspi-newapi

These sources describe mechanisms; they do not establish that this CPython
image or its extensions already support them. The prototypes are required.
