Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c4d58e9
feat: US-057 - Implement WHATWG encoding and EventTarget parity
NathanFlurry Mar 26, 2026
b4e8043
feat: US-058 - Implement stream/web, WHATWG Web Streams adapters, and…
NathanFlurry Mar 26, 2026
bd147a7
feat: US-059 - Implement http2 respondWithFile/respondWithFD/FileHand…
NathanFlurry Mar 26, 2026
fc34e63
feat: US-032 - Fix cross-runtime kernel network integration regressions
NathanFlurry Mar 26, 2026
696e5b8
feat: US-033 - Enforce deny-by-default kernel network permissions
NathanFlurry Mar 26, 2026
3416273
feat: US-034 - Eliminate remaining Node bridge networking bypasses
NathanFlurry Mar 26, 2026
e7d03ca
feat: US-035 - Respect omitted network capability in standalone Node …
NathanFlurry Mar 26, 2026
3452450
feat: US-036 - Replace regex-based Node loader source transforms
NathanFlurry Mar 26, 2026
c4ed0c5
feat: US-037 - Implement full WasmVM sigaction semantics
NathanFlurry Mar 26, 2026
bd4c233
feat: US-038 - Validate socket ownership against the kernel process t…
NathanFlurry Mar 26, 2026
48ad8cd
feat: US-039 - Correct vacuous pass accounting in Node conformance ex…
NathanFlurry Mar 26, 2026
640d93a
feat: [US-040] - Add hard CI guards for story-critical Wasm C artifacts
NathanFlurry Mar 26, 2026
21b4f22
feat: US-041 - Strengthen kernel networking tests against overfitting
NathanFlurry Mar 26, 2026
5ed58a6
feat: US-042 - Retarget kernel-consolidation verification suites away…
NathanFlurry Mar 26, 2026
bea24bd
feat: US-043 - Implement VFS correctness primitives for POSIX file se…
NathanFlurry Mar 26, 2026
d9320b5
feat: US-044 - Add unified blocking I/O wait primitives to the JS kernel
NathanFlurry Mar 26, 2026
fe72520
feat: US-045 - Implement deferred unlink with inode-backed open-file …
NathanFlurry Mar 26, 2026
8442b5b
feat: US-046 - Implement signal handler registry and signal masking i…
NathanFlurry Mar 26, 2026
95ee8cb
feat: US-047 - Build TCP server socket lifecycle in the JS kernel
NathanFlurry Mar 26, 2026
db873bc
feat: [US-048] - [Implement AF_UNIX stream and datagram sockets in-ke…
NathanFlurry Mar 26, 2026
21f1a26
feat: US-049 - Add UDP transport support to the JS kernel
NathanFlurry Mar 26, 2026
70bc0c0
feat: US-050 - Implement socket options and per-call send/recv flags
NathanFlurry Mar 26, 2026
0337047
feat: US-051 - Populate /proc entries for process and FD introspection
NathanFlurry Mar 26, 2026
1f896ad
feat: US-060 - Reclassify remaining Node conformance failures by impl…
NathanFlurry Mar 26, 2026
17bf666
feat: US-061 - Investigate Pi programmatic SDK path with real-provide…
NathanFlurry Mar 26, 2026
9032bf5
feat: US-067 - Fix Pi SDK import in NodeRuntime when Pi modules decla…
NathanFlurry Mar 26, 2026
50111c1
feat: US-068 - Fix Pi package asset discovery after SDK import bootstrap
NathanFlurry Mar 26, 2026
5c81981
feat: US-062 - Make Pi headless mode pass end-to-end with real-provid…
NathanFlurry Mar 26, 2026
7f41564
feat: US-069 - Complete modern Web API bootstrap for Pi PTY's undici …
NathanFlurry Mar 26, 2026
cf1561b
feat: US-063 - Make Pi PTY mode pass end-to-end with real-provider to…
NathanFlurry Mar 27, 2026
1639ca8
feat: US-070 - Make Pi PTY helper-tool bootstrap compatible with sand…
NathanFlurry Mar 27, 2026
2d22916
feat: US-064 - Investigate OpenCode SDK/server path with real-provide…
NathanFlurry Mar 27, 2026
859ff22
feat: US-071 - Fix sandbox child_process bridge for mounted host-bina…
NathanFlurry Mar 27, 2026
d9dfce4
feat: US-065 - Make OpenCode headless mode pass end-to-end with real-…
NathanFlurry Mar 27, 2026
0217d0f
feat: update quickstart examples to use ESM and NodeRuntime API
NathanFlurry Mar 27, 2026
e160a13
fix: quickstart examples need useDefaultNetwork and ESM filePath
NathanFlurry Mar 27, 2026
1a2d787
feat: update all doc examples to use ESM instead of CJS
NathanFlurry Mar 27, 2026
cf1f20a
fix: add dev shell and pi sdk regressions
NathanFlurry Mar 27, 2026
931f60f
chore: ignore pi session artifacts
NathanFlurry Mar 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .agent/contracts/compatibility-governance.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,32 @@ Changes affecting bridged or polyfilled Node APIs MUST keep `docs/nodejs-compati
- **WHEN** `docs/nodejs-compatibility.mdx` is updated
- **THEN** the page MUST retain an explicit target Node version statement at the top

### Requirement: Node Conformance Vacuous Self-Skips Must Not Inflate Genuine Pass Counts
Node conformance expectation and reporting flows SHALL reserve `category: "vacuous-skip"` for expected-pass vendored tests that exit `0` only because the test self-skipped without exercising functionality.

#### Scenario: Self-skipping vendored file is treated as vacuous pass
- **WHEN** an expectation is marked `expected: "pass"` only because the vendored test self-skips
- **THEN** it MUST use `category: "vacuous-skip"` and reporting MUST exclude it from the genuine-pass count

#### Scenario: Intentionally skipped file is still a skip
- **WHEN** secure-exec keeps a vendored file under `expected: "skip"` because functionality remains broken or intentionally unsupported
- **THEN** that entry MUST stay under its real failure category rather than `vacuous-skip`

### Requirement: Node Conformance Non-Pass Expectations Must Be Classified By Implementation Intent
Node conformance expectation and reporting flows SHALL classify every non-passing vendored test into exactly one implementation-intent bucket: `implementable`, `will-not-implement`, or `cannot-implement`.

#### Scenario: Remaining non-pass inventory is reported
- **WHEN** expectations or the generated conformance report are updated
- **THEN** the maintained conformance artifacts MUST expose the remaining non-pass counts grouped by implementation intent alongside the existing failure-category breakdown

#### Scenario: Non-pass expectation is categorized
- **WHEN** an expectation remains `expected: "fail"` or `expected: "skip"`
- **THEN** it MUST resolve to exactly one implementation-intent bucket using a specific, verifiable reason that distinguishes policy/out-of-scope exclusions from fundamental architectural blockers

#### Scenario: Conformance target is communicated
- **WHEN** the generated Node conformance report is regenerated
- **THEN** it MUST state that the tracked completion target is 100% of the `implementable` bucket rather than 100% of the upstream vendored suite

### Requirement: Node Compatibility Target Version Tracks Test Type Baseline
The runtime compatibility target MUST align with the `@types/node` package major version used to validate secure-exec tests and type checks. Compatibility documentation and spec references MUST describe the same target major Node line.

Expand Down Expand Up @@ -106,6 +132,17 @@ Fixture dependency installation SHALL be cached across repeated test invocations
- **WHEN** fixture files or cache key factors change
- **THEN** the matrix MUST prepare a new cache entry and reinstall dependencies before execution

### Requirement: Kernel-Consolidation Proof Must Use Kernel-Mounted Verification
Stories or docs that claim kernel-consolidation networking behavior is complete SHALL distinguish kernel-mounted proof from compatibility coverage for the retained legacy adapter path.

#### Scenario: Verification targets a retained legacy adapter path
- **WHEN** a test instantiates `createDefaultNetworkAdapter()` or `useDefaultNetwork`
- **THEN** that test MUST be treated as compatibility coverage for the standalone legacy path rather than as proof that kernel-consolidation work is complete

#### Scenario: Verification is used as evidence for kernel-consolidation networking
- **WHEN** a test or document is cited as proof that kernel-backed Node networking works
- **THEN** it MUST execute through `createNodeRuntime()` mounted into a real `Kernel` or an equivalent kernel-mediated path that exercises the shared socket table and host-adapter delegation

### Requirement: Parity Mismatches Remain Failing Until Resolved
Compatibility project-matrix policy SHALL NOT include a "known mismatch" or equivalent pass-through state for parity failures.

Expand Down
117 changes: 115 additions & 2 deletions .agent/contracts/kernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ The kernel VFS SHALL provide a POSIX-like filesystem interface with consistent e
- **WHEN** two directory entries refer to the same file through `link(oldPath, newPath)`
- **THEN** `stat(oldPath).ino` and `stat(newPath).ino` MUST be identical until the inode is deleted

#### Scenario: directory nlink reflects self, parent, and child directories
- **WHEN** the InMemoryFileSystem creates or removes directories
- **THEN** each directory MUST report POSIX-style `nlink` metadata: `2` for an empty directory, `2 + childDirectoryCount` for non-root directories, and root `nlink` MUST increase for each immediate child directory

#### Scenario: readDirWithTypes returns entries with type information
- **WHEN** a caller invokes `readDirWithTypes(path)` on a directory containing files and subdirectories
- **THEN** the VFS MUST return `VirtualDirEntry[]` where each entry has `name`, `isDirectory`, and `isSymbolicLink` fields
Expand Down Expand Up @@ -111,6 +115,10 @@ The kernel FD table SHALL manage per-process file descriptor allocation with ref
- **WHEN** a process duplicates an FD via `fdDup(pid, fd)`
- **THEN** a new FD MUST be allocated pointing to the same FileDescription, and the FileDescription's `refCount` MUST be incremented

#### Scenario: Duplicated FDs keep deferred-unlink inode data until the last shared close
- **WHEN** a file's pathname is unlinked after `dup`, `dup2`, or fork creates additional FDs that share the same FileDescription
- **THEN** the inode-backed data MUST remain accessible through the remaining shared FD references and MUST be released only when that shared FileDescription's final reference closes

#### Scenario: Dup2 redirects target FD to source FileDescription
- **WHEN** a process invokes `fdDup2(pid, oldFd, newFd)` and `newFd` is already open
- **THEN** `newFd` MUST be closed first, then reassigned to share `oldFd`'s FileDescription with `refCount` incremented
Expand Down Expand Up @@ -177,14 +185,29 @@ The kernel process table SHALL manage process lifecycle with atomic PID allocati
- **WHEN** a caller invokes `waitpid(pid)` on a process that has already exited
- **THEN** the Promise MUST resolve immediately with the recorded exit status

#### Scenario: kill sends signal to running process via driver
- **WHEN** a caller invokes `kill(pid, signal)` on a running process
#### Scenario: kill routes default-action signals to the driver
- **WHEN** a caller invokes `kill(pid, signal)` on a running process and the delivered disposition resolves to `SIG_DFL`
- **THEN** the kernel MUST route the signal through `driverProcess.kill(signal)` on the process's DriverProcess handle

#### Scenario: kill on exited process is a no-op or throws
- **WHEN** a caller invokes `kill(pid, signal)` on a process with `status: "exited"`
- **THEN** the kernel MUST NOT attempt to deliver the signal to the driver

### Requirement: Process Signal Handlers And Pending Delivery
The kernel process table SHALL preserve per-process signal dispositions, blocked masks, and pending caught-signal delivery state.

#### Scenario: caught signal handler runs instead of the default driver action
- **WHEN** a running process has a registered caught disposition for a delivered signal
- **THEN** the kernel MUST invoke that handler and MUST NOT route the signal through `driverProcess.kill(signal)` unless a later delivery falls back to `SIG_DFL`

#### Scenario: blocked caught signals remain pending until unmasked
- **WHEN** `sigprocmask()` blocks a delivered signal for a running process
- **THEN** the kernel MUST queue that signal in the process's pending set instead of dispatching it immediately

#### Scenario: unmasking delivers queued pending signals
- **WHEN** `sigprocmask()` later unblocks one or more queued pending signals
- **THEN** the kernel MUST dispatch those pending signals immediately in ascending signal-number order, skipping any that remain blocked

#### Scenario: Zombie processes are cleaned up after TTL
- **WHEN** a process exits and transitions to zombie state
- **THEN** the process entry MUST be cleaned up (removed from the table) after a bounded TTL (60 seconds)
Expand Down Expand Up @@ -345,9 +368,20 @@ The kernel pipe manager SHALL provide buffered unidirectional pipes with blockin
- **WHEN** `createPipeFDs(fdTable)` is invoked
- **THEN** the pipe manager MUST create a pipe and install both read and write FileDescriptions as FDs in the specified FD table, returning `{ readFd, writeFd }`

### Requirement: FD Poll Waits Support Indefinite Blocking
The kernel SHALL expose `fdPollWait` readiness waits that can either time out or remain pending until an FD state change occurs.

#### Scenario: poll timeout -1 waits until FD readiness changes
- **WHEN** a runtime calls `fdPollWait(pid, fd, -1)` for a pipe or other waitable FD that is not yet ready
- **THEN** the wait MUST remain pending until that FD becomes readable, writable, or hung up, rather than timing out because of an internal guard interval

### Requirement: Socket Blocking Waits Respect Signal Handlers
The kernel socket table SHALL allow blocking accept/recv waits to observe delivered signals so POSIX-style syscall interruption semantics can be enforced.

#### Scenario: sigaction registration preserves mask and flags
- **WHEN** a runtime registers a caught signal disposition with a signal mask and `SA_*` flags
- **THEN** the kernel MUST retain the handler, blocked-signal mask, and raw flag bits so later delivery and wait-restart behavior observes the same metadata

#### Scenario: SA_RESETHAND resets a caught handler after first delivery
- **WHEN** a process delivers a caught signal whose registered handler includes `SA_RESETHAND`
- **THEN** the kernel MUST invoke that handler once and reset the disposition to `SIG_DFL` before any subsequent delivery of the same signal
Expand Down Expand Up @@ -390,6 +424,74 @@ The kernel socket table SHALL reserve listener ports deterministically for loopb
- **WHEN** a loopback `connect()` targets a listening socket whose pending backlog already reached the configured `listen(backlog)` capacity
- **THEN** the connection MUST fail with `ECONNREFUSED` instead of growing the backlog without bound

#### Scenario: listening socket becomes readable while accept backlog is non-empty
- **WHEN** one or more pending connections are queued for a listening socket
- **THEN** `socketTable.poll()` for that listener MUST report `readable: true` until `accept()` drains the backlog

#### Scenario: closing a listener tears down queued unaccepted connections
- **WHEN** a listening socket is closed while its accept backlog still contains pending server-side sockets
- **THEN** the kernel MUST close those queued sockets as part of listener teardown so detached connections do not remain reachable without a listener owner

### Requirement: Socket Options And Per-call Flags Preserve Kernel And Host Semantics
The kernel socket table SHALL track socket options per socket, apply kernel-enforced options during bind/send paths, and preserve per-call read flag semantics across supported socket types.

#### Scenario: getsockopt returns values previously stored by setsockopt
- **WHEN** a caller sets `SO_REUSEADDR`, `SO_KEEPALIVE`, `SO_RCVBUF`, `SO_SNDBUF`, or `TCP_NODELAY` on a kernel socket
- **THEN** `getsockopt` MUST return the last value written for that `(level, optname)` pair on that socket

#### Scenario: SO_REUSEADDR changes bind conflict behavior
- **WHEN** a caller binds an internet-domain socket to an already-used local port after setting `SO_REUSEADDR` on the binding socket
- **THEN** the kernel MUST allow that bind instead of rejecting it with `EADDRINUSE`

#### Scenario: host-backed TCP sockets replay stored options after connect
- **WHEN** a socket with previously stored `TCP_NODELAY` or `SO_KEEPALIVE` becomes backed by a host TCP connection
- **THEN** the kernel MUST replay those options onto the host socket
- **AND** later `setsockopt` updates on that connected host-backed socket MUST be forwarded immediately

#### Scenario: MSG_PEEK returns data without consuming it
- **WHEN** `recv()` or `recvFrom()` is called with `MSG_PEEK` and data is queued
- **THEN** the kernel MUST return the readable bytes without removing them from the socket buffer or datagram queue

#### Scenario: MSG_DONTWAIT returns EAGAIN only for empty non-EOF reads
- **WHEN** `recv()` or `recvFrom()` is called with `MSG_DONTWAIT` and no readable data is available yet
- **THEN** the kernel MUST fail immediately with `EAGAIN`
- **AND** if EOF is already known for that read path it MUST still return `null` instead of `EAGAIN`

### Requirement: UDP Datagram Transport Preserves Message Boundaries And Source Addresses
The kernel socket table SHALL model UDP as connectionless datagram delivery, preserving one-send-to-one-recv boundaries and reporting the sender address for each datagram.

#### Scenario: sendTo delivers one datagram to recvFrom with source address metadata
- **WHEN** a bound UDP socket calls `sendTo()` to another kernel-bound UDP socket
- **THEN** the destination socket MUST queue exactly one datagram for `recvFrom()`
- **AND** `recvFrom()` MUST return the sender's address in `srcAddr`

#### Scenario: recvFrom truncates oversized datagrams without leaking the remainder
- **WHEN** a queued UDP datagram exceeds the caller's `maxBytes`
- **THEN** `recvFrom()` MUST return only the leading `maxBytes`
- **AND** the excess bytes MUST be discarded instead of surfacing as a second datagram

#### Scenario: unbound UDP destinations drop silently
- **WHEN** `sendTo()` targets an address with no kernel-bound UDP socket and no host-backed UDP route
- **THEN** the kernel MUST report the datagram length as written
- **AND** the datagram MUST be dropped silently instead of raising `ECONNREFUSED`

#### Scenario: host-backed UDP sockets route through the host adapter
- **WHEN** a bound UDP socket is attached to an external host-backed transport and sends or receives external datagrams
- **THEN** the kernel MUST delegate outbound sends through the configured host adapter
- **AND** inbound host datagrams MUST be surfaced via `recvFrom()` with the host sender address preserved

### Requirement: Kernel Socket Ownership Matches the Process Table
The kernel socket table SHALL only allocate process-owned sockets for PIDs that are currently registered in the kernel process table when the table is kernel-mediated.

#### Scenario: create rejects unknown owner PID in kernel mode
- **WHEN** `createKernel()` provisions the shared `SocketTable` and a caller attempts `socketTable.create(..., pid)` for a PID that is not present in the process table
- **THEN** socket creation MUST fail with `ESRCH`

#### Scenario: process exit cleanup closes only that PID's sockets
- **WHEN** a registered process exits and the kernel runs process-exit cleanup
- **THEN** the socket table MUST close all sockets owned by that PID
- **AND** sockets owned by other still-registered PIDs MUST remain available

### Requirement: Command Registry Resolution and /bin Population
The kernel command registry SHALL map command names to runtime drivers and populate `/bin` stubs for shell PATH-based resolution.

Expand Down Expand Up @@ -440,6 +542,17 @@ The kernel permission system SHALL wrap VFS and environment access with deny-by-
- **WHEN** network or child-process permission checks are configured
- **THEN** operations without explicit allowance MUST be denied, consistent with the fs permission model

#### Scenario: Kernel-created socket tables inherit deny-by-default network enforcement
- **WHEN** `createKernel({ permissions })` constructs the shared `SocketTable`
- **THEN** the socket table MUST enforce `permissions.network` for host-visible `listen`, external `connect`, external `send`, host-backed UDP `sendTo`, and host-backed listen/bind operations
- **AND** when `permissions.network` is missing those external socket operations MUST fail with `EACCES`
- **AND** loopback routing to kernel-owned listeners MUST remain allowed without a host-network allow rule

#### Scenario: AF_UNIX sockets bypass host-network permission checks
- **WHEN** a caller binds, listens on, connects to, or sends through an `AF_UNIX` socket path
- **THEN** the kernel MUST keep that traffic entirely in-kernel without consulting `permissions.network`
- **AND** missing Unix listeners MUST fail with `ECONNREFUSED` instead of `EACCES`

#### Scenario: Preset allowAll grants all operations
- **WHEN** `allowAll` permission preset is used
- **THEN** all filesystem, network, child-process, and env operations MUST be allowed
Loading
Loading