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

Worklist — endowment, gate, and boot model

Generated 2026-07-24 from a design review of docs/ against John’s rulings. This is a work ledger, not target design: the target lives in docs/. Every row cites both sides.

The rulings this implements, in one paragraph: an endowment is a name a program requests that the shop may deny — nothing about power level, $self and $cores qualify. Globals are the same delivery path without the deniability. A granted name arrives in exactly two ways — a constant the shop knows, or the return value of a unit in the realization — and the runtime holds no recipes. Compilation is the gate: built here means it runs, nothing is checked at start, and the boot cart is trusted by ruling. A mount is the trust decision. Minimal C is a prime edict: C is the audit surface, Pit is safe by construction.


Applicability: this ledger was written against 9466791a, we build from a3184e5e

The worklist below was drawn up against the 47k-line tip. This branch starts from the last green commit instead, so a good part of it describes code that was never written here. Surveyed on the base:

SectionOn the green baseMeaning
A — start-time ceremonyabsent. engine_lite.cm is 1766 lines with zero run_target_realization / exact_start_record / start_refusenothing to delete; simply never write it
B1 — three engine-value name tablesone deleted; the other two are not the same table. plan_engine_values (C) existed only to compare its ids against the ids ENGINE_VALUES baked, so both id halves are gone. What is left is a membership SET in cement.cm (build time: is this claim the engine’s?) and engine_value() in engine_lite.cm (run time: what is it?) — two halves of one fact either side of the build/run line, not a duplicatedone as far as it goes; the rest dissolves with the executable-as-array
B2-B4 — uniform endowment deliveryforward workdesign lands first
C — explicit realizationno realization exists yetforward work
D1 — cart_v2_boot.cabsentnever write it; read carts in Pit
D2 — source/start_plan.cpresentblocked on the new boot design
E — boot actorforward work
F — split engine_litepresent, 1766 linesactionable
G — interningpresent (intern_table, pit_intern_text, pit_record_shape_intern)actionable
K — shop modespresentactionable
L — hardcoded targetsappliesactionable
M — engine mcode in the cartneeds the new cartforward

Two consequences worth stating plainly. First, section A evaporates — the single largest cleanup on the list is work we now simply decline to do. Second, most of what remains is forward work rather than deletion, so the ledger’s framing (“delete X at file:line”) only fits rows G, K, L, and B1; everywhere else the line references point into a commit this branch does not contain and must be re-derived before use.

source/cache_profile_test.c, which proved caches-off correctness, also came with the 47k. Until it is rewritten, make check (vm suite, language suite, baseline, fuzz) is the gate for row G.


A. Delete the start-time authorization ceremony — N/A on this branch

The runner has exactly two callers — the shop, which holds the decision, and the C floor at cold boot, which mounts a medium trusted by ruling. Re-deriving the decision proves nothing (the checking code arrives in the same cart) and costs every actor start.

#DoEvidence
A1Delete packet.claims from the start packet and its decode. Decoded, hash-verified, never read.internal/engine_lite.cm:1914; produced at pit-shop/bootstrap_realizer.cm:574
A2Delete packet.policy_program and packet.policy_request. policy_program is decoded and never read; policy_request is read only to learn what schema to check policy_program against — a closed loop.internal/engine_lite.cm:1921, :1988-1991; pit-shop/bootstrap_realizer.cm:586-592
A3Delete packet.receipt and the whole pit.policy.receipt@1 artifact. The shop is the authority and it is in-process.pit-shop/bootstrap_realizer.cm:593; docs/spec/artifact-formats.md:782-830
A4Drop exact_start_record/exact_start_value re-hashing. The shop just built these; blake2 + JSON-decode per artifact per start buys nothing.internal/engine_lite.cm:1880-1907
A5Stop shipping canonical JSON bytes for every artifact in the packet — they exist only so the runner can re-hash.pit-shop/bootstrap_realizer.cm:571-601
A6Remove every start_refuse path. The runner makes no decisions; a failed primitive is a disruption (broken invariant), not a refusal (a decision). ~20 call sites.internal/engine_lite.cm:1865, callers throughout run_target_realization
A7Delete the private-binding guards. Naming is not a privilege; the actor is the trust domain. Includes the hardcoded package-locator list.internal/engine_lite.cm:1596-1606, :2231-2245

B. Uniform endowment delivery

No recipes in the runtime. Two cases only: a constant, or a unit’s return value.

#DoEvidence
B1Delete the engine-value name table. It exists in three places with a hand-maintained “keep BOTH in sync” comment.internal/engine_lite.cm:1570-1593; source/start_plan.c:71-98; pit-shop/cement.cm:80-83
B2Convert the built-in constructors — $clock, $delay, $start, $stop, $couple, $unneeded, $connection, $contact, $portal — into ordinary Pit modules claiming raw runtime primitives.internal/engine_lite.cm:1608-1739
B3Make log and send ordinary modules too, so the system is modifiable by swapping a module rather than editing the runtime.internal/engine_lite.cm:350-553, :708-913
B4Generalize the provider-adapter executable kind into the delivery path rather than a special case for $runtime/$native_loader.internal/engine_lite.cm:2296-2302; pit-shop/provider_adapters.cm
B5Confirm liveness falls out for free: no claim → no realization row → no unit instantiated. No stripping pass needed.docs/shop/endowments.md “What is not claimed is not there”
B6Keep globals a distinct claim kind. Same delivery, no deniability. The fixed global-name set survives.docs/spec/pipeline.md:32; docs/spec/executables.md:31-36

C. The realization becomes fully explicit

“The runner should do no thinking” means the realization is a program in a very small language and the runner is its interpreter.

#DoEvidence
C1Move per-unit claim lists out of the executable manifest and into the realization as explicit name→value binding rows. Today the runner carries and decodes every manifest solely to read these.internal/engine_lite.cm:2218-2271
C2Bake the result-array slot index per unit instance into the realization.internal/engine_lite.cm:2196-2215
C3Stop sending manifests in the start packet once C1/C2 land. The manifest stays a build artifact.pit-shop/bootstrap_realizer.cm:557-573
C4Spec the realization’s row kinds — open image, bind import, install name, invoke entry into slot N — as a closed list. artifact-formats.md specifies every artifact except the one the runner consumes.docs/spec/artifact-formats.md (no pit.target.start@1 section)

D. Minimal C

C is the audit surface. The Pit half of this already exists and is complete.

#DoEvidence
D1Delete source/cart_v2_boot.c (847 lines). It is a second implementation of a format Pit already reads and writes.source/cart_v2_boot.c; Pit codec at pit-shop/cart_v2.cm, runtime mount at pit-shop/cart_store.cm
D2Delete source/start_plan.c (870 lines) — the realization walk in C, complete with its own name table.source/start_plan.c
D3Remove the two-runner fork.source/pit.c:208
D4Shrink cold boot to: map cart → read a fixed header (magic, profile/ABI stamp, engine offset, boot offset) → memcmp the stamp → load the engine image → create actor 0 → enter. Target ~100-150 lines.replaces D1+D2
D5Keep exactly one check in C: the profile/ABI stamp compare. It is not security — it is “these bytes are uninterpretable on this build”, the same class as a bad magic number.source/cart_v2_boot.c:254; docs/architecture/target-profiles.md:65
D6The fixed header is the entire C↔Pit boot ABI. Everything else about the cart — directory, catalog, placements, assets — is Pit’s.

E. The boot actor

#DoEvidence
E1Introduce boot as the first Pit actor. Same code in dev and game builds; only the cart’s entry differs. Frees “root” (root actor / root cart / policy root) and “shop”.
E2Boot holds $mount so the entry does not. It mounts the cart, mounts adjacent bundles, and hands the entry a store rather than the power to mount. This is capability narrowing at the first hop, and it is the security story in one sentence: what a build can do is decided by what its boot cart’s entry may hold.
E3Boot is the front door for start requests: answer from the cart, or (dev) forward to a realize actor that can fetch source and build. Boot itself has no fallback chain.today’s chain lives in pit-shop/shop_actor.ce (1519 lines)
E4Boot canonicalizes locators for now — it knows the requesting package and its package.json aliases, so prosperon/sound resolves against prosperon = gitea.pockle.world/john/prosperon.docs/shop/shop.md:23-57
E5Later: rewrite locators to canonical form when they are placed into the stone image pool. A build-time string rewrite; every runtime locator is then already full, lookups get faster, and E4 disappears from boot.
E6Rename boot/root.cartboot.cart. It is 859 KB today, which is not a Gameboy artifact; the sealed case should carry no shop consortium at all.pit-shop/shop_actor.ce + development_shop.ce + shop_realize.cm = 5,564 lines

F. Split engine_lite

The runtime owns engine_lite (intrinsic, no realization names it) — so everything in it is paid for by every target, Gameboy included. It is 119 KB today.

#DoEvidence
F1Keep the realization walk intrinsic (~460 lines).internal/engine_lite.cm:1865-2327
F2Split out the ~1870 lines of standard library — logging, send/reply, turn processing, requestors — as claimable modules.internal/engine_lite.cm:1-1560
F3Remove the $args scrub. The shop should not have put private realization input inside a public endowment’s value; fix it upstream instead of grooming it in the runner.internal/engine_lite.cm:69-74

G. Memory

#DoEvidence
G1Delete text interning entirely — not shared, not per-actor. With a solid image stone pool it earns nothing.source/cache_profile_test.c (actor_text_interning)
G2Delete the runtime constant pool. Constants live in Mach images.docs/spec/stone.md:8, :101
G3Keep stoned text caching its hash in the length field — independent of interning, and what makes dynamic keys cheap without a table.docs/spec/stone.md:19-24

H. Documentation — done

The site builds clean with no dead links. docs/ now describes the model above.

#ChangeFile
H1Endowment definition rewritten: a name a program requests that the shop may deny. Globals vs endowments; the two delivery cases with an explicit no third case; layering over a raw primitive; unused-is-absent; narrowing between actors.docs/shop/endowments.md
H2Rewritten around the gate: compilation decides, boot cart trusted by ruling, policy not retroactive, mounts decide entry, guest-code flag. Receipt machinery gone.docs/architecture/capabilities.md
H3Start-time verification sentences deleted; the runner-does-no-thinking rule stated where startup is described.docs/spec/executables.md, docs/architecture/actors-and-memory.md
H4“Storage is not permission” → mounting is the trust decision, with the content-verifies / catalog-is-trust split as the hook for later per-mount policy.docs/shop/store.md
H5Opens with “what a build can do is decided by what its boot cart’s entry may hold”; adds the entry/mount row and the guest-code row.docs/architecture/security.md
H6New page. Minimal C as a prime edict, the fixed header as the whole C↔pit boot ABI, the boot actor, boot-holds-$mount, forwarding to a realize actor, engine ownership.docs/architecture/boot.md
H7shop is the consortium, not a store; new boot, realize, mount, guest code entries; policy receipt deleted; locator says its whole meaning belongs to the shop.docs/glossary.md
H8use() stays literal-only — that is what makes the closure knowable. Added the $start literal-or-variable rule and the guest-code flag. Opens by naming what is intrinsic (two naming rules) versus what is this shop’s design.docs/shop/shop.md
H9The shop derives provider keys, not the engine.docs/shop/extensions.md
H10Log-sink registration is an ordinary endowment, not a protected name.docs/architecture/debugging.md
H11“Three sources” over four subsections — fixed by the rewrite.docs/shop/endowments.md
H12Migration ledger deleted from the canonical spec; pit.policy.receipt@1 and its pipeline arrow removed; start packet no longer carries claims/policy_program/policy_request/receipt; authorize no longer re-decides.docs/spec/artifact-formats.md
H13Interning and the runtime constant pool deleted.docs/spec/stone.md, docs/spec/gc.md, docs/architecture/actors-and-memory.md
H14Realization described as explicit enough that starting is following rows.docs/architecture/build-and-artifacts.md, docs/glossary.md

Correction recorded: deny_c was described in an older audit as wholly unbuilt. It is built — pit-shop/executable_manifest.cm:410 walks the extension bindings, traces the import path back to the root, and fails the build, with tests at tests/executable_artifacts.cm:239. The docs describe it accurately.

Still open in docs: docs/shop/spec.md — see section J.

J. Split docs/shop/spec.mddone

The page is mined and deleted.

ContentWhere it went
Shop modesDeleted — rejected design, see section K
Payload policydocs/architecture/targets.md, reframed as cake defaults for what goes into a cart rather than a runtime mode
Catalog variants (pit.catalog.variants@1)docs/spec/artifact-formats.md, new section before Target realization
Fixed-root-shop responsibilities, C bootloader narrative, boot artifact layoutDeleted — superseded by docs/architecture/boot.md
Build-cache file shapes, dylib manifest spellings, .pit/build/<hash>Deleted — implementation trivia
Platform packaging / pit bundle app layoutDeleted — the parts that matter are in docs/architecture/boot.md and docs/shop/compilation.md

K. Delete shop modes — capability is cart composition

Ruled: what a shop can do is decided by which actors its boot cart carries, not by a mode. No compiler actor means it cannot compile; no cake means it cannot build C; no realizer means it resolves nothing it was not shipped. The absence is structural and therefore checkable — the same argument the docs already make for omitting $runtime. If no combination fits, write a bespoke boot actor and put that on the cart.

docs/shop/compilation.md already described exactly this (“you choose which actors to include”); spec.md’s mode table contradicted it. The doc side is now fixed.

#DoEvidence
K1Delete sealed_mode(), compile_refused(), native_load_refused() and the four-mode enum.pit-shop/shop_source.cm:1977-1985, pit-shop/development_shop.ce:1553-1566; ~10 call sites
K2dev and extensible were already identical in every column of the mode table — evidence the enum was not carrying real distinctions.docs/shop/spec.md mode table
K3native_load is gated twice: once as a build-time profile power the binary either has or does not, and again as a shop mode. Keep the profile power, drop the mode gate.internal/engine_lite.cm:2072 (profile.powers.native_load) vs native_load_refused()
K4“Mounts and ingest frozen” becomes: boot does not hand out $mount. Not a mode — an endowment that was not granted.section E2
K5Make the resulting failures legible. With no mode to name, a missing capability shows up at the point of use, so boot’s diagnostic must say which actor was absent — “no realizer in this cart” rather than a generic lookup failure.pit-shop/shop_actor.ce fallback chain

L. No hardcoded targets — docs done, code to audit

Edict: a target name is a label for a set of lever positions, not something the system knows. Nothing in the VM, compiler, shop, or runtime branches on “is this a PS1”. A build script sets levers — word width, numeric rep, collector, scheduler, powers, and which actors the cart carries — and that combination is the build. Two components legitimately know target names because they straddle the C boundary: cake (per-platform sources, flags, toolchains) and raw endowments (thin wrappers over one OS’s real surface).

Docs updated: target-profiles.md is now explicitly normative and says a target name is a label; its axis table no longer enumerates a closed target list. targets.md is reframed as a catalog of settings that have been built, plus how to add one. consoles.md reads as hardware rationale for lever positions. extensions.md names cake as the exception.

#DoEvidence
L1Audit the runtime and shop for branches on a target/system name outside cake and platform/<os>/. Each one is either a lever that should be in the profile or a fact that belongs in a platform folder.source/, pit-shop/
L2Check that a profile axis exists for every lever a port needs, so a new machine never requires a new conditional.docs/architecture/target-profiles.md axis table
L3Keep the two decisions separate in code as well as docs: what the machine can do (profile + platform folder) versus what a build is allowed to do (cart composition + endowments).sections E, K

M. Retiring the ABI crossing — measured on the green base

The crossing exists because boot/root.cart carries the engine as a finalized pool. Bump the Mach ABI and the runtime refuses its own engine, so it cannot run the Pit code that would re-cement the cart. Four agents lost a session to that circle.

Three things were established by experiment on a3184e5e, not by reading:

1. The Pit step and the ABI step are cleanly separable (pit-shop/shop_build.cm:2244). compiler.compile_result(source) produces streamlined mcode in Pit and is ABI-neutral; mach.mach_compile_mcode_bin(mcode) produces the stamped pool and is C. Recompiling internal/engine_lite.cm from source and serializing reproduces the committed boot/engine_lite.mach byte for byte — blake2 e9162acb…, 119,684 bytes both sides. Round-tripping the mcode through JSON and re-serializing yields the identical pool, so the mcode alone is a sufficient input.

2. A loose engine on disk is never consulted at boot — hypothesis disproven. boot_mach_data is fed only from pit_cart_engine_image() (source/scheduler.c:3851, :3879); the engine always rides inside a mounted cart. With both carts moved aside and boot/engine_lite.mach + boot.qop present, a cold bare binary refuses outright: “no root cartridge: none appended, no boot/root.cart, and PIT_ROOT_CART is unset.” So “boot the engine plus the qop with no cart” is not a path. boot/engine_lite.mach is a cementing input, not a boot input.

3. The C serialize step costs 7.5 ms. Measured five runs: 37.6 ms total, 7.5 ms each, against 1.16 s for the Pit compile. Caching it is unnecessary.

The fix

Let the cart’s engine section carry mcode rather than a finalized pool, with one header bit saying which form it is. C serializes at mount when it is mcode — 7.5 ms — and runs the result at whatever ABI the binary was built for. The same dev cart then boots on a v2 or a v3 binary, and an ABI bump requires no re-cementing at all.

This is the rule the system already applies to payloads, applied to the engine: dev ships mcode because mcode survives VM edits, ship cements the finalized pool. A shipped cart is unchanged — finalized engine, zero serialize cost.

#DoNote
M1Add the engine-form bit to the cart’s fixed header; C serializes mcode at mount, maps a pool directly.folds into D4’s fixed header
M2Cement dev carts with an mcode engine; keep ship carts finalized.matches cake/plan.cm:692 payload policy
M3Budget: a dev cart’s engine section grows ~120 KB → ~955 KB. Irrelevant for dev; ship is untouched.
M4Fallback if M1 is deferred: automate a stage-1 crossing — build the pinned old binary, cement with it, proceed. No new C, but the ritual stays.

The engine is one translation unit — a standing constraint

Two steps make images, and only one of them is C:

StepWhere
1:1one mcode unit -> one mach blobC, pit_mach_compile_mcode_bin (source/runtime.c:9290)
N:1several mcode units -> one poolPit, internal/mach_pool_emit.cm (958 lines, new in 9466791a: “Pure-Pit pit.mcode.unit@3 -> pit.mach.pool@1 finalization”)

At the green base nothing fuses units at all: an executable is a record whose modules array carries one blob each (pit-shop/shop_build.cm:347, :383, :456), and the cart cements those N blobs into sections. The link group is the new idea.

So M1 is viable only because internal/engine_lite.cm has no use() imports and is therefore a single mcode unit that C’s 1:1 serializer can rebuild. Were the engine ever several units, C would need the pool emitter, and porting 958 lines of Pit into the audit surface is exactly what the minimal-C edict forbids.

This constrains F2. Splitting the ~1,870 lines of standard library out of the engine must be done by removing code from engine_lite.cm into ordinary modules that go through the normal N:1 path — never by having the engine use() its pieces. An import would make the engine multi-unit and silently break C’s ability to rebuild it, which would surface only as a failed ABI bump much later. Done the right way F2 helps M: a smaller engine means less mcode in the cart than the 955 KB measured today.

Corroboration that this is the grain of the system rather than a new invention: payload_mode already has mcode / mach / mixed arms with a matching engine_abi (pit-shop/shop_build.cm:278-298). Dev-ships-mcode is implemented one level up already; M extends it to the engine.

L1 findings — audited on the green base

The codebase is not uniformly leaky. It is clean where the levers already exist and target-keyed where they do not. pit-compiler/ branches only on rep names, never on target names (rep_fold.cm:301-312); pitlib/ has zero hits; the shop’s source resolver does target arms by data lookup with no hardcoded names (shop_source.cm:694-716); and internal/sysfacts.c / source/pit.c:12-16 publish PIT_TARGET_* as build-injected facts with "unknown" defaults, which is the right plumbing. Two places already demonstrate the correct pattern: rep-keyed folding in pit-compiler/rep_fold.cm, and lever-keyed sizing in source/capacity_harness.c (contrast adder_harness.c, which does the same job by being the PS1).

There is one structural spine and nearly everything hangs off it. source/pit_profile.h is an eight-arm #if/#elif chain — one per console, at lines 13, 26, 41, 57, 146, 177, 275, #else 305 — and each arm sets profile name, value-word bits, object ABI, numeric rep, region defaults, and image cap together, indivisibly. Because a cart’s stamp must match that bundle byte for byte, pit-shop/rep_profile.cm:60-69 was forced to grow a mirror-image TARGET_AXES = {psp, dc, n64, ps1} table in the shop, which internal/cement.ce:250,360,412 then threads through as --target. Adding a console today means editing pit_profile.h, sys_thread.h, rep_profile.cm, three package.json manifests, and pit-linker/toolchains.cm — runtime enums that grow, which is what the edict forbids. Fixing pit_profile.h collapses most of the rest in one move.

Four axes do not exist and are required:

AxisWhyEvidence
memory budget (dynamic region / static region / max images)every pit_profile.h arm exists partly to set three numbers; there is no way to say “2 MiB dynamic” without being the N64pit_profile.h:20,21,24 / 37,39 / 53,55 / 142,144 / …; pgo_profile.h:16
endiansee the ruling needed belowrep_profile.cm:63
thread-primitive providerdistinct from the scheduler axis: PIT_SINGLE_THREAD says “one drain”, not “which mutex links”. Six unrelated targets share one #if for four documented reasonssys_thread.h:33,56
out-of-process daemon powerwhether the host may fork a daemon, symlink a socket, honour PIT_INPROCESS. Currently answered by !defined(_WIN32)source/host.c:334-343,393-399

Three more removals need no new axis, only routing: the profile name as a build-supplied string rather than a chain output; default content path as a startup fact (shop_actor.ce:138-139 hardcodes playdate/emscripten); and provider set discovered from linked providers rather than declared by hand in internal/policy.ce:13-27, which enumerates every platform’s raw endowments by name.

Platform-fact leaks — portable code doing OS-specific work. Well-scoped, no new axis, each belongs in platform/<os>/: internal/vm.c:82-89 (Playdate memory stats called from the portable VM-stats endowment), source/runtime.c:2589,2617 (pit_web_log_tee inside Pit_Log), source/host.c:242-249 (macOS .app path sniffing), source/crash.c:159-165 (Win32 pipe shim), internal/bundle.ce:238-249 (hardcoded macOS .app layout).

Build-system work outside cake. pit-linker/build.cm selects IO backends, link flags, Objective-C source lists, and executable suffixes by tc.system (lines 526-528, 546-557, 859-873, 2032, 2156-2161), and pit-linker/toolchains.cm carries the canonical 22-target supported list plus a hardcoded developer path at line 14. Legitimate kind of work in the wrong folder — cake already owns plan.link_flags(tc).

Roughly 410 further target-name mentions are non-branching: comments, log strings, help text, test fixture names. Not worth touching.

RULING NEEDED — endian

docs/architecture/target-profiles.md lists endian in the axes table, but the profile string it documents carries no endian= field, and source/pit_profile.h:58-70 says the omission is deliberate, with a stated reason: adding it “would change every existing little-endian stamp and break the desktop cart’s byte-identity.” Endian instead travels three ways — the distinct profile name (n64-nan32-v1), the mach header’s endian byte, and the cart header’s endian byte.

That decision is exactly what forces rep_profile.cm:60-69 to recover byte order from the literal name n64, which is a target-name branch in the shop and precisely what the edict forbids. The two positions cannot both stand:

  • Add endian= to the profile string. The name stops being semantically load-bearing and the shop’s target table dies. Cost: every existing stamp changes. That cost is near zero right now — the Mach ABI is being bumped anyway, so stamps are already moving. It will not be near zero later.
  • Keep it out. The profile name stays load-bearing, and the shop keeps a name-to-endian table that the edict has to carve out an exception for.

The doc currently asserts the first and the code implements the second, so one of them is wrong either way. Recommend adding the axis while stamps are already in motion.

Source: plans/archive/worklist.md