Working state — a note taken while the work happens, not a specification. The system as it is meant to be is in Architecture.
Deletion inventory — plans/programs.md R1–R9
Worktree: /Users/johnalanbrook/Documents/work/cell/.claude/worktrees/agitated-dubinsky-a3501e
Branch claude/module-export-link-review-9f8813 @ c13443301. Read-only pass, 2026-08-05.
No daemon was running (lsof -t .pit/pit.sock empty; .pit/pit.sock is a stale
node from 11:52). Nothing was built, seeded, tested, edited or deleted.
Sizes are wc -l of whole files or of the named line spans; treat them as
order-of-magnitude. “Region” line numbers are exact.
0. Summary table
Order is a dependency order, not a priority order. Rows 1-6 are free-standing and can land today; 7-11 need their ruling implemented first; 12-14 have named prerequisites.
| # | System | Ruling that kills it | Evidence (file:line) | Size | Risk | Order |
|---|---|---|---|---|---|---|
| 1 | stale PIT_POOL_PAYLOAD state block (lever read by nothing) | subsumed by R6/R7 | pit-shop/mach_lower.cm:42-79; + :316-318, shop_tools/test.ce:149, tests/hook_image_name.ce:15 | 38 comment lines | zero | 1 |
| 2 | allow_compile + allow_static + the type:"allow" policy handler | residue; R3a supersedes (refusal is by actor absence, by name) | pit-shop/policy.ce:61,63, 236,239, 369-376 | ~10 L | low | 1 |
| 3 | docs hot-reload: two-traversal swap + restart_frame | R4 | docs/architecture/debugging.md:176-205; duplicate at docs/shop/compilation.md:94-108 | ~30 L deleted / ~20 rewritten | zero — no code implements restart_frame | 1 |
| 4 | PIT_APP env probe + app-mode inference | R1 | platform/host/source/host.c:476-480, 488 | ~6 L + fanout | low | 2 |
| 5 | sys_is_app_bundle (decl + 2 impls + 1 caller) | R1 | source/sys_os.h:52-59; platform/posix-runtime/source/sys_os_posix.c:291-305; platform/windows/source/sys_os_win32.c:146-151; host.c:479 | ~35 L, 4 files | low | 2 |
| 6 | app_shop() convention + unconditional ensure_shop_dirs | R1 + R2 | host.c:243-252 (used :261-262); :212-224 (def), :269-273 (call) | ~30 L (mostly conditionalized, not deleted) | low | 2 |
| 7 | pit.bootstrap.target@1 spec block (no producer, no reader) | R8 + residue | docs/spec/artifact-formats.md:219-357 | ~140 doc lines | low, needs one ruling | 2 |
| 8 | dev pass profile (and small) — collapse to one profile | R7 | pit-compiler/passflags.cm:88-108; DEV_LINK at :92-94 is byte-identical to SHIP at :95-97 | ~21 L machinery + 4 test blocks + 1 doc section; zero cache invalidation | low-med | 3 |
| 9 | mach in bundles / mach payload CLIs / bundle-supplied mach execution | R8 | shop_build.cm:2924-2945; shop_realize.cm:1663, :1802-1808, :1871; shop_tools/bundle.ce + publish.ce; shop_actor.ce:614-643 | ~85 L | med — breaks the Playdate ship lane | 4 (after §7.3) |
| 10 | multi-target executable catalog (pit.catalog.variants@1) | R8 | pit-shop/target_catalog.cm (whole, 188 L) + --targets plumbing | ~208 L | med-high — appears in boot.qop’s own manifest | 4 (after §7.3) |
| 11 | shoplib/mach_press.cm cross-press arm (the no-refusal bypass) | R8 | shoplib/mach_press.cm:22-29, 50-58, 120, 132 | ~15 of 143 L | low-med — verify pit-shop/mcode_lower.cm:25 first | 4 |
| 12 | linker program-side immutability census / mutable_export_risk | R5 | pit-linker/mcode_link.cm:798-1091, :2543-2558, :2497, :2500, :2654; tools/export_taxonomy.ce:134-142, 157 | ~330 L + 1 test case + 3 oracle rows | med — hinges on R5 emitting a real stone op | 5 |
| 13 | the STATIC link lane (prepare_static_export_defs, $linked_export_N, cart IMPORTS/NAMES rows, boot-walk name binding) | R6 + R7 | pit-compiler/mcode.cm (~207 L); compiler.cm (~33); shop_build.cm/shop_source.cm (~93); cement.cm:392-432 + engine/boot_walk.cm:262-272, :328-343 (~65); tooling (~149) | ~550-600 L code + ~210 L tests | HIGH — the only lane production uses today | 6 |
| 14 | build.capabilities.daemon all-or-nothing recipe lever + daemon-stub | R1 tail / R9 | 12× recipes/*.json:29; 12× platform/*/package.json endowments.daemon; cross-check tests/recipes.ce:194-196; platform/posix-daemon/ (1,076 L), platform/daemon-stub/ (24 L) | 24 recipe/manifest lines gate ~1,100 L of C | med — needs the R9 per-product lever designed first | 7 |
| — | pit-shop/terminal.ce pool-lane PGO refusal | NOT killed by these rulings (L2b, carried forward at plans/programs.md:263) | pit-shop/terminal.ce:2175-2183, 2418-2432 | ~22 L | do not sweep | — |
| — | cart N-section machinery | R6 ALMOST kills it — the ROM-window escape clause saves it | cement.cm:731-782; cart2.cm:279-330; cart/cart.cm:227, :282, :510, :540, :732-786 | ~150-250 L becomes unexercised | see §5.0 | — |
1. R1 — the cart declares the mode; PIT_APP dies; daemon split
1.1 Every PIT_APP / sys_is_app_bundle / PIT_INPROCESS site
Full grep (excluding build/, plans/, .qop). Classified.
DIES (R1 replaces with a cart-declaration read):
| site | text | verdict |
|---|---|---|
platform/host/source/host.c:476 | const char *app_env = getenv("PIT_APP"); | DIES |
platform/host/source/host.c:477-479 | app_mode = !daemonized && ((app_env && …) || sys_is_app_bundle(paths.binary)); | DIES — becomes a read of the boot entry’s start declaration |
platform/host/source/host.c:488 | if (app_mode) force_inprocess = 1; | DIES (folds into the declared mode) |
platform/host/source/host.c:243-252 | app_shop() — invents ~/Library/Application Support/<exe>/shop | DIES under R2 (the declaration says whether a shop exists at all, and the product names it) |
platform/host/source/host.c:261-262 | else if (app_mode) paths->shop = app_shop(paths->binary); | DIES |
platform/host/source/host.c:489, 493 | resolve_paths(&paths, requested_shop, app_mode) / if (!daemonized && !force_inprocess && !app_mode) | REWRITTEN to read the declaration |
source/sys_os.h:52-59 | declaration + the doc-comment that asserts the env-probe model (“an app gets its own shop and an in-process boot rather than the development daemon … the launcher’s PIT_APP override still works there”) | DIES |
platform/posix-runtime/source/sys_os_posix.c:291-305 | impl. #if defined(__APPLE__) → strstr(binary, ".app/Contents/MacOS/"), #else → return 0; — this is exactly the plan’s §2 claim: a shipped Linux binary has no way to say what it is | DIES |
platform/windows/source/sys_os_win32.c:146-151 | impl, return 0, comment says “PIT_APP remains the way to ask for app behaviour here” | DIES |
STAYS (compat shim / test harness — PIT_INPROCESS is not named by R1):
R1 deletes PIT_APP by name. PIT_INPROCESS is a different thing: it is the
test harness’s way to force a no-daemon boot, and it has a large live
consumer set. It should be re-expressed as “the declaration says in-process”
but the tests need an override that does not require re-forging a cart.
| site | note |
|---|---|
platform/host/source/host.c:481-487 | the PIT_INPROCESS read itself; #if !defined(_WIN32) |
platform/host/source/host.c:533-538 | the “forced in-process node has no daemon transport” comment + config.daemon_socket_path gate — still needed under a declared mode |
source/pit_internal.h:2650-2658 | pit_set_cli_exit_code doc names “(windows/app/PIT_INPROCESS)” — comment edit when PIT_APP goes |
internal/os_posix.c:735-745 | in-process CLI exit-code path — comment mentions PIT_INPROCESS |
pit-shop/terminal.ce:2145 | “In-process CLI (windows/PIT_INPROCESS): no client control fd” — comment |
source/scheduler.c:859 | historical deadlock note |
tests/cli/inprocess.ce:5,26,69,71; tests/cli/plan_image_cap.ce:6,18,85,125,131; tests/cli/reply_lifetime.ce:11,102; tests/cli/fuzz_progress.ce:73,120; tests/fixtures/plan_image_cap_child.ce:6; tests/watch_snapshot_lock_order.ce:14 | live test harness uses |
cake/forge.cm:1191 | var env = {PIT_INPROCESS: '1', PIT_CONTENT: content} — forge itself boots in-process to press; this is load-bearing for the seed lane |
aot_bench/perf_one.ce:3 | bench harness |
Not app-mode at all (false positives on the grep, listed so nobody re-raises
them): platform/watchos/watchkit_interface.c:3-5, platform/uikit/uikit_window.c:3-5,
platform/darwin/appkit_window.c:3-4 — those are PIT_APPLE_UI_* macros.
1.2 The .app consumers that depend on the path probe
sys_is_app_bundle’s only reason to exist is the macOS .app double-click
path. The producers and their tests:
shop_tools/bundle.ce— 896 L,pit bundle app. Lays outContents/MacOS(:175),Contents/Frameworks(:176),Contents/Resources(:177), writesContents/Info.plist(:302). It never writesPIT_APP— it relies entirely on the path probe. Under R1 this file gains a start-declaration write and loses nothing else.tests/cli/app_bundle.ce(131 L) — asserts “double-clickable app boots in-process and runs its app root”. Pins the probe behaviour; must be rewritten to assert the declaration.tests/cli/aot_app_bundle.ce(170 L) — same shape plus the native dylib.
Docs carry nothing about PIT_APP or app mode. rg 'PIT_APP|app bundle|app mode' docs/
returns only two unrelated hits (docs/architecture/debugging.md:134,
docs/architecture/build-and-artifacts.md:117). So the mode is entirely
undocumented environmental behaviour — the plan’s charge is exact, and there
is no doc to delete, only a doc to write.
1.3 What makes the daemon all-or-nothing today
Three coupled levers, none of them per-product:
build.capabilities.daemon— a boolean at line 29 of every recipe:recipes/darwin.json:29andrecipes/linux.json:29aretrue; the other ten (emscripten, ios, tvos, watchos, windows, n64, ps1, psp, dc, playdate) arefalse. One bit per target, not per product.- The platform package’s
endowments.daemonrow picks the C file:platform/darwin/package.json:9→runtime::platform/posix-daemon/source/daemon_posix.cplatform/linux/package.json:8→ sameplatform/{windows,ios,tvos,watchos,web,playdate,n64,ps1,psp,dc}/package.json→runtime::platform/daemon-stub/source/daemon_stub.cdocs/architecture/recipes.md:50,201documents both halves.
tests/recipes.ce:194-196asserts the two agree:check(r.build.capabilities.daemon == …)with the message “capabilities.daemon disagrees with the platform package’s daemon provider”. This test is what makes the pair a contract; a per-product lever has to replace it, not just drop it.linked_packagesnamesposix-daemondirectly —recipes/darwin.json:38region ("posix-daemon"in thelinked_packagesarray).
C surface the lever selects (the whole thing that a daemon-free product would stop compiling):
| file | LOC |
|---|---|
platform/posix-daemon/source/daemon_posix.c | 942 |
platform/posix-daemon/source/sys_ipc_uds.c | 134 |
platform/daemon-stub/source/daemon_stub.c | 24 (the null arm) |
source/daemon.h (12 exported entry points, :19-36) | ~40 |
Plus the client half in host.c:492-512 (pit_daemon_launch — the “never
boots pit, forwards argv” role) and host.c:405-443 (setup_daemon_identity,
pid file), and source/scheduler.c:3222 (pit_daemon_listener_stop).
R9’s shape, noted not designed: today the pair is
{capability bit in the recipe} × {file in the platform package}. R9 wants the
same pair keyed by product, and wants it to do both halves — strip the C and
drop the module shim, so an actor asking for an absent feature is refused at
realization by name. $hook is the existing model
(source/pit_internal.h:41-53, “no ring, no field”); the endowment set is
recipe.lists.endowments / endowments_whole read by link_scope()
(cake/plan.cm:367-418 — the take() closure at :394-404 is exactly the
per-recipe read that must become per-product).
Risk on deleting daemon-stub: the stub is the definition of “this
target has no daemon”. Under R9 it becomes the default rather than a selected
file, so it is a rename/relocation more than a deletion. Deleting it before
the per-product lever exists breaks ten recipes.
1.4 STATUS after D1 (branch arc/start-declaration, 2026-08-05)
Landed: the declaration itself. start_flags is a u32 at header offset 196
of pit.cart@2, carved out of the 60 reserved bytes — so the crossing is
additive and no existing cart needed a read bridge. Each bit subtracts a
default, making 0 the development program (resident/shop/daemon), which is
exactly what every pre-declaration cart already says.
pit_cart_start_flags (source/cart_boot.c) reads it from the linked image
before any role is chosen; pit_host_main takes its role from it and from
nothing else. PIT_APP is gone. sys_is_app_bundle survives at one call site
— choosing the Application Support shop path for a bundle that DECLARES a shop
— and decides no mode. ensure_shop_dirs runs only for a program that
declared a shop or whose invoker named one with --shop.
NOT landed: the daemon split (row 14 above, R1’s tail). D1 made the daemon
conditional at RUN time — a cart declaring no_daemon never spawns one, never
serves one, and is handed no socket path — but the ~1,100 L of daemon C is
still LINKED into every desktop binary. Closing that needs three things D1 does
not have, in this order:
- A per-product replacement for the per-recipe pair. “Does this build have
a daemon” is
recipes/*.jsonbuild.capabilities.daemon×platform/*/package.jsonendowments.daemon, andtests/recipes.ce:194-196asserts the two agree — so it is a contract, and a per-product lever must replace it rather than sit beside it. That lever is R9/D3, not D1. - Closure discovery, to know whether daemon actors are in the product at
all. That is D2’s
$start-site static analysis; without it “this closure contains no daemon actor” is not a computable fact, only a hand-assertion. - A named realization refusal for an actor that asks for an absent daemon (R9), or stripping the C turns a missing feature into a link error or a mystery runtime failure instead of a refusal.
The declaration is the right input to that lever — a no_daemon cart is by
definition a product that should not link posix-daemon — which is why D1
defines the bit even though nothing strips C on it yet. Wiring the bit into
link_scope() before (1) and (3) exist would remove the code and leave the
refusal missing.
2. R8 — mach is never shared or shipped
2.1 Do the bundles carry mach? — No today, but the writers still can
Measured by parsing the qop indexes and inflating the /manifest members
(qop format source/qop.h:17-42; index = trailing index_len/archive_size/qopf):
| archive | bytes | entries | manifest kind tally |
|---|---|---|---|
boot.qop | 13,700,682 | 153 | module 323, mcode 128, program 13, executable 12, manifest 12, mach 0 |
boot/targets/linux/boot.qop | 13,257,178 | 153 | identical tally, mach 0 |
shop.qop | 13,384,036 | 709 | manifest.json + packages/** — pure source (.c/.cm/.ce/.h) |
The only mach substrings in boot.qop’s manifest are three module locators
(shoplib/mach_pool_emit, shoplib/mach_press, pit-shop/mach_lower) — not
artifacts. The ruling behind that is recorded in-tree at
pit-shop/shop_build.cm:2832-2864 (payload_mode “mcode”, RULED John 2026-07-30)
and pinned at :2980; the manifest writer is write_prepared_content
(:3047-3095, format line :3069).
DELETE — the dormant mach arm in the seed writer. pit-shop/shop_build.cm:2942-2945
plus its comment :2924-2941, ~22 LOC:
if (is_text(m.mach_hash) || (m.mach && is_text(m.mach.hash))) {
objects["blake2:" + bare_hash(m.mach_hash || m.mach.hash)] = {kind: "mach"}
carried = carried + 1
}
Unreachable only because :2980 pins mcode — this is the exact code that would
put mach into a content bundle.
DELETE — the mach edge in the publish closure walk. pit-shop/shop_realize.cm:1663
(1 line, load-bearing): if (m.mach && m.mach.hash) push(m.mach.hash, "mach", …).
That walk is documented as “exactly what publish ships” at :1536-1547, and
publish_one_root writes every walked object into the archive at :1830-1843.
DELETE — mach as the publish default. pit-shop/shop_realize.cm:1871
(verified verbatim): payload_mode: request && request.payload_mode ? request.payload_mode : "mach".
Gated by the CLI test tests/cli/bundle_info.ce:154 (if (x.payload_mode != "mach") bad(…)).
DELETE — cross-rep publish threading, pit-shop/shop_realize.cm:1802-1808
(~7 LOC): it exists only because mach ships — “mach is target-final and
rep-stamped at generation, so a bundle destined for a narrow-rep runtime must
realize under that rep or every module in it is refused at mount”.
DELETE — the mach payload form in the shipping CLIs (~40 LOC):
shop_tools/bundle.ce:37 (PAYLOAD_FORMS = ["mcode","mach","source"]), :51
(flag help “default: mach”), :138 (the default), :141 (refusal text), and
the bundle info accounting at :465, :473-487, :525, :725-727;
shop_tools/publish.ce:19, :54 (--payload “mach (default), mcode, mixed”).
DELETE — bundle-supplied mach execution. pit-shop/shop_actor.ce:614-643
(~14 LOC). scratch_blob_map’s header at :614-618 names its three sources —
“dev realize, supplied bundle, qop content volume” — and the mach arm at
:630-638 maps a received blob straight into the scratch image. KEEP the
mcode arm (:645-661), which is already the lower-on-arrival path R8 mandates
(and :642-645 already refuses an unhydrated mach reference by name).
KEEP — realize_payload_mode’s "mach" default at
pit-shop/shop_realize.cm:334-341: that is the mode for running locally.
2.2 boot/targets/linux/engine_lite.mach — not the forge floor; a Git-transported CI seed, and byte-identical to the host one
Exactly two committed .mach files exist in the tree:
| file | bytes | |
|---|---|---|
boot/engine_lite.mach | 69,360 | |
boot/targets/linux/engine_lite.mach | 69,360 | cmp says IDENTICAL (verified) |
By contrast boot/root.cart (9.48 MB) vs boot/targets/linux/root.cart
DIFFER, and the two boot.qops differ. The identity of the two .mach
files is load-bearing for the plan: the engine pool is stamped by profile
(rep/endian/mach-ABI), not by ISA, so one blob already serves darwin-arm64 and
linux-x86_64.
Consumers of boot/targets/** — all CI, never forge:
scripts/use_ci_seed.sh:10,12,25,26 (copies root.cart/engine_lite.mach/boot.qop
over boot/), scripts/ci_platform_vm.sh:20, scripts/ci_local.sh:23,53,131-135,259,
.gitea/workflows/ci.yml:79,207,256,306, .gitea/workflows/cross-check.yml:67,137,195,
ci/README.md:36,47-50, .gitignore:96. Never referenced by cake/forge.cm,
cake/plan.cm, Makefile, or boot/manifest/.
Consumers of the host boot/engine_lite.mach — this is the forge floor:
Makefile:342 (seed-freshness prerequisite), cake/forge.cm:1260
(FIXPOINT_ARTIFACTS), :1293 (stage), :1327-1329 (promote); produced by
pit-shop/shop_build.cm:3173, :3218, :3240; freshness at
pit-shop/derivation_status.cm:62, :120-123. It is cemented into root.cart,
which forge .incbins into the binary (cake/forge.cm:64-72 boot_section_source;
Makefile:88). Never shipped loose. KEEP — R8-allowed.
boot/targets/linux/* is the awkward case. It is 22.8 MB (9.48 MB cart +
69 KB mach + 13.26 MB qop) crossing machines over Git so a Linux box with no
pit can bootstrap. Under a literal reading of R8 the .mach and the .cart
(which is pools, i.e. mach — pit-shop/boot_cart.cm:118-260) are shared mach.
Recommend: KEEP, and state it as a named second exception in R8 — “the
bootstrap floor may be transported as a committed pair”. As written, R8 deletes
CI. Existing risk already recorded: this seed goes stale silently
(plans/archive/night-2026-08-04/cross-builds.md:97, ci/README.md:54-60 — 81 commits
behind while the lane stayed green).
A third committed mach artifact R8 hits: source/mach_pool_fixture.h —
96,815 bytes (verified), GENERATED by tools/pool_fixture.ce (308 LOC),
carrying ten cross-pressed pools for ten distinct profiles
(source/mach_pool_fixture.h:1701-1722: base, gc-compact, endian-be, nan32,
nan32+compact, fixed32, fixed32+compact, f32, fixed, n64-be — two of them
big-endian at :1706, :1720). Compiled into source/mach_pool_test.c:16,
read at :1943-1962, regenerated on every make smoke (Makefile:313-334).
It is cross-target mach lowering outside the forge path whose product is
committed to the repo. KEEP + R8 needs a third carve-out (“test fixtures”),
or the rule fails on day one of make smoke.
2.3 Can mach be served to another machine? — one path only
(a) pit-shop/courier.ce (572 LOC) + courier_transport_net.cm (25) +
courier_transport_web.cm (15) — actor messages only, NO artifacts. Wire
vocabulary fixed at courier.ce:10-15; envelope constructors :230-238, :241-263, :301-309 carry only {type: "contact", portal_port, data} /
{type: "user", target, data} plus reply/trace fields. KEEP — not an artifact
path at all.
(b) advertise / serve (pit-shop/source_upstream.cm, 400 LOC) — SOURCE
TEXT ONLY. The kind filter is the key derivation itself:
pit-shop/source_upstream.cm:132-135
function peer_source_key(locator) {
return shop_store.bare_hash(shop_store.hash_text(
"pit.source.file@1\n" + text(locator || "")))
}
advertise (:218-255) returns {files: […]} for git (:223), shop-peer
mount (:234) and directory (:249); serve (:258-312) returns {data: raw}
for the same three; peer object reads go through peer_object (:155-163) →
peer_catalog_hash → peer_source_key → pit.source.file@1. Bound in at
pit-shop/shop_build.cm:207-208, re-exported pit-shop/shop_source.cm:3058-3059.
KEEP entirely — never sees mcode or mach.
(c) publish → signed .qop → HTTP ingest_bundle — THE mach wire.
Producer pit-shop/shop_realize.cm:1858-1935 (realize_publish), default mach
at :1871, closure objects into the archive at :1830-1843, archive written
:1932. Consumer pit-shop/shop_source.cm:2884-2930 (ingest_bundle), fetched
at :2861-2869, verified at :2829-2857 (re-hashes every object, :2850-2856).
Execution of a received blob: pit-shop/shop_actor.ce:630-638. This is the
whole of §2.1’s DELETE list.
Narrowing worth recording: cross-rep mach publish is ALREADY refused. The
realize lowering path is shop_build.finish_unit_mach (:932-978) →
lower_mach_unit (:759-783) → build_fleet.request {type:"mach", numrep, endian} → pit-shop/builder_worker.ce:246 → mach_lower.fragment_unit → the
refusal at pit-shop/mach_lower.cm:205-218. So --target playdate --numrep nan32 from a nan64 host is already blocked. The surviving hole is host-rep
mach shipped to a foreign system — narrower than R8’s framing implies, so
the artifact-path deletion is smaller than §7.4 suggests. The bigger deletion
is the executable catalog (§2.5a).
2.4 pit-shop/shop_store.cm K_mach paths — zero deletions
846 LOC file, 70 lines mention mach. Every one classified:
| function | lines | ~LOC | role | verdict |
|---|---|---|---|---|
current_mach_abi | 20-32 | 13 | process-constant ABI stamp folded into every K_mach identity | KEEP (cache correctness) |
cache_mach_blob_value | 621-630 | 10 | content-address the pool into the LOCAL object store | KEEP (a) local cache |
mach_debug_key | 632-660 (comment 632-657) | 3 + 26 | f(mach content hash) → debug sidecar key | KEEP (c) §7.2 |
cache_mach_debug_value | 662-668 | 7 | write pit.mach.debug sidecar | KEEP (c) |
mach_origin_key | 670-687 (comment 670-684) | 3 + 15 | f(pool_content_hash) → pit.mach.origin@2 — the exact function plans/programs.md:302 cites | KEEP (c), explicitly carved out |
cache_mach_origin_value | 689-697 | 9 | write the origin sidecar | KEEP (c) |
mach_derivation_key (K_mach) | 703-731 | 15 | K_mach\npit.mach.pool@1\n<abi>\n<lowering_identity>\n<mcode_hash>\n<label>\n<numrep>\n<endian> | KEEP (a) — purely local derivation cache, never leaves the shop |
stage_mach_result | 733-744 | 9 | payload + both sidecars, staged in a disposable actor | KEEP (a)+(c) |
publish_mach_result | 746-753 | 8 | “publish” = write the LOCAL catalog edge (catalog_put_value only) | KEEP (a) — misnamed only |
cache_mach_result | 755-775 | 21 | lookup-or-fill on K_mach | KEEP (a) |
| exports | 836-844 | 9 | — | KEEP |
Net: no deletions in shop_store.cm. Nothing in the store shares mach; it
is all local cache plus the §7.2 identity/debug keys. Two naming hazards worth
fixing in the same landing so a later grep audit stays legible:
publish_mach_result (:747) reads like distribution and is not, and
build_read_value’s comment at :498 says “mach blobs, bundle-served objects”.
The numrep/endian fields inside mach_derivation_key (:717-718, :727)
only differ from host when a caller is cross-repping — harmless as cache inputs.
2.5 Cross-target mach lowering outside the forge path
The refusal is pit-shop/mach_lower.cm:196-218 (host normalization at
:197-198, refusal at :205-218), rationale comment :22-40 — the line-38 the
plan cites is mid-sentence in “because a caller asking THIS lane for a foreign
target has confused realizing-to-run with pressing-to-ship”. Pinned by
tests/cross_target_lowering.ce (106 LOC), both directions. KEEP — permanent
per plans/programs.md:256-260.
Three routes around it:
(a) press_unit / press_floor_unit — same module, no refusal. KEEP: this
IS the forge path. pit-shop/mach_lower.cm:336-389 (~54 LOC), exported :391.
Takes a full rep_profile stamp, derives a foreign target at target_of
(:304-312), passes {target} to both mach.mach_compile_mcode_bin(…, {fragment: true, mach: false, target}) (:342-347) and pool_emit.emit(…, {target}) (:378). Callers: pit-shop/boot_cart.cm:176 (every module of every
cart executable), :272 (engine/engine_lite), :282 (engine/boot_walk) —
all inside pool_executable (:118-260) and cement_root_cart; plus
shop_tools/cement.ce:204. AUDIT that last one: pit cement (539 LOC) is a
user-reachable cross-target press with an --endian flag at :49 and an
--app ship-cart mode at :43, :332 — the forge path exposed as a command.
(b) shoplib/mach_press.cm — a full cross-press with NO refusal at all.
143 LOC; its own header states the bypass at :22-29: “The heavy realization
lane is pit-shop::mach_lower, which does the same two steps plus … the
foreign-target refusal a shop realization owes. This module is what everything
ELSE presses through… CROSS-PRESSING is opts.stamp”. target_of at
:50-58 (a duplicate of mach_lower.target_of), mach_compile_mcode_bin at
:120, pool_emit.emit at :132. Non-test importers: scheme/compiler.cm:8,
pit-compiler/compiler.cm:9, pit-shop/mcode_lower.cm:25,
pit-shop/mach_lower.cm:151, tools/cross_unit_inline_probe.ce:12,
tools/compiler_budget.ce:26, aot_bench/{vs,mt,pause_test,mstress,census}.ce,
aot_bench/diagnostics/fannkuch_bisect.ce:5.
DELETE the stamp/target parameter (~15 of 143 LOC); KEEP the host press.
Every listed caller presses for the host; the cross arm has no in-tree
production consumer. Risk: pit-shop/mcode_lower.cm:25 imports it and
mcode_lower is the runtime’s start-time lowering — verify no stamp reaches it
before cutting.
(c) tools/pool_fixture.ce — a ten-profile cross-press, committed. See
§2.2. Uses shoplib::mach_pool_emit directly (:21) and
mach.mach_compile_mcode_bin (:149, :236), bypassing BOTH mach_lower and
mach_press; self-described at :7-16 as “THE CROSS-EMITTER GATE”.
KEEP + carve out.
Also: shoplib/mach_pool_emit.cm (1,190 LOC) is fully target-parameterised
(profile-mismatch refusals at :266, :277, :957, :966). That parameterisation
is what the forge path needs — survives R8 intact.
2.6 Executable-sharing machinery — yes, and this is the big one
(a) The per-target executable catalog — pit-shop/target_catalog.cm, 188 LOC,
pure distribution. DELETE the multi-target variant dimension. Header :1-12:
“A catalog entry lets one logical locator name several independently realized
executables: {schema: "pit.catalog.variants@1", variants: [{selector: {system: "darwin"}, object: "blake2:…"}, {selector: {system: "linux"}, object: "blake2:…"}]}”. Selector axes at :24-33 (target, system, arch, cpu,
abi, toolchain). Written during publish at pit-shop/shop_realize.cm:1834, :1846-1850 and during seed at pit-shop/shop_build.cm:3025-3027. The schema
appears in boot.qop’s own manifest. This is exactly the “executable-sharing
machinery” plans/programs.md:335 names. Under R8 a machine that must lower
its own mach cannot consume a foreign variant, so the variant dimension is
dead; keep at most a single-object catalog. ~188 LOC + --targets plumbing at
shop_tools/publish.ce:22-23, :49-53, pit-shop/shop_realize.cm:1864-1866,
shop_tools/bundle.ce:216-217 (~20 LOC).
(b) Executable objects in every shipped archive — KEEP the records, delete the
mach hashes they name. Seed: pit-shop/shop_build.cm:2906 (kind: "executable"), :2921 (kind: "manifest") — measured 12 + 12 in boot.qop.
Publish: pit-shop/shop_realize.cm:1643-1666 walks executable → manifest →
mcode/mach → pgo; :1827 picks the root exe hash. These objects are records,
not machine code — pit-shop/executable_manifest.cm:1-11: “The manifest is
the program’s LOGICAL identity… It carries no code, no target profile, no
placement.” That is the mcode-side identity R8 needs.
(c) pool_executable — KEEP, the sanctioned product. pit-shop/boot_cart.cm:118-260
(~143 LOC), exported :325. Runs mcode_link.link_result(rows, PRESS_LINK_PLAN)
then mach_lower.press_unit per module into profile-stamped pools. Callers
:245 (root exe), :258 (each fleet row), shop_tools/cement.ce:204; referenced
by pit-shop/shop_build.cm:3258. Its output goes into root.cart, which forge
.incbins into the binary — the fused-blob-in-the-binary product R8 allows.
(d) The one path that ships a linked image off-machine: pit bundle app --seed-only for a foreign --target. shop_tools/bundle.ce:210-223 sends
{type: "publish", target, targets, numrep, payload_mode: planned.payload};
scripts/build_playdate_sim.sh:269, scripts/build_playdate_device.sh:369,
scripts/build_playdate_linux.sh:222 all pass --target playdate --payload mach
and drop the resulting seed.qop into the .pdx. DELETE the mach arm; keep
mcode/source. See risk 3 below.
No path in pit-linker/ (build.cm, mcode_link.cm, static_link.cm,
toolchains.cm) ships anything off-machine — it produces link plans and
static-link inputs consumed in-process.
2.7 R8 risk register — five things to settle before deleting
- R8 as written deletes CI.
boot/targets/linux/is 22.8 MB of mach-carrying artifacts (root.cartis pools;engine_lite.machis a pool) transported between machines over Git and consumed by 8 CI steps. R8 needs an explicit “bootstrap floor” exception, orscripts/use_ci_seed.shand both workflows must be redesigned first. - R8 as written also deletes
make smoke.source/mach_pool_fixture.his ten cross-pressed pools, committed, regenerated every smoke (Makefile:313-334). - The Playdate ship lane breaks. The three
scripts/build_playdate_*.share the only in-tree consumers of--payload mach, and the.pdxseed is exactly “mach in the content bundle”. The replacement (ship mcode, let the target press) needs a target-side presser in the Playdate build — nothing today does this. plans/programs.md:326-331(§7.3) is a hard blocker on the positive half. mcode is host-rep-tainted, so “mcode is the only shared artifact” ships a nan64-limited artifact to a nan32 target. Deleting mach sharing before fixing §7.3 removes the only currently-correct way to get a rep-exact payload onto a narrow-rep target. Fix 7.3 first; it is a prerequisite, not a parallel item.- Two names will mislead a later grep audit —
publish_mach_result(shop_store.cm:747, actually a local catalog write) andbuild_read_value’s “bundle-served objects” comment (:498). Rename in the same landing.
3. R5 — the compiler stones module returns; the linker census collapses
Anchors verified by hand:
- The promise already exists in the spec, unimplemented:
docs/spec/stone.md:52-58— “Every module’s return value is automatically stoned … The returned object is stone inside the actor.” - The C read that does not stone:
source/mach_vm.c:5447VM_CASE(MACH_LOAD_BOUND_MODULE)readsresults->values[slot]/ the binding’s$providersrow raw. Related opcode sites:source/mach_vm.c:223, 3080;source/mach_pool.c:1285, 1443, 1906, 3183;source/mach_internal.h:372. pit_stone_valueis one header bit and shallow —source/pit_internal.h:1505.
3.0 BLOCKING PRECONDITION — the deletion depends on how R5 stones
pit-linker/mcode_link.cm:772 is the hinge:
return {kind: "stone_record", fields,
frozen: run.return_fact.kind == "stone_record",
complete, member_count: length(names)}
frozen is true only when a stone op in the module main’s mcode dominates
the return. The census at :2543-2558 runs only for frozen != true.
- If R5 makes the compiler emit a real
stoneinstruction immediately before module main’sreturn,frozenbecomes universally true and everything in §3.2 is unreachable dead code. - If R5 freezes on the runtime side instead (at
MACH_LOAD_BOUND_MODULE,source/mach_vm.c:5447— the siteplans/programs.md:143-145cites),export_summarystill sees nostoneop,frozenstays false, and nothing below is deletable without separately telling the linker “module main returns are frozen by construction” (a one-line change at:772).
R5’s own wording — “stoned by the compiler at the module main’s return” —
picks the first. Record it as a hard requirement of the implementation, and
also require that the stone be the last thing before return:
mcode_link.cm:641-647 marks a candidate escaped when base.kind == "stone_record", so any store after the emitted stone drops the whole export
to generic.
3.1 KEEP — the provider-side recognizer, mcode_link.cm:159-775 (~617 LOC)
Stone proves immutability; it does not prove identity. Finalization rewrites
p.f into ["function_ref", …] (mcode_link.cm:1236-1239) and must know
which function each member holds. Nothing about stoning the return says that.
So the CFG builder, the definite-assignment lattice, the escape check and the
per-member exactness all survive R5 intact:
| region | lines | ~LOC |
|---|---|---|
same_fact | 159-167 | 9 |
CFG-shape rationale comment (the control_safe post-mortem) | 169-181 | 13 |
BLOCK_TERMINATORS/BLOCK_COND_JUMPS/jump_label | 182-196 | 15 |
build_blocks | 198-273 | 76 |
lattice comment, UNKNOWN_FACT, copy_state, merge_states | 280-329 | 50 |
transfer_fact (incl. the is_stone/is_record fold, :372-389) | 331-383 | 53 |
taken_successors | 384-422 | 39 |
safe_candidate_use | 419-434 | 16 |
note_member | 435-448 | 14 |
member_key/copy_assigned/intersect_assigned | 450-471 | 22 |
block_assignments | 473-500 | 28 |
union_slot_set/nested_touches_slots | 496-527 | 32 |
RECOGNIZER_INSTRUCTION_CAP + analyze_export_body | 529-670 | 142 |
export_summary | 676-775 | 100 |
Sub-line edits only: :772 frozen: becomes constant true (or the field
goes), and its explanatory comment at :686-690 goes with it.
3.2 DELETE OUTRIGHT — the program-side census, mcode_link.cm:798-1091 (~294 LOC)
| region | lines | ~LOC |
|---|---|---|
| census header comment (“a module result is NOT frozen when the runtime hands it out — a consumer really can assign to an import”) | 798-817 | 20 |
safe_import_use | 819-838 | 20 |
mark_providers_unsafe | 839-853 | 15 |
union_holder | 855-864 | 10 |
merge_holder_states | 866-879 | 14 |
copy_holder_state | 881-900 | 20 |
holder_seeds_main | 899-917 | 19 |
holder_seeds_nested | 919-930 | 12 |
transfer_holder | 932-955 | 24 |
audit_holders | 954-1041 | 88 |
withhold_units_imports | 1040-1058 | 19 |
mutable_export_risk | 1060-1091 | 32 |
:798-817 is the block R5 falsifies verbatim — its whole premise is that
the runtime hands out an unfrozen record. Every unsafe use it enumerates
(store into the import, import-as-call-argument, push, re-export) becomes a
runtime disrupt once the record carries the stone bit, because shallow
stone freezes exactly the member bindings finalization rewrites.
Do not demote to a debug assert. An assert would have to re-run the whole 294-line dataflow to fire, and would fire on programs that are now legal-but- disrupting — a runtime question, not a link-time one.
One carve-out to record, not to keep: mcode_link.cm:1017-1019
(op == "put" → mark_providers_unsafe(…, "put@cell")) is the only census
check stone does not cover — it guards rebinding the slot the import arrived
in, not mutating the record. But it is not load-bearing today either: the
census applies only to frozen != true exports (:2552), so a stone(...)ed
export already reaches rewrite_function with this hole open (rewrite_function
propagates import origin through get at level 1, :1268-1272, and has no
put handling; pit-compiler/structured.cm:129 classes put as cell_write
so local_defs never reports it as a def of the parent slot). Delete it with
the rest and track the put-rebinding gap in rewrite_function as the
pre-existing defect it is — the fix is ~15 lines (a per-unit “which main
slots does any nested function put to” scan feeding slot_set(origins, …, null)),
not 294.
3.3 DELETE — census application and census-only telemetry
| site | what | ~LOC |
|---|---|---|
mcode_link.cm:2543-2558 | the withhold loop; risk = mutable_export_risk(rows); reason: "export_may_be_mutated" at :2554; stats.mutable_exports_withheld += 1 at :2555 | 16 |
mcode_link.cm:2497 | mutable_exports_withheld: 0 in the stats initializer | 1 |
mcode_link.cm:2500 | var risk = null | 1 |
mcode_link.cm:2654 | mutable_export_risk in the module return record (the export) | 1 |
mcode_link.cm:846-847, :1051-1053 | risk.reasons[…] / risk.sites[why] += 1 reporting | 5 |
tools/export_taxonomy.ce:134 | mcode_link.mutable_export_risk(rows) — the only non-test caller of the exported function | 1 |
tools/export_taxonomy.ce:136-142, 157 | withheld[] list and risk_all/risk_sites/withheld JSON fields | 10 |
KEEP: mcode_link.cm:2559-2568 (the exact_exports/exact_export_members
tally — reindent out of the if), :1219-1233 (rewrite_function’s
summary.complete gate on link.missing_member — complete is per-member
exactness, orthogonal to stone), and stats.finalized_sites (declared
:2488, computed :2604 as constant_members + function_members) — it counts
finalization, not the census.
Total deletable in the linker: ~330 LOC, leaving the 617-line provider recognizer intact.
3.4 Tests that pin the current contract
tests/mcode_link.cm (809 L):
| lines | case | verdict |
|---|---|---|
| 527-531 | comment: “An UNFROZEN export is finalizable only when the program itself proves the record is never mutated… a consumer that assigns to the import takes it away again” | DELETE — R5 repeals the premise |
| 532-552 | test_plain_record_return_finalizes_only_when_no_unit_can_mutate_it — the one test that pins the census | the clean arm (:534-536) survives as “a plain record return finalizes”; the mutating arm (:537-542, source p.f = function(x){…}) must be deleted or inverted — under R5 that consumer disrupts at runtime rather than silently de-finalizing |
| 547-548 | count_op(instructions(mutating), "function_ref") != 0 | deleted with the arm |
| 549-550 | census(mutating, "mutable_exports_withheld") != 1 | the only assertion on the census counter in the whole tree — DELETE |
Survive unchanged (provider-recognizer rows): :439-463, :466-489
(asserts finalized_sites at :486), :492-509, :511-525, :554-572,
:574-589, :591-604, :606-625, :627-636, :638-645, :647-656.
shop_tools/internal/diff_runner.cm (the differential oracle) carries a
parallel set: DELETE :135-139 (the mutated case), :170
(census(mutated)), :204 (check("consumer-mutated export stays generic", !finalized(mutated))). KEEP :102-134 and :199-203 (provider-side).
finalized_sites elsewhere — all KEEP, none census-specific:
tests/mcode_link.cm:486,546; shop_tools/internal/diff_runner.cm:76,152;
benchmarks/link_inline.ce:103,127; pit-shop/boot_cart.cm:135 (the press log
line). Frozen fixture data recording "finalized_sites":0 (pre-recognizer):
plans/archive/night-2026-08-04/rows/ship_linked.json:1,
plans/archive/night-2026-08-04/rows/ship_linked_join.json:1.
Stale prose to update (not code): plans/archive/night-2026-08-04/link-opt.md:563-604
(the census design section), :612, :654-658, :752-756.
3.5 What R5 retroactively justifies rather than deletes
pit-compiler/mcode.cm:110-115, 149-241, 3939-3968— the static export cache. The compiler already hoistsp.memberinto a hidden slot once, at thedef p = use(...)site: descriptors built at:190-221(gated onm.object_definition_fn_nr == 0), emission at:3946-3967doing oneis_recordguard (:3956-3957) then a batch ofloads into$linked_export_Nslots — with no invalidation on any later write top. So the compiler has already baked in “an export’s members never change”, unproven, on the static-link lane. R5 makes that sound retroactively. Not deleted by R5 — but it is the strongest argument that the linker’s 294-line census was proving something the toolchain had already assumed. (See §5 — this same code is the static lane R6/R7 puts in question.)pit-compiler/cross_unit_inline_experiment.cm:103-190—prove_export_impl/prove_export/prove_export_any(exported at:318): a straight-line abstract executor over module main that recognizes one literal export record and returns a member’s function id. The direct ancestor ofexport_summary, and a second, older, unreconciled export recognizer. Note:148-149foldsis_stoneon a candidate record tofalseand:167-169passesstonethrough transparently — both stay correct under R5. Merge candidate withexport_summaryregardless.pit-compiler/stone_records.cm:1-19(CP2b level-1 folding) — intra- function, requires an explicit dominatingstoneand no escape before it. An R5 stone at module main’sreturnlands after every body load, so this pass neither gains nor loses. KEEP.pit-compiler/notstone.cm:1-26(is_stoneguard elision from “provably never stone” facts) — its escape list already excludes any slot that isreturned, so an export record can never be claimed not-stone. Safe under R5, but this is the pass most likely to miscompile if that escape rule is ever relaxed — worth a comment pinning it to R5.
4. R4 — repoint reload, no frame migration
Confirmed: restart_frame exists ONLY in prose. Whole-tree grep
(rg 'restart_frame|restart frame', excluding build/) returns exactly two
hits:
docs/architecture/debugging.md:193plans/programs.md:136(the ruling itself)
No C, no pit, no test implements it. So R4’s deletion is a pure docs
rewrite. The block is docs/architecture/debugging.md:159-205 — the whole
## Hot reload section (## Debug information starts at :207). Specifically:
| lines | what it says | R4 verdict |
|---|---|---|
:159 | ## Hot reload heading | keep |
:161-165 | “A reload builds a new Mach pool … and installs it into a running actor”; stable {mcode unit hash, function ID} identity | keep — the identity half is S1, needed by both reload and the debugger |
:171-174 | “The link plan sets the granularity.” | keep, now restated by R7 |
:176-181 | “Installing it is a traversal, not a blind repoint. The swap creates one replacement function object per compatible old function and rewrites the actor’s heap and roots to point at the new ones.” | DELETE — R4 mutates the existing function objects in place (PitFunction.u.image = {binding, owner, outer_frame} + image_index, source/pit_internal.h:3115). No replacement objects, no heap rewrite. The “an active frame keeps running its old pool” half (:179-181) survives and becomes the whole story |
:182-186 | the second traversal at a later safe point for escaped-closure stragglers | DELETE — R4 accepts the straggler as a named residual (“a closure already handed out by an old-code frame keeps old code until it dies”, plans/programs.md:139-140). The “old pool stays pinned” sentence (:185-186) is kept, reworded — R4 keeps that residual |
:188 | “Two things decide whether an individual function can be swapped” | rewrite — under R4 the deciding question is export-record shape, not per-function swappability |
:190-195 | “The captured environment. … A layout-changing swap uses restart_frame, which unwinds and re-enters the function from the top; re-entering runs it again from its start, including effects it already performed” | DELETE ENTIRELY — the section R4 names by name |
:197-201 | “A unique replacement. … a development build disables the passes that would fold two distinct functions into one shared sequence” | restate under R7 — R7 says identity uniqueness is the reason the granular pool form is the reloadable one, not a separate lever, so this stops being a pass-level rule (“disable the folding passes”) and becomes a pool-granularity consequence. Note this is the same claim §5’s pass-profile question turns on |
:203-205 | “Rollback is the same traversal aimed at the prior pool” | restate — “the same traversal” no longer exists; rollback under R4 is a second repoint |
Missing and to be added, not deleted: R4’s refusal rule — “you cannot add
or remove functions on the exported record. A mismatch is a warning or an error
from the reload machinery — never a silent partial swap”
(plans/programs.md:126-128), and the dependency on R5 (:146-147).
Also to sweep for stale references to the old design:
docs/architecture/debugging.md:147-149(the$pgo“profile outlives the pool” claim is unaffected — keep).docs/shop/compilation.md#how-much-code-per-poolis linked from:170; check it does not repeat the two-traversal story.
New size: ~30 lines deleted, ~20 rewritten, out of a 49-line section. Risk: zero (no code changes).
5. R6/R7 — cart = one pool; the two link lanes; the pass profiles
5.0 The cart format today is N pools, one per module — R6 ALMOST kills the machinery
This is the finding to hold carefully, because R6’s own escape clause saves the code it appears to condemn.
Today a cart carries one pool section per deduped module:
pit-shop/cement.cm:731-744— the comment says it outright: “One section per deduped module, in the module table’s canonical order, so section i IS module i and the two never need a mapping table.” The loop is:738-743(sections[] = stone(m.bytes),section_of_hash[m.hash] = i).pit-shop/cement.cm:746-753—engine_section/walker_sectionlooked up throughsection_of_hash.pit-shop/cement.cm:761-782—plans_for(rows)/unit_row(hash): every plan UNIT row carries its own pool’s placed coordinates.pit-shop/cart2.cm:165-200, 279-330— the iterative ceiling-convergence layout over N sections: up to 5 probe rounds (while (ceiling_rounds < 5),:281) because the boot entry names sections by offset and cannot be encoded until they are placed, then a real layout pass and two “layout did not converge” refusals (:320-323,:327-330).cart/cart.cm:227(meta.sections),:282(section(cb, index)— “one pool’s verbatim bytes, read on demand”),:510(“pool sections: N”),:540(report.dedup.unique— “pools: N sections, …”),:732-786(the per-section verify sweep with its hash check and stamp cross-check),:640(“the cart carries no pool section at all”),:656-667(the header’s engine pool must equal the directory’s row).
Sizes: pit-shop/cement.cm 865 L, pit-shop/cart2.cm 467 L,
cart/cart.cm 926 L, shop_tools/cart.ce 287 L, cart/image.c 93 L,
source/cart_boot.c 397 L.
Under R6 the default collapses to one section — which deletes the reason
for section_of_hash, the plan-row coordinate mapping, the dedup report, and
most of the convergence loop. But R6 says the split survives “for esoteric
targets with ROM windows and paging (gameboy)”, and cart2.cm:160-161, 171-172, 196-199 already carries exactly that lever (section_window: “no pool may
cross one of these absolute image-window boundaries”). So:
Verdict: R6 makes the N-section path dead in practice but keeps it in the
format. Estimated ~150-250 LOC becomes unexercised-but-live across
cement.cm/cart2.cm/cart.cm. This is a “note it, do not delete it”
item unless the arc’s owner rules that the gameboy case is speculative enough
to delete now and re-derive later. Whichever way it goes, the press change
(one pool, whole-program) is real deletion and belongs to §5.1.
Genuinely dead already, adjacent: cart/cart.cm:188-217 decode_mach_stamp
keeps "pmac" (per-unit register-lane mach blob) and "pmcd" (mcode) arms —
diagnostics only, since :768-778 refuses both by name (“a cart section is a
pool”, ruled 2026-07-31). Under R8 the pmac arm has no producer anywhere
(pmac count is 0 post-L3). ~30 L, keep-as-diagnostic is defensible; flag for the
codename/residue sweep rather than this arc.
5.0b Docs that R6/R7 narrow
docs/shop/compilation.md:68-92— “## How much code per pool” presents a three-row menu (One unit per pool/One executable per pool/Several executables per pool,:74-77) plus “You can also partition anywhere between those points” (:79). R7 says there are exactly two shop forms and the cart is not a choice at all. Delete row 3 and the free-partition sentence; restate the cart as a fact.docs/shop/compilation.md:81-86— “A development build usually puts each module in its own pool … a shipped build usually merges” — restate as R7’s named default rather than “usually”.docs/shop/compilation.md:94-108— a second “## Hot reload” section repeating the swap-and-move-references story (“the running actor’s references are moved over”; “Two things bound what a swap can do. A function whose captured state carries meaning needs…”). Same R4 rewrite asdocs/architecture/debugging.md:176-205(see §4); do not fix one and leave the other.
5.1 The answer to the key question: the static lane has no surviving consumer
Today, nobody in production asks for the pool lane, and everybody gets the
static lane by default. The default is a chain of x == "pool" ? "pool" : null
guards that all fall through:
pit-shop/shop_build.cm:1788—var link_mode = msg && msg.link == "pool" ? "pool" : null, with the comment at:1785-1787saying it outright: “Nothing in the tree asks for it yet: the fleet is statically linked and stays that way until the fleet-from-pools landing.”pit-shop/shop_build.cm:898— “null (the default, and every lane in the tree today) keeps static linking.”pit-shop/boot_cart.cm:80-86— “The press compiles its units on the STATIC link lane (shop_build’slink_modeis null unless a caller asks for ‘pool’)… ause()is a context load, not an import op.”- Other fall-through guards:
pit-shop/shop_realize.cm:440,:1067;pit-shop/shop_build.cm:97;pit-shop/build_fleet.cm:69;pit-shop/builder_worker.ce:64,:73. - Nothing in
Makefile,scripts/,ci/or.gitea/mentions--link-modeorlink_modeat all.
The dev realize lane is static, not pool — this is the load-bearing answer:
prepare_simple_bundle is the entry for both dev realize and the press
(shop_build.cm:1788 is inside it), state.link_mode (null) is threaded into
every compile_unit of the closure walk (shop_build.cm:1426, :1451, :1492),
and no caller of realize_executable sets req.link — the two production
requests are shop_realize.cm:1757-1758 (compile_root_native) and :1800-1802
(publish_one_root), neither of which does.
So the only two things keeping the static lane alive are (1) the cart press
(pit-shop/boot_cart.cm:118 pool_executable, called at :245, :258 and
shop_tools/cement.ce:204) — which R6 flips to pool — and (2) the dev
realize lane — which R7 moves to one-pool-per-mcode-unit, i.e. the pool
lane’s import op resolving through MACH_LOAD_BOUND_MODULE.
Conclusion: under R6 + R7 the static lane has zero remaining production consumers.
5.2 DELETE — static-lane-only code
pit-compiler/mcode.cm — ~207 LOC, the bulk:
| region | lines | ~LOC |
|---|---|---|
s_static_export_defs / s_static_export_descriptors state + comment | 110-115 | 6 |
prepare_static_export_defs (the P8 export-cache hoist) | 149-243 | 95 |
static_export_ref / static_export_slot forward decls | 245-246 | 2 |
emit_linked_export_load | 1325-1327 | 3 |
emit_get_prop linked-slot arms | 1329, 1346-1348, 1362-1364 | ~8 |
emit_call_method linked-slot arm | 1868, 1908-1910 | ~4 |
static_export_ref body | 2909-2933 | 25 |
static_export_slot body | 2935-2938 | 4 |
gen_expr locals linked_export_slot/_ref | 3207-3208 | 2 |
.-access call site (collapses to plain emit_get_prop) | 3338-3339 | 2 |
method-call site (collapses to plain emit_call_method) | 3598-3600 | 3 |
def x = use() export-cache locals | 3873-3879 | 7 |
the static use() lowering arm — intrinsic_value(linked.name) + export-cache priming (:3928-3931 is the static use() itself; :3936-3969 is pure export cache) | 3928-3969 | 42 |
| “was not resolved for static linking” diagnostic | 3970-3973 | 4 |
prepare_static_export_defs(ast) call | 4608 | 1 |
KEEP mcode.cm:4601 (s_static_bindings = ast._static_bindings) — the pool
lane still reads it as evidence the request was answered (:4601-4606).
KEEP intrinsic_value (:510-518) and prepend_unit_intrinsics
(:520-541) — the static lane rides them, but they are shared with real
intrinsics (log, text, format, $receiver: :549, :562, :943, :2980, :3264, :3329, :3659).
pit-compiler/compiler.cm — ~33 LOC: claim_static_bindings (:1492-1520,
29 L), folded._static_bindings = … (:1605), analyze_result_from attach
(:221, :225), mcode_unit_result default (:1652). The record survives;
the exports sub-map it carries (:1498-1516) does not.
Shop / executable side — ~93 LOC: claim_static_bindings shop copy
(pit-shop/shop_build.cm:1264-1296, 33 L), add_context_bindings (:1299-1315,
17 L), static_bindings on the executable module row (:373, :888, :1054, :1665, :1686), module_context accumulation published as bundle.bindings
(:1806, :1872, :2118), add_provided_name (pit-shop/shop_source.cm:1923-1932).
Cart / boot seam — ~65 LOC, and this is R6’s actual seam:
pit-shop/cement.cm:392-416 (cart IMPORTS rows built from static_bindings,
25 L), :420-432 (cart NAMES rows built from exe.bindings, 13 L),
engine/boot_walk.cm:262-272 (the name-binding loop
env[irow.name] = values[irow.producer], 11 L) and :328-343 (16 L).
Tooling — ~149 LOC: shop_tools/compile.ce:1003-1009, :1031-1088, :1123-1126
(linked_plan / linked_ir, both named “static”, ~70 L);
shop_tools/footprint.ce:242-320 (module_context_keys mirror, ~79 L).
Lane-selection scaffolding that collapses to nothing: the link field in
mcode_cache_key (pit-shop/shop_store.cm:86-88, :102) becomes a constant, and
every threading guard (shop_build.cm:97, :898, :1788; shop_realize.cm:440, :1067; build_fleet.cm:69; builder_worker.ce:64, :73) collapses from a
two-valued lever to nothing.
Tests pinned to the static lane (must be deleted or rewritten):
tests/linked_export_cache.ce (131 L) + 3 fixtures
tests/fixtures/linked_export_{record,proxy,number}.cm;
tests/linked_export_plan.ce (81 L); tests/compile.cm:1010-1030, :1115-1128, :1129-1170, :1244-1270.
Spec text to rewrite: docs/spec/mcode.md:106-110 — “The default lane
resolves use() by static linking, lowering it to a context load … and emits
no import instruction at all.” (This is also plans/archive/todo.md:121-123’s “lane”
ambiguity — R7 settles it.)
Static-lane total: roughly 550-600 LOC of code plus ~210 LOC of tests. Highest-risk deletion in the whole inventory: it is the only lane anything in production uses today, so it can only go after the press flip and the dev realize move both land and are green.
5.3 What R6/R7 makes LIVE (not deleted) — the counterweight
pit-linker/mcode_link.cm carries ~1,200 of its 2,661 lines as import-edge
finalization machinery that is structurally dead today because nothing in
production emits an import op: the provider recognizer (:169-796, 628 L),
the import-holder census (:798-1092, 295 L — which R5 deletes, see §3.2),
import_link_type/rewritten_sites/slot_called_on_cfg (:1093-1184, 92 L),
rewrite_function (:1185-1283, 99 L), discovered_main_members/
member_references (:1284-1369, 86 L), and the finalize_edges gate
(:2513, :2522, :2536, :2546, :2575, :2605, :2642).
Same on the C side: source/mach_pool.c:4143, :4324, :4563-4582, :4590-4617;
source/mach_vm.c:5417-5438 (MACH_LOAD_MODULE_RESULT), :5441-5500+
(MACH_LOAD_BOUND_MODULE); the module_imports relocation sidecar
(source/mach_pool.c:877-883, :4578).
Reconciliation note: §3 and §5 disagree in appearance only. §5 classifies
the census as pool-lane (so it survives the static lane’s death); §3
classifies it as R5-obsolete (so it dies to the stone ruling). Both hold:
the census is pool-lane machinery that R5 deletes. Net, mcode_link.cm
loses ~330 LOC and the remaining ~900 goes from dead to live.
5.4 source/mach_pool.c:4590 — function_ref lowering pays off, it does not die
:4590-4617 (28 LOC, contract comment at :4583-4589) lowers the linker’s
["function_ref", dest, provider_unit, function_id, member] into three mach
instructions:
EM_ABX(MACH_LOAD_MODULE_RESULT, s.module_scratch, provider_unit) // :4612
EM_ABX(MACH_LOADK, s.key_scratch, cpool(member)) // :4613
EM_ABC(MACH_LOAD, ref_dest, module_scratch, key_scratch)// :4614
It reads the provider’s already-initialized stone export record rather than
manufacturing a fresh closure (the exported function may close over its module
frame), and unlike member_import at :4578 it does not set
s.module_imports[…] — a finalized same-program edge needs no fragment-linker
relocation (:4588-4589).
It is purely pool-lane: function_ref is produced only by
pit-linker/mcode_link.cm:1242 inside rewrite_function, which fires only when
a slot traces back to an ["import", …] op.
The consequence R6 must own: MACH_LOAD_MODULE_RESULT indexes
state.base + bx into the actor’s one results array
(source/mach_vm.c:5433-5436), so a function_ref is resolvable only if the
provider is in the same pool (recorded at
plans/archive/night-2026-08-04/press-switch.md:230-238). Under one-unit-per-pool it is
not. R6’s “a cart is always ONE mach pool” is exactly the precondition that
makes this instruction resolvable. :4590 is code that starts working when
R6 lands, not code that dies. Its streamline/structured registrations
(pit-compiler/structured.cm:29, :56, :125, :265, :1021;
pit-compiler/stream_ir.cm:44; pit-compiler/streamline.cm:373, :3111, :3156, :3172) all stay.
Corollary for R7’s shop default: “one pool per mcode unit” cannot use
function_ref finalization at all — cross-pool edges must stay
MACH_LOAD_BOUND_MODULE. So the reloadable form is also the unfinalized
form. Worth stating in the plan; it is the mechanical reason R7’s granularity
lever is also the optimization lever.
5.5 The pass profiles — dev is mostly already gone
pit-compiler/passflags.cm (221 L). Profile machinery proper is
:88-108 = 21 LOC; the rest is the switch registry (:31-58), aliases
(:60-64), helpers, parse (:145-166), the env diagnostic read
(:170-188) and resolve (:194-219).
Exactly what differs, verified line by line:
:88 ALL = all 25 switches on
:89-91 DEV_UNIT = ALL minus {inline, licm, int_ranges, sroa, forward, join_types, switch}
:92-94 DEV_LINK = ALL minus {join_types}
:95-97 SHIP = ALL minus {join_types}
:98-101 SMALL = ALL minus {join_types}, then inline = false
Two real facts (dev/unit turns off 6 passes at :90; small turns off
inline at :100) and two dead ones:
DEV_LINK(:92-94) is byte-identical toSHIP(:95-97) — same construction, same set. Half of “three profiles” is already one profile.join_typesis off in all three at both stages (:90, :93, :96, :99), so it is not a profile axis at all — the linked streamline turns it on through its own link-plan switch (pit-linker/mcode_link.cm:68-71), as the header says at:12-18.
No production caller ever passes dev or small. Every producer defaults
to "ship": pit-shop/builder_worker.ce:75-79, :134-138;
pit-shop/build_fleet.cm:72, :85; pit-shop/shop_build.cm:98-99, :123, :1798-1799; pit-shop/shop_realize.cm:388-392; literal "ship" at
shop_realize.cm:1758, :1801 and shop_build.cm:2552, :2776;
pit-shop/boot_cart.cm:45 (link-plan vocabulary). Every literal "dev"/"small"
compiler_profile in the tree is in tests/ or plans/. The only compiler-side
reader of PROFILES is pit-compiler/streamline.cm:20, :36.
The profile name DOES feed both derivation caches — deleting the value is free, deleting the field is not:
K_mcode—pit-shop/shop_store.cm:97-105, specifically:103("\n" + text(d.compiler_profile || "ship") +), documented at:95-96. Constructed atpit-shop/shop_build.cm:983-987,pit-shop/shop_fetch.cm:693-697,pit-shop/derivation_status.cm:35-39.- The realization context hash —
pit-shop/shop_realize.cm:416, with the landmine comment at:410-415: “a realization built with the dev pass set must never satisfy ship. Keep the profile in the realization salt as well as each K_mcode key.” Threaded at:1058, :1436, :1458. The regression history is written at:367-387— adevrealization default meant no seeded resident could match its own realization: coldps0.42 s → 21.0 s, +36 % store bytes.
Because of the || "ship" default, every existing key is byte-identical
after deleting the dev profile — no cache invalidation. Dropping the field
itself does re-key everything; don’t.
What breaks if dev goes:
| what | file:line | note |
|---|---|---|
| the definitions | passflags.cm:89-91 (DEV_UNIT), :92-94 (DEV_LINK), :104 (PROFILES.dev) | delete, 8 LOC |
header doc examples naming dev | passflags.cm:5, :7, :9 | doc edit |
resolve returns null for an unknown profile | passflags.cm:204 | a stale --compiler-profile dev now returns null → streamline gets no switch record. Needs a named refusal, not a null. |
the link-plan profile vocabulary, which separately names dev | pit-linker/mcode_link.cm:104-108 (valid_link_plan accepts dev|ship|boot; dev forces granularity: "module"), consumed at :2424-2425 (plan.profile == "boot" ? "ship" : plan.profile → passflags.resolve) | a dev link plan would resolve to null. The one real breakage in shipped code, invisible today only because no production caller builds one. And R7 makes granularity the whole lever, so this vocabulary is exactly what R7 is about. |
| profile validator | shop_tools/internal/compiler_perf.cm:456-459 | drop the dev arm |
| CLI flag text | shop_tools/shop.ce:21, :159-160; shop_tools/compile.ce:1139 | says (dev|ship|small; default ship) |
test_join_types_profile_switch | tests/compile.cm:3734-3754 (asserts at :3735-3736, :3746-3747) | fails — resolve({profile:"dev"}) returns null |
key_checks “compiler profile did not move K_mcode” | tests/store_freshness.ce:83-86 | passes if the field stays |
profile_cache_checks | tests/store_freshness.ce:52-112 (helper), :225-278 — esp. :231, :237-240, :261-263, :273, :277 | fails — compiles the same fixture under both profiles and asserts distinct content hashes; unsatisfiable |
resident_stability_checks | tests/store_freshness.ce:300-323, esp. :310, :321-323 | assertion becomes vacuous; rewrite or delete |
| profile realize round-trip | tests/store_freshness.ce:325-340 | passes msg.compiler_profile = profile |
DEV_PLAN link plan | tests/mcode_link.cm:12, :228, :261-266, :518 | :261-266 asserts dev-vs-ship produce different instruction shapes (load vs function_ref) — this is the link-plan dev, and R7 keeps granularity while retiring passes |
| docs | docs/architecture/build-and-artifacts.md:195-217, esp. :200-202 ("dev is the per-unit rebuild profile") and :212-214 ("dev skips the expensive unit-stage inline, LICM, integer-range, SROA, field-forwarding, and switch passes; its link-stage settings remain all-on") | rewrite — this is where R7’s “dev differs by pool granularity, not passes” lands |
| fuzz comment | shop_tools/fuzz.ce:29 (“OFF in every shipped profile (dev, dev/link, ship, small)”) | reword |
Verdict on the dev pass-profile: DELETABLE, and cheap. ~8 LOC of definition, ~21 LOC of profile machinery, 2 shipped source files, 2 CLI strings, 1 validator, 4 test blocks across 3 files, 1 doc section, and zero cache invalidation. It matches the switch report’s measurement (dev within 0.32 % of ship while compiling slower).
small should collapse too. passflags.cm:100 (SMALL.inline = false) is
the entire difference from ship. If profiles collapse, small is better
expressed as {profile: "ship", passes: {inline: false}} — which resolve
already supports at :207. That leaves one pass profile, and R7’s dev/ship
distinction becomes purely the link plan’s granularity.
6. Residue these rulings subsume
6.1 PIT_POOL_PAYLOAD stale state block — CONFIRMED STALE, delete
plans/archive/todo.md:93-94 says “the lever is read by nothing. Delete it.” Verified,
and it is larger than todo.md says:
pit-shop/mach_lower.cm:42-79— the whole “STATE, 2026-07-29: the pool lane is BUILT AND NOT ON.PIT_POOL_PAYLOAD=1turns it on” block, including the RUNG-2 measurement table and thecli/pgo_flagsgap note. 38 comment lines. todo.md names:42-55; the block actually runs to:79.- The lever is genuinely gone from code:
pit-shop/terminal.ce:2181-2183is nowfunction pool_payload_wanted() { return true }with the comment “FLIPPED 2026-07-30: pools are the only payload the shop cements”. Nogetenv/os.envread ofPIT_POOL_PAYLOADsurvives anywhere (rg PIT_POOL_PAYLOADoutsideplans/hits only comments). pit-shop/mach_lower.cm:53-56(the “recipe is./pit down && PIT_POOL_PAYLOAD=1” instruction) is actively misleading — it tells a reader to set a variable nothing reads.- Adjacent stale comments naming the dead lever, same sweep:
pit-shop/mach_lower.cm:316-318,shop_tools/test.ce:149,tests/hook_image_name.ce:15. - R6/R7 subsume this: once “a cart is always one mach pool” and “the shop is one pool per unit or per executable” are the ruling, there is no “pool lane off” state to describe.
NOT deletable in the same pass: pit-shop/terminal.ce:2418-2432 — the
--pgo_collect refusal. Its own comment says “at which point this whole block
deletes”, but the trigger is L2b’s drain, which plans/programs.md:263-265
explicitly carries forward untouched. Listed in the table as row 15 so it is
not swept by mistake.
6.2 allow_compile — CONFIRMED DEAD, and it takes the whole handler with it
plans/archive/todo.md:112-114 asks for “one look and a ruling”. The look:
pit-shop/policy.ce:63—allow_compile: trueinCONFIG.policy.pit-shop/policy.ce:234-240—function allow(kind);:239isif (kind == "compile") return p.allow_compile != false.allow()has exactly three call sites, all insidepolicy.ce::370(msg.type == "allow"),:378(msg.type == "native"),:483(msg.type == "executable_policy").rg "'allow'|\"allow\"" --glob '!plans/**' --glob '!website/**'finds exactly one hit outsidepolicy.ce—http/hpack_tables.cm:89, the HTTPallowheader name. Nothing in the tree ever sends{type: "allow"}. Sopolicy.ce:369-376is an unreachable handler, andallow_compile(:63) andallow_static(:61) are only reachable through it.- Same for
executable_policy(:482) and thereforeallow_mach(:62,238): the only tree hits are the handler itself andtests/executable_policy.ce:18(which is atest_resultsend, not a request).shop_build.cmbuilds a localcompatibility_policy_decision(:2086-2096) instead of asking. - Same for
type: "native"(:378) and thereforeallow_dylib: the two hits fortype: "native"are a record field inshop_build.cm:1392and a fixture intests/pgo.cm:46, not sends. - The one caveat that keeps this honest:
pit-shop/clerk.ce:1077-1085is a generic passthrough —{type: "policy", request: {…}}forwards any record to the policy actor. So an out-of-tree caller could reach{type: "allow", kind: "compile"}. In-tree the passthrough is used only for{type: "configure"}(tests/cli/policy_veto.ce:56, :150, :173), and the shop’s own policy traffic is{type: "environment"}(clerk.ce:600),{type: "catalogs"}(shop_source.cm:2947) and{type: "configure"}(clerk.ce:1661). So: no in-tree sender, reachable only through the documented passthrough — which is what makes it a deletable knob rather than a dead one.
Verdict: the diary’s record was right and the deletion was incomplete — the
knob survived because the handler survived. Delete policy.ce:369-376,
:63, :61, and the "static" / "compile" arms of allow() (:236, :239).
~10 lines. Keep allow_dylib/native (:60, :237, reached from :378) and
allow_mach (:62, :238, reached from :483).
Why a ruling subsumes it: R3a says the shop degrades by actor
availability — “it cannot compile, because there is no mcode compiler actor
in this cart”, refused by name. A allow_compile: false policy bit is the
other model (a knob that says no while the compiler is present), and R3a
replaces it. R8 does the same for allow_mach: once mach is never shipped,
“may this shop accept a mach payload” stops being a policy question — flag
allow_mach (:62, :238, :483) as a second-round delete once §2 lands.
6.3 pit.bootstrap.target@1 — the open ruling
plans/archive/todo.md:96-100 records the remaining ~140 spec lines as needing a
ruling. Located: docs/spec/artifact-formats.md:219-357 ("### Target bootstrap
configuration" through the pit.bootstrap.provider.catalog@1 rows and the
“implementation hash is not a complete bootstrap target” close at :357).
Confirmed no producer and no reader in code — the only non-doc hits are
plans/archive/transition-diary.md:796,933,943,1399,1524, which record its
constructor as deleted (T014, audited 2026-08-01).
R8 bears on it: the record is per-target bootstrap configuration for locally finalizing a portable bundle. Under R8 the shipped cross-target artifact is “the C binary plus its fused mach blob”, built by forge — which does not read this record. That makes it dead design, not pending target truth. Recommend: delete, citing R8. ~140 doc lines, zero code.
6.4 Residue items these rulings do NOT cover (leave in plans/archive/todo.md)
plans/archive/todo.md:101-104— the second codename pass (CAKE1/FRESH1/SHOP1/…).plans/archive/todo.md:105-107— stale “pit.cart@1 trailer” comments atsource/cart_boot.c:344andcake/manifest.cm:376.plans/archive/todo.md:108-111— T020’s skipped meson parity gate.plans/archive/todo.md:115-117— the do-not-delete note onruntime_code_count/code_cache_*(pinned at zero bytests/runtime_arena_counters.cmas L3’s observable). Keep-list item.plans/archive/todo.md:118-120—docs/shop/c-modules.mdnames nonexistent recipes.plans/archive/todo.md:121-123— the “lane” glossary ambiguity. R7 resolves the compilation half of this (pool-per-unit vs pool-per-executable is now the named lever), so the glossary sentence should be written from R7 rather than frommcode.md:104-108.
7. Keep-list (do not delete under any of these rulings)
| thing | why |
|---|---|
local K_mach caching in the shop store | R8 kills sharing, not local cache — a machine must cache the mach it lowered for itself |
the forge cross-build lane and boot/targets/** mach | R8’s one legitimate cross-platform product is “the C binary plus the heavily fused mach blob bound into it” (plans/programs.md:177-179) |
pool content-hash identity (image_hash on frames, mach_origin_key) | plans/programs.md:296-308 (§7.2) keeps identity while deleting sharing — it is the debug key |
pit-shop/mach_lower.cm:38 foreign-target refusal | plans/programs.md:258-260 — “correct and permanent for every other path” |
{mcode unit hash, function ID} stable identity | S1; both R4’s repoint and the debugger need it |
PIT_INPROCESS | R1 names PIT_APP only; cake/forge.cm:1191 uses it to press the seed, and 6 test files depend on it |
runtime_code_count / code_cache_* counters | plans/archive/todo.md:115-117, pinned at zero by tests/runtime_arena_counters.cm |
pit-shop/terminal.ce:2418-2432 PGO refusal | its deletion trigger is L2b, carried forward untouched by plans/programs.md:263 |
pit-compiler/passflags.cm semantic lowerings | passflags.cm:22-25 — insert_stone, lower_concat, lower_concat_space, compress_slots “define valid mcode output and are never switchable” |
pit-shop/shop_store.cm — all 11 K_mach functions | §2.4: every one is local cache or §7.2 identity; zero deletions |
pit-shop/courier*.cm / courier.ce | §2.3(a): actor messages only; carries no artifacts of any kind |
advertise / serve (pit-shop/source_upstream.cm) | §2.3(b): the key derivation is the kind filter — pit.source.file@1 only |
boot/engine_lite.mach | §2.2: forge build input, cemented into root.cart and .incbin’d into the binary; never shipped loose |
source/mach_pool.c:4590-4617 (function_ref lowering) | §5.4: pool-lane only, starts working when R6 lands; it is the payoff, not the casualty |
pit-linker/mcode_link.cm:159-796 (the provider recognizer, 628 L) | §3.1: stone proves immutability, not identity; finalization still must know which function each member holds |
pit-compiler/mcode.cm:510-541 (intrinsic_value, prepend_unit_intrinsics) | §5.2: the static lane rides them, but they are shared with log/text/format/$receiver |
8. What a ruling ALMOST kills but doesn’t — and why
These are the traps. Each looks deletable from the ruling’s text and is not.
| thing | which ruling seems to kill it | why it survives |
|---|---|---|
cart N-section machinery (cement.cm:731-782, cart2.cm:279-330, cart/cart.cm:227,282,510,540,732-786) | R6, “a cart is always one mach pool” | R6’s own next sentence keeps the split “for esoteric targets with ROM windows and paging (gameboy)”, and cart2.cm:160-161,171-172,196-199 already implements exactly that (section_window). ~150-250 L goes unexercised but live. Needs a separate ruling to actually delete |
boot/targets/linux/{root.cart,engine_lite.mach,boot.qop} (22.8 MB) | R8, “mach blobs are neither shared nor shipped” | It is mach crossing machines over Git — but it is the cold bootstrap floor for a Linux box with no pit, consumed by 8 CI steps (scripts/use_ci_seed.sh + both workflows). R8 as written deletes CI. Needs a named second exception |
source/mach_pool_fixture.h (96,815 b, ten cross-pressed profiles) | R8, “cross-target mach lowering outside the forge path” | It is the cross-emitter gate for source/mach_pool_test.c, regenerated on every make smoke (Makefile:313-334). R8 as written deletes make smoke. Needs a third (“test fixtures”) exception |
pit-shop/mach_lower.cm:196-218 (the foreign-target refusal) | it looks like the cross-target machinery R8 removes | plans/programs.md:256-260 rules the opposite: it is “correct and permanent for every other path”. KEEP, and keep tests/cross_target_lowering.ce with it |
pit-linker/mcode_link.cm:798-1092 (the import-holder census) | §5 classifies it as pool-lane, so it survives the static lane’s death | …but R5 deletes it anyway, for a different reason (stone makes the proof unnecessary). Both classifications are right; the census dies to R5, not to R6/R7 |
PIT_INPROCESS | reads like the same environmental mode-inference R1 abolishes | R1 names PIT_APP only. cake/forge.cm:1191 uses PIT_INPROCESS to press the seed, and 6 test files depend on it. It becomes “the declaration says in-process”, but the override must survive for tests that cannot re-forge a cart |
pit-shop/terminal.ce:2418-2432 (the --pgo_collect refusal) | its own comment says “this whole block deletes” | …but the trigger is L2b’s drain, which plans/programs.md:263 explicitly carries forward untouched. Do not sweep it with the PIT_POOL_PAYLOAD residue next door |
platform/daemon-stub/ (24 L) | R9, “optionality must move from the recipe to the product” | It is the definition of “this target has no daemon”. Under R9 it becomes the default rather than a selected file — a relocation, not a deletion. Deleting it before the per-product lever exists breaks ten recipes |
pit-shop/target_catalog.cm’s catalog itself | R8 kills executable sharing | Only the multi-target variant dimension dies. A single-object catalog is still how a locator names its realized executable locally, and pit.catalog.variants@1 appears in boot.qop’s own manifest — so the seed writer must be changed in the same landing or the seed stops parsing |
pit-compiler/cross_unit_inline_experiment.cm:103-190 (prove_export*) | R5 makes export immutability a compiler fact, so a second recognizer looks redundant | It is a merge candidate with export_summary, not a deletion — and merging it is orthogonal to R5. Flag it; don’t fold it into this arc |
9. Sequencing notes
- §7.3 (mcode is host-rep-tainted) blocks the positive half of R8. Until
tokenize.cm:281-284/fold.cm:118stop folding in host arithmetic, “mcode is the only shared artifact” ships a nan64-limited artifact to a nan32 target — and deleting mach sharing first removes the only currently correct way to get a rep-exact payload onto a narrow-rep target. Fix 7.3 before rows 9-11, not in parallel. - R5’s deletion hinges on the compiler emitting a real
stoneop at the module main’s return, immediately before it (§3.0). A runtime-side freeze makes row 12 worth zero lines. - Row 13 (the static lane) can only land after BOTH the press flip (R6) and
the dev realize move (R7) are green. It is the only lane production uses.
Land it as the last thing in the arc, behind
make check-all. - Row 14 (the daemon split) needs R9’s per-product lever designed first.
The recipe bit and the platform-package file are a contract enforced by
tests/recipes.ce:194-196; a per-product lever has to replace that test, not drop it. - The Playdate ship lane (
--payload mach) needs a target-side presser before row 9 can land. Nothing in the tree presses on the Playdate today.
Source: plans/proposal-notes/deletion-inventory.md