Archive — history, not state. Kept for its reasoning and its evidence; its plan is closed.

Shop actor and native-runtime split handoff

Status: merge checkpoint prepared 2026-07-15.

This note connects two concurrent changes that must land together conceptually:

  1. splitting the monolithic shop actor into smaller service actors; and
  2. splitting the runtime’s globally linked C/module fleet into package-owned native providers selected by executable dependency closure.

The first change improves ownership, failure isolation, and service authority. It does not by itself materially reduce a process image or startup memory. That reduction only appears when each new actor has its own package closure and the target build stops linking every native extension and endowment merely because the target can support it.

What this checkpoint implements

This checkpoint is the follow-on to the standard-library federation commit. It finishes three capability verticals that the shop split can consume directly.

Process

  • pit-process owns the public process and capture APIs.
  • Darwin and Linux adapt $posix_spawn; Windows adapts $create_process_w.
  • Web, Playdate, and Apple embedded targets have no process arm. Absence is by omission, not an unavailable stub.
  • Git, the linker, and desktop native builds declare pit-process explicitly.
  • The shop selects native_build_desktop only for Darwin/Linux/Windows and native_build_none for targets that cannot run a local compiler toolchain.

Fetch

  • fetch is independent of the portable HTTP experiment and no longer pulls net into the shop transitively.
  • Target arms adapt the actual host facilities: $nsurl_session, $libcurl, $winhttp, $browser_fetch, and $playdate_http.
  • Linux resolves libcurl dynamically. If a suitable thread-safe libcurl cannot be realized, the endowment is absent; there is no hidden HTTP fallback.

Watch

  • pit-watch/watch selects Darwin, Linux, and Windows adapters.
  • Raw providers expose facility-shaped events from $fsevents, $inotify, and $read_directory_changes_w.
  • Recursive traversal, rename pairing, portable action names, subscription management, and cancellation live in Pit.
  • Unsupported targets have no watch arm.

The committed boot fleet is regenerated so those package and target selections are present in the Darwin, Linux, Windows, Web, and Playdate firmware graphs.

What the shop-actor split should preserve

Each new shop service actor should have a deliberately small program root and manifest. Do not replace one monolithic actor with several actors that all load the old shop closure.

Suggested ownership:

ServicePackages/capabilities it should own
source/package servicefile, archive/QOP, fetch, Git where remote source actually needs it
store/realization servicefile, archive/QOP, crypto/signature verification
build servicecompiler, linker, Cake, process on desktop only
target/bundle servicecompiler/linker target metadata, archive/QOP; no process on sealed targets
logger/profiler serviceshop logging plus privileged runtime/VM observation capabilities

Services should exchange records through actor messages. They should not all import core/internal/pitfs, os, mach, nota, wota, crypto, and shop_paths merely because the old shop actor did. Move shop-owned helpers under pit-shop/internal, and give other facilities declared public seams in their owning packages. This is also the prerequisite for enforcing same-package-only internal/ resolution.

The remaining image-size blocker

cake/cake.ce still has three broad global lists:

  • base_runtime() includes support libraries that are not all runtime substrate (monocypher, wildmatch, full miniz, cJSON, and the unused wota_json adapter);
  • qbe_runtime() links the compiler backend into ordinary desktop runtimes;
  • extension_files() registers the complete native module fleet and then appends every target endowment.

Therefore package federation is currently a correct authority/dependency model, but it is not yet a native link-closure model. The next build step is to derive native extensions, support sources, external libraries/frameworks, and target endowments from the realized executable claims. A shop process should only carry QBE, Git, WebGPU, WinHTTP, libcurl, watching, or process support when one of its resident program roots actually closes over that facility.

For memory work, measure all of the following after each split:

  • modules and native providers in each executable manifest;
  • linked image size and mapped external libraries/frameworks;
  • boot QOP/firmware size;
  • runtime allocator and per-actor heap footprint.

Actor count alone is not evidence of a memory reduction and can initially add mailboxes, stacks, and actor contexts.

Native work ready to continue

Safe removals

  • source/wota_json.c and .h are an unused JSON-to-WOTA adapter.
  • internal/libgit2.c has no current Pit consumer; Git uses pit-process.
  • debug/debug.c and debug/runtime.c are obsolete stub surfaces. Their two working limit setters should not preserve those modules.
  • The importable internal/kim.c module is test-only. Its KIM primitive must remain private to NOTA until NOTA is rewritten.
  • Test-only native modules should be selected only by test build roots.

Runtime and VM observation

The useful implementation currently sits mostly on the ambient actor module: VM counters, actor snapshots, heap profiles, buddy allocator state, value footprints, stack capture, and profiling controls. Host memory facts sit in sysinfo.

Consolidate the read-only runtime-owned information into two privileged endowments:

  • $pit_runtime: actor/scheduler snapshots, allocator state, aggregate GC and runtime memory information;
  • $pit_vm: VM counters, stacks, heap profiles, value footprints, opcode counts, function information, and disassembly.

Keep mutation and policy separate. Heap/stack limits, closure mutation, tracing, and profiler control are stronger authorities and should remain boot policy or later become separate control endowments.

Host/process memory is not intrinsically uniform. A Pit observation package should combine the runtime endowments with actual platform facilities such as Mach task/host statistics, Linux proc/sysinfo, Windows process/global memory, or WebAssembly memory. Missing target facts are omitted.

WebGPU

WebGPU is used and must not be deleted. Move wgpu/gpu.c into an explicit WebGPU package rather than registering it in every runtime.

Keep device/resource APIs separate from surface integration so headless GPU work does not require a window. Desktop code claims $wgpu_native; Web claims the actual browser/Emscripten WebGPU facility. Target surface adapters combine that GPU capability with an actor-owned window capability:

  • AppKit window to CAMetalLayer;
  • Win32 window to HWND/HINSTANCE;
  • X11 or Wayland window to its matching surface descriptor;
  • Web window to an HTML canvas selector.

Native shims must continue validating the owning actor and unwrapping opaque handles internally. Do not expose native pointers as Pit numbers.

cJSON

cJSON now has two real consumers. Web argument JSON can be passed as text to a Pit actor and decoded with use('json'). POSIX daemon control JSON runs partly before Pit startup, so it cannot simply import a Pit module. Retire it by moving post-connection session semantics into Pit and using a minimal length-framed WOTA bootstrap protocol for the irreducible C launcher/client.

NOTA and WOTA

Do not remove NOTA as dead code. Courier/SEIF uses NOTA for cross-runtime wire envelopes and then converts received messages to WOTA for the local actor mailbox. NOTA also encodes signed bundle manifests. WOTA remains the same-runtime/boot encoding.

NOTA can leave the primary binary as a declared codec package. A later pure-Pit implementation is plausible, but it must preserve private actor-value serialization, record traversal, cycles, replacer/reviver hooks, and binary compatibility. Define the narrow runtime seam for private actor data before removing the C codec.

Boot-only seams

  • Keep QOP reading and a minimal inflater in the runtime while boot members are compressed; move full deflate/miniz functionality to archive/compression.
  • Keep WOTA wire primitives required by actors and boot; move the importable codec facade to an owning package.
  • Keep Mach loading/execution in the runtime; move QBE generation and the importable compiler facade to compiler/linker ownership.
  • Replace importable bootgate and shop_paths modules with private runtime boot operations/configuration delivery.

Verification for this checkpoint

  • make seed completes and regenerates all committed target firmware arms.
  • Focused process, fetch, watch smoke, and watch cancellation contracts pass.
  • The default suite passes: 1,953 passed and 0 failed.
  • ./pit test network passes the HTTPBingo fetch case, but the independent pit-http HTTP/2 case times out after connecting to nghttp2.org. The same endpoint responds over HTTP/2 with curl, and this checkpoint does not modify pit-http, pit-tls, pit-net, or the HTTP/2 test. Treat that integration failure as a visible follow-up rather than as fetch coverage for this split.
  • Native Linux libcurl/inotify execution and native Windows WinHTTP/ReadDirectoryChangesW execution remain target CI gates; their target firmware graphs compile during the fleet rebuild.

Source: plans/archive/perf-2026-07/shop-actor-native-split-handoff-2026-07.md