Archive — history, not state. Kept for its reasoning and its evidence; its plan is closed.
The finalization arc — THE live plan (ruled 2026-07-29)
The one plan in flight. The target design lives in docs/ — normative pages for this arc:
docs/architecture/principles.md (the golden rules), build-and-artifacts.md (“Pit
emits, C reads” + “Finalizing for a target”), boot.md, cartridges.md,
docs/glossary.md (the ratified vocabulary), docs/spec/artifact-formats.md (“One
producer, many profiles”). Historical rulings and the superseded plans are in
plans/archive/ — read them for reasoning, never for direction.
The rulings this implements (John, 2026-07-29)
- No PORTABLE cement (“no cross-target cement” was a language error). Cement is always for one named profile; cross-pressing carts for other platforms is the expected, ordinary act. Portability ends at mcode.
- Pit emits everything; C only reads. C never writes what Pit can write; C reads only what boot needs before Pit runs (fixed header + nota metadata); every C-read format has one writer, in Pit, exercised by CI. The authority is the per-arm C reader/executor, gated by pressed-fixture boots (replaces the byte-identity mechanism, which retires with the C emitter).
- Carts carry pools; mcode never boots. M1 repealed;
pmcdandpmacboth die. An ABI bump = the old binary presses the new cart (a new ABI is just another profile). - Actors are the unit of code-stripping; the compiler is never shipped. A live fleet is a hard prerequisite for all compilation, forever. (“This is ideal” — John.)
- One execution lane (
pmp1). kim8’s stone layout is a future profile arm of the emitter (built when a recipe adopts kim8 — today none does); the kim8 register lane in C deletes. Native/AOT stays parked (edict 7) and conforms to one-lane when its pass comes. - Formats: metadata is nota; pools are binary inside (O(1) row indexing — the one sanctioned magic); one fixed outer header on boot media, stamp checked by memcmp before any parse; pool sections at aligned offsets, never inline nota blobs. qop stays — a legitimate format, John 2026-07-29. Daemon wire is JSON lines and stays.
- Terminology ratified — see the glossary: pool / image / stamp / profile / manifest /
realization / cement / press / bundle. Dead words:
pmac,pmcd,pit.executable@1.
The bootstrap contract (replaces M1’s protection — enforce, never work around)
- Every commit carries a matching (source, cart) pair;
make seedverifies. - Ordinary dev: the running system re-presses its own cart.
- An ABI/profile change lands WITH its old-binary-pressed cart in one landing. A commit whose cart stamp mismatches its source profile is a broken commit, full stop.
- Catastrophe (no working binary): the cold floor (
boot/manifest/bootstrap.sh) builds the binary; by rule 1 the committed cart matches it. Wedged mid-crossing: check out the last green pair and press forward. - Foreign targets only ever receive pressed carts; they never self-press.
Landings, in dependency order
Serial where noted; every landing merges by the drill (bottom of this file) and lands green.
L1 — the profile-parameterized emitter. RULED 2026-07-29 (John, after the L1 recon):
(a) the per-rep value-encoding table is C, capability-gated — the five ABI-only rep
TUs compile only into builds whose recipe grants the press capability; each rep header
stays the single writer of its encoding (see build-and-artifacts.md “Pressing is a build
capability”). (b) The gate is validate-everywhere, execute-where-endian-matches: one
committed fixture table with a row per profile the emitter knows, every arm validates its
row, execution proof for foreign-endian rows belongs to L6 on real runners. Byte order
belongs to the LINKER, not the fragment. stamp.endian currently never reaches the build
(pit_profile.h derives from the toolchain) and arm_identity_lint misses it — fix the
lint in-lane. shoplib/mach_pool_emit.cm + the lowering
take a full profile record as an argument; every target fact (endian, value width, object
header width, text layout, hashes) is read from it, none compiled in. Arms needed now:
endian (be targets), value width (nan32/fixed32 constant words), object header 32/64.
utf32 stone stays the one text arm until a recipe adopts kim8. Wire the new gate:
check-arms presses a fixture pool per arm with the emitter and BOOTS it under that
arm’s C (extends the existing tools/pool_fixture.ce + mach_pool_asm_test.c pattern —
which flips from “compare bytes with C’s emitter” to “validate and run what Pit pressed”).
L2 — close the pool-lane gaps, flip the lever. Then PIT_POOL_PAYLOAD=1 becomes the
only behavior, the env lever deletes, reseed. (Lever reads happen in the DAEMON —
./pit down before any measurement.) State after the 2026-07-29/30 landings:
tests/log_stack.ce,profile_report— CLOSED already, green under the lever. The lane-aware stack walk (40db3673) had fixed both; nothing to do.cli/debug_tool— CLOSED. One assertion, and its owncloses:text was right about the key and wrong about the producer:mach_pool_emit.cm’semithas never returned an origin map,origin_mapis a separate export nothing in the build path called, andmach_lower.cmread alinked.originfield that does not exist — sooriginwas silently null on every unit and no map was ever stored. Fixed by calling the producer, keying the sidecar by the pool CONTENT hash (the only hash a live frame can name; the store address is unreachable from a frame), and fetching it inshop_tools/debug.ce. Lever-on 19/19, lever-off 20/20.cli/publish_subscribe/bundle_ingest— CLOSED, and were never a flake. Carried for weeks as “~1 in 6, pre-existing”, the rate was shop state, not chance: on a fresh shop the refusal was deterministic. The bundle signature covered a RE-DERIVED encoding — publish signednota.encodeof a record it built by insertion, ingest re-built the same field set from the decoded manifest and re-encoded it — and a nota record is an unordered collection whose encoded key order belongs to the record OBJECT, not to its key set. Measured 20/20 disagreement on real published manifests, 15–35% on synthetic records. The signature now covers the/manifestmember’s bytes as written and rides beside them in a/signaturemember; bothmanifest_signed_fieldsfunctions are gone. The rule the docs now state (docs/spec/nota.md,packages-and-distribution.md): nota bytes encode a value, they do not identify one — sign or hash bytes you kept, never bytes re-derived from a decoded value. Worth a grep for the same shape elsewhere;shoplib/canonical.cmexists because this bit once already.cli/pgo_flags— RULED by John 2026-07-30: refuse now, build later. The refusal half is LANDED:--pgo_collectunder the pool lane refuses by name inpit-shop/terminal.ce, beside the--aotrefusal it copies, andtests/cli/pgo_flags.cegrew a lever arm asserting it (lever-on 1/1, lever-off 1/1). What it prevents, precisely: the pool lane registers no PGO session, somach_pgo_snapshot_copyreturns “no session”,pgo_capturesaves an EMPTY profile and thenpgo_snapshot_finishrefuses to commit a generation of 0 — the user saw “PGO profile saved but collector commit failed” and a zero-site profile in the store. The drain is landing L2b, still open: C returns pool coordinates, pit joins the origin map (the two lanes disagree about what a site IS — a pool’s u64stable_site_keycannot reconstruct{unit_hash, function, pc, kind}); PGO_SITES emission lands with it (neither producer writes the section today). Touchesmach.c,development_shop.ce,pgo.cm, the emitter. The refusal deletes when the drain lands, along with the lever arm in the test.
L2b — the pool drain. THE IDENTITY HALF IS LANDED; THE DRAIN IS NOT. The fork below was RULED by John 2026-07-30 in favour of the docs, and the identity change shipped with it. Do not re-derive the map — build from it.
THE DRAIN IS BLOCKED ON A DEEPER WALL THAN THIS SECTION KNEW — MEASURED 2026-08-01.
Everything from step 3 down assumed a working register lane for the pool lane to be joined
to. There is none, and there has not been since D5 (d27b0701, “one payload form, and
the register lane goes”, 2026-07-31 — the commit before the one this was attempted on). D5
deleted pit_load_mach_code, and with it mach_pgo_runtime_load: the only production
constructor of a PitPGORuntimeCode, the only caller of mach_runtime_meta_ensure, and the
only production caller of mach_pgo_session_attach. Measured on da6ffa0c:
mach_pgo_session_attachhas two call sites, both insidepit_mach_test_pgo_snapshot_protocol(source/mach_vm.c:1235,1258) — a C self-test that builds its sessions by hand.mach_runtime_meta_ensure(source/mach_vm.c:295) has zero callers, and nothing anywhere assignsruntime_meta->pgo.mach_pgo_runtime_loadandpit_load_mach_codeare gone from the tree entirely.
So PGO collection is dead on both lanes, not merely unbuilt on the pool one: no actor
registers a session, allocates a collector, or increments a counter. The refusal in
pit-shop/terminal.ce now understates the situation rather than overstating it — its text
blames the pool lane for a hole that is runtime-wide.
Nothing caught it, and that is the part worth keeping. tests/pgo_executable.ce is the
only end-to-end PGO test and it never drains a counter: run_profile_roundtrip (line 177)
synthesises its observations (executions: 64) and feeds them back through
prepare_from_file, so it covers build, store and consume while the runtime half is absent.
A drain test that runs a program and asserts a non-zero count is the gate this family never
had, and it should land with the counters rather than after them.
What this does to the sequence below. Step 3 does not become unblocked when the identity
road arrives — it becomes a rebuild of the collector-construction path D5 removed, now
against a pool binding rather than a PitCodeRegister (PoolBinding,
source/pit_internal.h:391, is the natural home, and its “immutable for one image
generation” comment is a stated invariant a counter array would change). The
session-identity blocker sits on top of that unchanged, and L3 phase 2 is still outstanding.
Two walls, in series, not one.
Step 2 therefore stayed in the stash. The B-without-C rule bites harder than when it was
written: the reader is not missing, it was deleted. And the hazard is live rather than
hypothetical — --pgo_auto/--pgo_from are not refused under the pool lane and do
produce PGO site descriptors (MACH_PGO_FIELD_LOAD_PIC via ml_pgo_site,
source/mach_pool.c:2633), so an emitter landing alone would start setting the
instrumented header bit on consume-mode pools whose sites the VM ignores. The stash was
applied, reseeded and smoke-tested to prove it still reconciles — it auto-merges onto
post-L3 mach_pool_emit.cm (the only drift is one comment line, mach.c → mach_pool.c) —
and then withdrawn. It is re-derivable from the shape spelled out in step 2 if lost.
Landed. A site is {mcode, function, site} — the mcode site id, exactly as the origin map
names it (pit-compiler/pgo.cm site_key). C composes no identity JSON at all any more
(mach_pgo_site_identity and its mid-string splice are gone); pit writes the identity and the
runtime carries it as opaque constants. docs/spec/artifact-formats.md “PGO profile” now
states the real row, which had been stale in three further ways (it omitted kind,
immediate_failures and targets, and spelled the target list values).
The one finding worth carrying forward, because it cost a build-and-diagnose cycle. The
obvious shape — store each site’s whole identity — does not work. It repeats a 71-character
mcode hash once per site, and 512 selected sites overrun the 256 KiB runtime-wide PGO
budget. It failed order-dependently: collection was green on a fresh daemon and red once
tests/pgo.cm had run first, because the budget is runtime-wide, not per actor. And it
surfaced as could not snapshot PGO session from a mach_pgo_snapshot_reserve failure —
nowhere near the cause. The identity is therefore handed down in two halves
(site_identity_prefix per code body, site_identity_suffix per site) and concatenated at
drain time, where the cost is transient. Anything that adds long-lived per-site bytes should
expect to meet this budget the same way. test_pgo_site_identity_halves_compose pins the
property that makes concatenation correct (site sorts last), because C cannot notice if a
pit-side edit breaks it.
Still to build — the pool drain proper. In this order, and note that B must NOT land without
C: an emitter writing PGO_SITES and setting the header’s instrumented flag while C still has
no counters would make a pool claim to be instrumented and still run its sites generically,
which is the one wrong answer.
Fragment carries PGO descriptors. LANDED.
functions[]rows carrypgo_sites: [{pc, semantic_pc, kind}], present only on an instrumented lowering, in the order the VM names them.pcneeds no translation: the fragment copies each function’s instruction block verbatim, so a lowering-time pc already indexes it.shoplib/mach_pool_emit.cmwrites the section. WRITTEN AND HELD BACK — it is in a stash on the agent worktree, not in any commit, because of the blocker below. Shape: rows grouped per function in fragment order,first_pgo_site/pgo_site_counton the FUNCTIONS row,counter_index= the pool-global row index, the tenth section placed only when rows exist (an always-present empty section would change every ordinary pool’s bytes and break the byte-identitymach_pool_asm_test.cholds), header bit0 DERIVED from “there are rows” rather than requested, andinstrumentedfolded intoderivation_textso two pools over the same units that differ only in instrumentation are different pools.C builds a per-binding counter array at bind time and
mach_exec_pgostops returning NULL forMACH_EXEC_IMAGE; the VM’s ordinal becomesfn.first_pgo_site + Bx. BLOCKED TWICE — RAISED, NOT GUESSED (2026-07-30, and again 2026-08-01). Read the 2026-08-01 block above first: D5 has since deleted the collector-construction path this step was going to mirror, so it is a rebuild and not a port. The 2026-07-30 blocker below is still true, and still sits on top of it.My own map said “the session identity arrives at BIND time from the realization”. It does not, and cannot today: the production pool bind path never sees a realization. The chain is
start_plan.c plan_run_unit→pit_plan_run_window→plan_run_pool→pit_private_image_provider_resident_function(ctx, bytes, size, placement_id, artifact_hash, owner_results, entry_index)— grepsemantic_hashandcompiler_saltacrosssource/start_plan.candsource/image_provider.creturns nothing. The register lane never had to solve this because the identity rode INSIDE the per-unit mach blob (mc->pgo_semantic_hash); a pool cannot carry it, being content-addressed and shared.$image.binding, which does take a request record, is called from tests only.So: where does a pool-lane actor learn the PGO session identity it reports into? Three candidates, none clearly better on the evidence I have:
- (a) The start plan carries it. Natural — the plan is built per realization, which knows its semantic hash. But it is a plan FORMAT change, and L3 is about to rewrite the cart/plan shape, so it is work done twice.
- (b) The actor context carries it, set by the shop at actor start. Smallest change, touches no committed format — but it is a runtime API addition, and CLAUDE.md says runtime changes want approval.
- (c)
$pgocarries it. Most in the grain:debugging.mdalready says counters exist only when$pgodoes, so making the endowment the thing that both gates and identifies collection follows the docs rather than bending them. Cost:$pgotoday is a DIFFERENT endowment (count_instructions/op_counts, instruction counting), so this reshapes an existing name.
Not guessed, because the last fork on this landing was decided by “the docs are the source of truth” and (c) is the doc-aligned candidate while (b) is the cheap one — exactly the shape of choice that went the other way last time.
The drain returns pool coordinates (
content_hash,function_index, pool pc, kind) andpgo_capture+pgo.cmjoin them against the origin-map sidecar — already keyed by pool CONTENT hash since the L2 landing — to produce the same{mcode, function, site}the register lane now emits. The join is a pure lookup: no new origin-map field is needed, which is the whole benefit of the ruling.Delete the
--pgo_collectrefusal inpit-shop/terminal.ceand the lever arm intests/cli/pgo_flags.ce; the test then asserts the positive path under both lanes.Counters exist only when
$pgodoes — a build without the endowment pays nothing.
stable_site_key: DELETE it. My call, per John’s invitation, with the reasoning. The
origin-map join supersedes it — identity comes from the join, so a u64 in the row would be a
SECOND way to name a site, which “one way to do each thing” forbids. It is read by nothing
(parsed at source/mach.c:297 and never used), and it has no specified derivation, so keeping
it means inventing one for a field with no consumer. Cost is contained because no producer has
ever written the section: the row goes 24 → 16 bytes
({function_index, pc, kind, counter_index}), touching the row size at source/mach.c:160,
the accessor at :297, PitMachPoolPGOSiteRow in source/pit_internal.h:371, the hand-built
fixture in source/mach_pool_test.c, and the spec table. No committed artifact contains a
PGO_SITES row to migrate.
The fork as it was posed, kept for the reasoning. Two options were priced; John took A.
The C facts the drain rests on, measured. The PGO_SITE row IS specified
(docs/spec/artifact-formats.md:1173 — today 24 bytes, {function_index, pc, kind, counter_index, stable_site_key}; drop the last per the ruling above), and
source/mach.c:745-753 already validates pc as a POOL-final
function-relative pc and requires the rows to be grouped per function to match
first_pgo_site/pgo_site_count. The meta word the VM reads
(source/mach.c:9578, MACH_GET_Bx of the third word of the three-word region) is
function-local — ml_pgo_site allocates it off McodeLowerState, which is per function
(source/mach.c:11152) — so the pool arm is fn.first_pgo_site + Bx with no relocation
and no third reloc class, which matters because the fragment spec says import relocs are
“the second, and last, operand class the link owns”. The shared mcode→mach lowering ALREADY
emits MACH_PGO_LOADF_COLLECT on the fragment lane; mach_exec_pgo returns NULL for
MACH_EXEC_IMAGE (source/mach.c:1676), so today a pool-lane instrumented site carries a
dangling ordinal and silently executes as a generic load. stable_site_key is parsed at
source/mach.c:297 and read by nothing — its derivation is unspecified, unlike
stable_function_hash, whose spec says “the target key hash of that same text”.
The session identity does not belong in the pool. _pgo_semantic_hash /
_pgo_compiler_salt ride on the mcode main today (pit-compiler/pgo.cm:685) into
mc->pgo_*. A pool is content-addressed and therefore SHARED — two executables with
different semantic hashes can name one pool — so the {semantic_hash, compiler_salt} that
keys a PGO session must arrive at BIND time from the realization, not from the pool bytes.
That is the same conclusion as “the realization binding/instance owns the counters”.
RULED by John 2026-07-30 — the identity is a START-SUPPLIED FACT, and the drain
sequences BEHIND L3. The recon found the production pool-bind path never sees a
realization (plan_run_pool receives placement + artifact hash + results only; no
semantic hash anywhere in start_plan.c/image_provider.c), so the identity has no road
in today. The ruling: it travels the way the logger token travels in the ruled logging
model — a fact the shop supplies at actor start — and it rides the NOTA start plan L3
creates. So the drain’s final wiring lands after L3 rather than teaching the dying plan
format one more field. Until then PGO collection stays honestly refused under the pool
lane. The emitter step (74 lines, PGO_SITES rows grouped per function, tenth section only
when non-empty, header bit derived from row presence) exists as a stash in the L2b
worktree and is fully specified here — re-derivable if the stash is lost; it must land
TOGETHER with the counters (B-with-C), since --pgo_auto/--pgo_from already produce
PIC sites under the pool lane and a pool must never claim instrumentation it does not
perform.
THE FORK — what identifies an observation? The docs and the code answer differently, and nothing anywhere calls the other one a defect:
- Three doc pages say the observation is addressed by mcode function and SITE ID.
docs/spec/artifact-formats.md:1390gives the row as{mcode, function, site: "call:0", executions, values}, sorted by(mcode, function, site).docs/architecture/debugging.md:143: “addressed by mcode function and site ID, which the pool’s origin map supplies”.docs/architecture/build-and-artifacts.md:250is the most explicit — “A persisted profile object records the mcode hash and function/site ID for each observation”, under the heading “Observations are recorded in mcode identity, not in the pool’s.” pit-compiler/pgo.cm:182site_keysays it is{unit_hash, function, pc, kind}, andvalidate_site(line 423) requires exactly those four fields. Consumption keys on the whole record (find_observation→canonical(site)), so the shape is load-bearing on both ends.
The origin map supplies {mcode_hash, function_id, site_id, source_hash, span} and drops
the mcode pc (shoplib/mach_pool_emit.cm:960-971 — append_range computes mcode_pc and
does not store it). So the join as the brief directs it cannot produce the record the brief
names. One of the two has to move:
Option A — TAKEN. Name sites {mcode, function, site_id}. The pool join becomes a pure
origin-map lookup with no new field anywhere, which is what all three pages describe. The open
risk was whether the sites PGO collects even have site IDs, since sites is sparse — a probe
over five real files found 1329/1329 instrumentable loads do (function_sites,
pit-compiler/compiler.cm:1331, one row per instruction carrying a source position, id =
opcode + per-op ordinal, unique within the function). It also moves identity construction out
of C, which is the “Pit emits, C reads” direction. Cost paid: stored profiles invalidate
(fine — regenerable), and the register lane moved too.
Option B — the road not taken, kept because its one virtue is real. Keep the pc-shaped
identity and add mcode_pc to the origin record: a one-line change in append_range plus a
spec line, since append_range already computes mcode_pc and merely discards it
(shoplib/mach_pool_emit.cm:952,960). Its virtue is that both lanes would emit
byte-identical profiles, so a profile collected before the lane flip would stay consumable
after it. It was rejected because it corrects three normative pages to match the code, which
method.md names as the wrong default direction. Worth remembering if profile portability
across the flip ever turns out to matter more than it appears to. The pc spaces do coincide
(lowering_inline_literals is 1:1, pit-shop/mcode_lower.cm:146), so B remains
index-correct and buildable.
The rest of the landing, once the fork is called. Fragment functions[] rows gain
pgo_sites: [{pc, semantic_pc, kind}] in Bx order (extend the way origin rides today);
shoplib/mach_pool_emit.cm writes PGO_SITES grouped per function, fills
first_pgo_site/pgo_site_count, sets header flag bit0 (instrumented) and assigns
counter_index (the spec: “allocated per actor from the highest counter index plus one”);
C builds a per-binding counter array at bind time from PGO_SITES, mach_exec_pgo stops
returning NULL for images, and the VM’s ordinal becomes fn.first_pgo_site + Bx; the
snapshot returns pool coordinates and pgo_capture + pgo.cm join them against the origin
map sidecar (already keyed by pool CONTENT hash since the L2 landing). Counters exist only
when $pgo does — a build without the endowment pays nothing.
L2c — pit log <channel> retargets at the logger (RULED by John 2026-07-30) — LANDED.
The boot-actor landing left it half-dead: the floor selector still filters the floor’s own
lines (it must — the sysym reaches claim-less actors), but a claiming actor’s lines flow
unfiltered. Ruling: pit log <channel> configures the LOGGER actor’s channel filter
(logger.ce channel_allowed, which already exists for subscribers); per-channel
filtering is the logger’s business.
The verb is pit log channel <name> [--off] [--reset] (pit log <name> was not available:
pit log [id] is the observe-log listing, whose positional is an actor id). It sends
{type: "log_channels", channel, enabled} to the logger, which is the message name the
shop already used for the floor selector — one vocabulary, a different target. Two calls
the ruling did not scope, both taken the small way:
- It gates FANOUT, not the store.
append_log/rememberstill run for a silenced channel, sopit logandpit whykeep their forensics — and the filter lands as one condition inside the existingchannel_allowed, which covers replay-on-subscribe too. - It is uniform across channels. No fatal-channel exemption;
error/disruptare silenceable like anything else, and the record survives in the store either way.
tests/log_channel_filter.ce (in the baseline suite) proves all six: enabled → arrives,
disabled → reaches nobody, another channel unaffected, still in observe.jsonl, a
claim-less actor still runs and is still reported dead through the floor, re-enabled →
restored. Two findings from writing it, both about what a claim-less actor can be OBSERVED
to emit: its floor_log goes to the boot sink, not the logger (no module published log
into its context), and the runtime’s own actor halted line is written at actor_free,
which a live overling reference defers past the end of a test. Separately: a start-time
disrupt produces no notice on the $start callback at all — only the greet — so
tests/log_token_handoff.ce’s disrupt arm is a branch nothing can currently reach.
L1b — stamp truth flows from the recipe (RULED by John 2026-07-30). profile_name,
value, and wota are carried by the rep macro today: --set stamp.rep=nan32 builds a
binary calling itself playdate-nan32-v1;value=32;wota=32 while its recipe says
host-nan64-v1/64/64 — the endian lie-class, three levers wide, pointing the other way.
Ruling: build derives from recipe (docs/architecture/recipes.md, “The recipe is the
authority”). pit_profile.h stops hardcoding per-rep stamp facts and takes them from the
recipe at forge; combinations the toolchain cannot honor are refused at forge by name (the
endian-refusal shape L1 built). Touches every arm; arm_identity_lint + the pressed
fixture keep it honest during the move.
Refined by John 2026-07-30: value DERIVES from rep — it leaves the recipe schema
entirely (a rep names a complete value-word scheme, width included), so rep-vs-value
contradiction becomes unwritable, not refused. wota remains a free lever (rep
follows the value representation, wota follows what the architecture serializes fastest;
a small-rep + wide-wota build is legitimate). Both still render into the profile string.
The ps1→fixed32 ruling is RETRACTED (John 2026-07-30), and the retraction is the
lesson. The L1b agent held the change and produced the history: ps1=nan32 was John’s own
informed ruling of 2026-07-18 (recorded in pit_profile.h) — R5.d measured soft-float
nan32 on emulated R3000A as bit-identical to host (“slower than fixed32, never wrong”),
and R5.c proved fixed32’s s14.16 integers cap at 16,383 and cannot address a cartridge
(an 868KB cart read landed ~852,000 bytes short; the rung STOPPED on it). The “copy-paste
default” premise behind the reversal was false. nan32 stands. REVISIT flagged by John:
the wanted future is a fixed-point rep that stores larger integers and converts to fixed
point only when a value goes out of range — a rep redesign with its own measurement
burden, queued behind this arc, never a recipe edit. Method note: a ruling that reverses a
committed position must be checked against the history before it is recommended — the
orchestrator failed to, the agent caught it.
SQ1 — the actor-lifetime side quest (RULED by John 2026-07-30, all blessed). The
liveness model is the scheduler’s, and it is already ~90% implemented: the slow ladder
(fast budget → freeze mid-turn, back of the line → repeated overruns accrue strikes →
halt at the limit; CONFIRMED including the halt) and the ar/$unneeded reap (idle
countdown, default-on, waiting-on-async is NOT liveness, $unneeded(fn, secs) with the
goodbye-turn callback; -1 never). The defect is the SECOND liveness system stacked on
top — the messaging reply-deadline layer, whose true origin was a memory-leak guard for
reply callbacks held forever. Ruled resolution: a reply resolves by answer, by the
counterparty’s death, or by the holder’s own death — never by clock. The landing:
- Delete the default reply deadline and
REPLY_HARD_CAP_Sfromlang/messaging.cm; explicit per-request deadlines only where a caller passes one;$time_limitis the opt-in bound. - Reply-route death notices over the EXISTING watch machinery (
actor_watch_stop_register— couple’s plumbing, monitor-shaped, no lifetime binding): on B’s halt, every pending reply routed to B fires with the death reason; letters still queued at halt bounce the same way. Fix the parked PIT_INPROCESS lock-order deadlock (scheduler_snapshot × actor_watch_stop_register) in the same seam. - The terminal ladder replaces the daemon’s 600s bound:
$unneeded(short)awaiting first contact, then explicit$coupleto the child +$unneeded(never); the child’s death is the notice that ends the session. Composes with the outstanding-request honesty gate. - Pending-reply count and age surfaced through
$inspect— alive-but-silent counterparty is an OBSERVABLE protocol defect, not reclaimed memory. - The knobs go coarse: default ar, fast/slow budgets, strike limit into recipe
build.budgets; the 600/900 constants and the bare-vs-arg envelope asymmetry die. - Docs: one lifetime page (positive voice) — the model lives today in the scheduler spec,
the endowments list, and one excellent C comment; the glossary carries the sharpened
principle (“the runtime reclaims and kills; it never restarts or supervises”). Sweep
the std long-wait modules for the
$unneededdiscipline.
SQ2 — exit-code honesty sweep (same seam, same landing series). The 600s false-green
is fixed (the diagnosis landing); the remaining inventory: ./pit compile compile exits 0
silently on syntax errors; the error-surfacing seam per the standing ruling (a caught
disruption must not exit 1 — terminal.ce treats any panic-channel line as fatal while
Pit_RaiseDisrupt logs to panic AT THE RAISE SITE, before catchability is known; the fix
is DELIVERY of the failing path’s error-channel lines, never a new error-return shape —
this also closes the inspect_debug intermittent, ~20% measured); pit.c:213’s
disruption_reported special case exists only in the boot lane, not for spawned test
actors; the first-test-run-after-build timeout reports as an ordinary failure.
SQ3 — sched leaves the stamp (RULED by John 2026-07-30). The flip’s strict pool
reader refused byte-identical pools between the playdate sim (sched=single) and the host
daemon (sched=threaded) — the exact “checkable open question” the recipe ratification
flagged, now checked by reality. The eviction: the profile string drops ;sched=…;
sched moves to the recipe’s build group (it selects provider files only);
shoplib/recipe.cm axes, cake/plan.cm’s stamp render, pit_profile.h’s string
composition, arm_identity_lint, and the pressed fixture all follow; one routine reseed.
This greens check-playdate with zero lever plumbing (the sole differing axis stops
existing). Findings recorded with it: press_floor_unit now threads the caller’s full
rep-profile stamp (real ship-path defect, fixed); bootstrap --numrep nan32 is a
PRE-EXISTING dead lane (lower_unit’s foreign-target refusal predates the flip) — the
n64/ps1 cross-seed scripts are dead until the press lane serves them, L6 territory.
SQ3 + REGEN1 — MERGED TO THIS BRANCH 2026-07-31. The two-reseed crossing
replayed here exactly as recorded: reseed #1 (old binary, new sources) produced
the mcode snapshot; TRUE bootability verified with every cart aside (3.45s, vs
the agent’s 3.42s); rebuild showed the new spelling (no ;sched=); reseed #2
re-pressed cart/engine/manifests/fixture. check-playdate ALL GREEN on this
branch. Budgets render as build| rows (the plan.cm merge put them in
build_defines, where SQ1d’s own comment said they belonged). Three findings
on the way, each now landed:
- The seed blew the 1 GB heap cap embedding what it never reads.
load_cached_executable_valueparsed every module’s mcode unit into records (one 85-module executable ≈ >1 GB) so the seed could read the payload HASHES. The seed now asks forpayloads: "named"and the loader answers existence bybuild_has_value— same refusals, no parse. The deeper flip (records always carry names; consumers fetch bodies at consumption) belongs to L3’s D5 payload conversion. - RULED (John, 2026-07-31): the heap limit WARNS, it never kills. A heap
past its stated limit names a defect (a leak, or a need nobody stated);
killing converts the signal into lost work and a mystery death. Warn once
per doubling; a target that must kill states it as a recipe budget later.
Pit_RaiseOOMnow writes its reason intodisrupt_msg— a real OOM stops presenting as the bare fallback “disrupted”. - Deaths get their own logger ring. The shared 256-record ring evicted a
disruption under two minutes of compile chatter, so
pit whyanswered “no recent deaths” moments after one — the query’s own realization helped evict its answer. Death channels (disrupt/panic/abort/crash/fatal) now keep a separate 64-slot ring that chatty channels cannot touch.
The make-vs-hand check discrepancy is closed: the old gates ruled on
tail -4 | grep — a sliding window that any post-summary line (fresh-daemon
realization chatter) shifts — and discarded the output, so the red was
undiagnosable. scripts/gate.sh rules on exit code AND the last failed:
line, and prints the tail on failure. Also RULED (John, 2026-07-31): no AOT
or native-perf work anywhere right now — tests and mach bytecode only.
SQ1 + SQ2 — LANDED 2026-07-30. What the work found, beyond the brief:
The lock-order inversion was not a curiosity, it was load-bearing. actor->mutex is the
TURN mutex, and actor_watch_stop_register took a FOREIGN actor’s copy of it from inside
the watcher’s own turn — so it inverted against scheduler_snapshot (which walks every
actor’s mutex from inside ITS caller’s turn) and against a second registration coming the
other way. The fix is a leaf lock for stop_watchers and nothing else. It had to land
FIRST and separately: once a reply-carrying send registers a watch, the pre-fix binary
wedges on ordinary traffic, and it wedged make itself — the forge deadlocked twice
building the very binary that fixes it. The escape is PIT_WORKERS=1 (one worker
cannot hold two turns, so no cycle). tests/watch_snapshot_lock_order.ce gates it and
HANGS on the pre-fix binary, which is how it was proven.
The old reply deadline was an accidental heartbeat. Every reply-carrying send armed a
60 s timer; timers fire as letters, letters are turns, and a turn resets the idle
countdown — so any actor with an outstanding request was being kept alive by the deadline
it was waiting under. Deleting the deadline let the ar timer do what it is documented to
do, and pit-shop/watch (a shop singleton, quiet between subscriptions, no $unneeded)
was reaped at 60 s. Two fixes, both ruled shapes: the service states its own lifetime, and
shop_actor’s dynamic-service cache stops treating a reaped singleton as permanently
dead (it was poisoning the entry forever, so one quiet minute retired pit watch for the
rest of the daemon session).
The inspect_debug intermittent was two defects, and the first was hiding the second. A
CAUGHT disruption left ctx->disrupt_msg set, so the next BARE disrupt — which carries
no message of its own — reported the earlier, caught failure instead of itself, seconds
later and from an unrelated frame. That is why the obvious diagnosis kept being wrong: the
message named the wrong event. With the buffer cleared at both handler-entry sites the
real complaint surfaced (no live number in a frame frozen inside a counting loop — the
freeze parking outside the counting loop, where the test’s own retry ladder should have
sent it back round). 20%→0/15 measured.
L3 PHASE 1 — LANDED 2026-07-31. The cart is pit.cart@2 end to end: cart2.cm
writes and reads the byte table (16-test raw-offset gate), cement decides WHAT and
cart2 decides WHERE, C boot reads the 256-byte header entire and nothing else,
pit_start_plan_run decodes the boot entry (root path never touches the metadata),
a plan row names its pool inline (no MODULES/CODE/string tables on the run path),
and the pit-side reader + five consumer tools moved to the value-shaped surface
(cart.cm 1356→926 lines; ~530 of v1 hand-rolled decoding deleted). root.cart
4.5MB→1.6MB. Fresh-daemon test all 2728/2 (http2 standing; zz cold per its ruling).
What the landing found, each now law: a PIT_ROOT lives at FUNCTION scope (the
block-scope variant wedged every boot-service start in an unbounded GC — ASan on
the O0 build named it after release-build inference failed three times); an
observer never blocks on a turn mutex it does not own (scheduler_snapshot ABBA’d
against itself under four CLI sessions — sys_rmutex_trylock joined the seam in all
seven providers, busy actors report racy-safe scalars, and pit ps no longer
stalls behind long turns); single-threaded observers do not retry (nothing else
can release the lock); the layout ceiling is a FIXPOINT (sections sit behind the
metadata whose length depends on their offsets’ width — the scratch lane proved
it, and the named refusal turned a mislaid cart into a one-line diagnosis).
PHASE 2 REMAINS: the deletion per the recon inventory, D5’s payload conversion,
and the ==0 observables’ restoration.
L3 — the cart crossing. RULED 2026-07-31 (John, on the recon agent’s batched
questions): (1) the byte layout is DRAFTED INTO docs/spec first — it now lives at
docs/spec/artifact-formats.md “Cartridge v2” as pit.cart@2, magic pcr2 (the old
pcr2 section described a never-implemented format and is replaced); implementation
follows the spec exactly. (2) The 16-byte discovery trailer survives — the appended-binary
lane is load-bearing boot ABI. (3) The any-generation boot floor is the mcode SNAPSHOT
(REGEN1), not the cart: a pools root cart is generation-locked and that is correct;
boot_cart.cm’s M1-era comment gets rewritten. (4) The M1-era test assertions are
OVERRIDDEN by name: cart_payload_form.ce inverts pmcd→pmp1, runtime_arena_counters.cm’s
window-pool observable dies with its cache, the shape observables flip to == 0. Recon
also resolved: the header names the engine pool and boot entry DIRECTLY (nota needs an
actor context that does not exist at mount), and the deletion inventory measures
~4,250-4,300 lines with two cautions (mach_shape_spec_decode stays — the pool lane calls
it; kim8 TEXT encoding stays, only the register arm dies).
The new cart shape: fixed header (magic, profile stamp, nota
offset/len) + metadata as one nota value + pmp1 sections at aligned offsets. cement.cm
writes it; C boot reads the header by memcmp and the metadata through nota2value;
engine/boot_walk.cm receives a decoded record and its hand-rolled plan/string-table
decoding deletes. Two phases, one reseed each, per the section-kind recipe: phase 1 = the
current binary (which still lowers mcode) presses the first all-pools cart; phase 2 = the
new C lands and DELETES: the C pool assembler (Pit_MachAssemblePool*,
mach_pool_lower_window), the pmcd lane (Pit_MachWindowIsPoolLane’s mcode arm, the
window-pool cache), mach_lower_mcode_window, the pmac writer (mach.c:2873-3142) and
readers, pit_load_mach_code, mach_materialize_cpool,
mach_resolve_shape_spec/_record_shapes, the code cache + mark_code_tree_shared +
the three code->shared GC arms, and the kim8 register arm. $mach_compile_mcode_bin /
$mach_load keep their names and change payload to pools (D5 stands; renaming is a later
cosmetic landing). Observables: grep -c pmac source/ → 0, grep -c pmcd source/ → 0,
every cart window magic pmp1, runtime_code_count == 0, ct_lock callers shrink per
the old stone-6 list.
L4 — mach.c splits. After L3’s deletions (~3.5-4k of 12.5k lines gone), split what
remains into mach_vm.c / mach_pool.c (+ shared internals); pit_internal.h shrinks
with it. Mechanical, behavior-free, make budget guards the hot loop.
L5 — the boot-actor rewrite — LANDED 2026-07-29. The boot actor starts actors: it mints
the guid (⇒ $self), receives the request (⇒ $overling), holds the logger token, and the
new actor’s first act is walking its own plan (7.3 landed the walk; this landed the facts).
The two halves are one landing, and the ORDER INVERTS the brief’s. Retiring the gate had
to come first: with $log_enabled still live, deleting logger_id/shop_id/root_id makes
default_log_enabled answer false for every channel on actor zero, so the root’s floor error
lines vanish instead of falling through to C’s boot sink. Gate first, wota second.
The gate is gone. pit-compiler/mcode.cm:emit_log_call (was emit_lazy_log_call; “lazy”
named the gate) emits one call and no consult
(three slots, a call and a branch off every log line); ENGINE_NAMES and the unconditional
claim_entry("$log_enabled") are out of cement.cm; engine_lite.cm loses log_enabled,
default_log_enabled, log_enabled_fn and actor_set_log_enabled_fn, and with them the dead
identity chain they existed to compute (logger_id, shop_id, root_id,
log_self_routes_to_root, logger_is_external, log_target_id); C loses
PitLogEnabledCallback, the three PitContext fields, pit_log_enabled_callback,
actor.set_log_enabled, Pit_LogEnabled (declaration included) and four GC root sites,
not three — pit_gc_compact.c one, pit_gc_copy.c three. Log arguments now always
evaluate and per-channel filtering is the logger’s.
The claim is the gate. shop_actor.ce:claims_log(exe) reads exe.globals; an actor that
mentions log gets the token, one that never does gets neither token nor function.
The claim aggregates over the whole CLOSURE, which is correct — the closure is what decides
whether log is bound at all — and it means anything importing lang/messaging.cm claims
log transitively, so the withholding arm is real but rarely taken.
The boot wota shrank 13 fields → 8. pit.c deletes shop, shop_id, root, root_id
and the logger_id: "bootstrap" placeholder. What C still states about actor zero is the one
identity it minted (id, self), its own build/target stamps, and this invocation’s command
line and medium. It states nothing about who the shop is, who the root is, or where logging
goes.
Test. tests/lazy_log_enablement.ce + tests/fixtures/log_enabled_native/ are replaced by
tests/log_token_handoff.ce. Note the finding it encodes: the negative case cannot be
reported the way the positive one is, because replying needs lang/messaging.cm and that
module logs — so the claim-less child answers through the FLOOR (disrupt if it holds a token,
clean stop if not) and $start delivers the verdict as an underling notice.
Still open (asked, not decided): $actorsym’s home is unchanged. (The log-selector half of
this note was ruled and landed — see L2c.)
L6 — the cross-target proof. THE PRESS HALF IS LANDED; CI ACTIVATION IS THE USER’S.
make cross-check: on this machine, press carts + runtime builds for at least linux,
emscripten, and windows (playdate if the runner allows), push, and CI runs the test suite
on each. Green there = the system works. This is THE acceptance test of the arc and the
standing “does this work?” test afterward.
What landed 2026-08-01, and the shape of the fix. The SQ3 finding was right that
bootstrap --numrep nan32 was dead, and wrong about where. lower_unit’s foreign-target
refusal is CORRECT and did not move: that lane realizes a unit the shop is about to RUN,
so the running profile is the only honest answer. What was wrong is that the boot press
was walking into it — bootstrap_artifacts realized the root executable
payload_mode: "mixed", and boot_cart.pool_executable then threw every mach payload
away and re-pressed each module from its portable mcode unit and the target’s stamp. The
mach was computed, stored and discarded on every seed, and asking for it while naming a
foreign rep is what the refusal caught. The boot realize is now payload_mode: "mcode"
and pool_executable keys on the mcode unit rather than on a host payload’s presence.
Nothing relaxed; one lane stopped asking a question it never used the answer to.
The second half was the stamp. bootstrap --target linux used to change the provider
arms and NOT the stamp — the cart came out host-stamped whatever you named — so the levers
(--numrep, --endian, --gc) were the only way to describe a target, and the n64 script
carried the axis list by hand. A target is a recipe: rep_profile.target_stamp_for reads
all seven axes off recipes/<target>.json through shoplib::recipe profile_string (the one
renderer, the one cake renders the build define from), and the levers still override.
pit cake profile <target> exposes that same rendering so nothing outside pit needs a
second parser or a private copy of the axis order.
Answering the two questions this landing was told to stop on. The content universe ships
mcode, and it already did — seed_fleet_entry has realized payload_mode: "mcode"
since REGEN1, so “portability ends at mcode” was settled in code before it was asked. It
was missing the target REP, though: the seed threaded endian and not numrep, so a
cross-rep seed folded its fleet’s constants in the presser’s arithmetic. Both now ride as
one {numrep, endian} record. CI artifact transport needed no ruling either: git is the
transport for the pressed-elsewhere direction (the committed pair is darwin-pressed and the
Linux runner boots it), and the linux job in ci.yml has silently depended on that since it
was written.
Evidence. make cross-check presses for linux + emscripten (their recipes declare this
host’s own stamp — which is exactly why a cart crosses between them) and for playdate + n64
as the FOREIGN-STAMP arm, because without an arm whose stamp really differs the refusal
check is vacuous. The n64 cart comes out
n64-nan32-v1;value=32;rep=nan32;endian=be;text=utf32;record=hash;gc=copy;wota=32 on a
little-endian nan64 host, pit cart info walks all 30 sections of it, and this binary
refuses it naming every differing axis. Foreign-endian EXECUTION proof still belongs to a
real runner; the byte-level gate for it is the pressed fixture table in check-arms.
What remains for the user. Push, then activate .gitea/workflows/cross-check.yml (three
jobs: the committed darwin-pressed pair running the suite on linux, the linux runner
pressing for other targets, and the web lane). Windows needs a ruling — the press arm
works today and the run job is written and commented out; standing up a runner is not this
landing’s call.
Deliverables — what done looks like
- One lane: zero
pmac/pmcdinsource/; every cart windowpmp1; the ENTRIES plan is nota;runtime_code_count == 0on a booted fleet. - Thousands of lines deleted: the L3 list plus its tests (the byte-identity gates,
tests/mach_code_cache.cmrewritten, pmac fixtures dropped). Report the real number. make cross-checkgreen — carts pressed here run the suite on linux, emscripten, windows CI runners.- A compiler-less runtime proven: a pressed cart boots and runs on a runtime whose
store carries no compiler fleet (the
vm_onlyshape) — the actors-as-stripping-unit demonstration. - Docs remain the single source of truth: no plans file contradicts a docs page;
plans/holds this file,method.md,compiler-vision.md(the next arc), and journals only. - The guard-comment sweep (RULED, John 2026-07-31): every surviving
“do not relax”-class comment is re-anchored to the current ruling it protects, and
old-world ones die with their worlds — a fossil comment must never argue against
the next migration. (The rule itself is recorded in
method.md.)
Deferred from the test-lifetime work (2026-07-31), needs a ruling when picked up:
a reaped actor’s overling notice is {type: "stop", reason: "actor stopped"} —
byte-identical to a voluntary $stop(). The harness now compensates (it states each
test’s lifetime, so a reap cannot precede the deadline), and
tests/reap_is_not_a_pass.ce pins the indistinguishability so a change gets noticed.
The open question is runtime surface: should a reap carry its own reason? The lifetime
model’s honesty says yes (a mystery death is the class we keep killing); it waits
because nothing depends on it now and the arc comes first.
Deferred from the sweep wind-down (2026-07-31): a pit test all client can
complete its suite, print the summary, and then fail to EXIT — parked in teardown
(exit_handler’s join, most likely on a leftover pit debug sites worker from a
failed debug_tool run), leaving a live process with a dead endpoint that correctly
refuses all later daemons. One observation, on a binary predating the busy-row
snapshot fix; likely moot now that debug_tool passes, but the teardown-join shape
also appeared once elsewhere (a v1 binary’s –version joining a stuck thread), so
if a “daemon alive but endpoint absent” refusal recurs, this is the first suspect.
The parked concurrency experiment’s notes live in the sweep agent’s final report:
the PIT_TEST_CONCURRENCY instrument is committed and daemon-read; resume on a
quiet machine, add an orphan check between runs, and treat short wall times as
suspect by construction.
THE FIRST NAMED ITEM OF THE MACH-BYTECODE PERF WORK (RULED with the L3 phase 2b
re-baseline, John 2026-07-31): the image-arm closure-upvalue cost. closure_calls
runs an IDENTICAL instruction stream on the pool lane at 4.6× the wall time of the
deleted register lane (8.76 ns/i vs ~1.9; isolated to bind_getup, 150k calls), and
numeric_loop pays +280 fixed bytes per mach_load (the image function + owner
array). The old baseline was measuring a deleted lane; the budget gate now watches
the lane the fleet actually runs, and this number is the one it was re-baselined to
keep visible. Two findings from the same landing, recorded for the next crosser:
the cart-crossing class needs its two reseeds IN ORDER (press lands and reseeds
BEFORE C readers go — reversing it leaves a binary that cannot boot the committed
cart, and make needs a booting daemon; the escape is the cold floor: rm -f pit && make), and a failing test with a long reason renders in the human errors:
list as bare - true (real reason only under –machine) — a rendering defect
worth one small fix.
Known defects deliberately left (do not chase inside this arc)
network/http2— the one red test at baseline (2705/1), pre-existing, unrelated.— FIXED in SQ1 (leaf lock forPIT_INPROCESS=1cold-realization deadlockstop_watchers);tests/watch_snapshot_lock_order.cegates it.— FIXED in SQ2; the carriers named here were both real and neither was the cause. See the SQ1+SQ2 landing note above.inspect_debugintermittentmake check-playdateis RED at baseline (found 2026-07-29 running the drill; it was not on this list and should have been). Two files fail against the installed Playdate SDK:platform/playdate/playdate_sys.c:718useskPDLanguageUnknown, which that SDK no longer declares, andplatform/playdate/playdate_gfx.c:339callsmakeFontFromDatawith 2 arguments where it now takes 3. SDK API drift, not a regression — but the drill listscheck-playdate, so treat a red there as pre-existing until these two lines are fixed.- PGO runtime descriptors hold an unrooted PitValue.
MachPGORuntimeCall.keylives in C memory and neither collector scans it —grep pgo source/pit_gc_copy.c source/pit_gc_compact.creturns nothing, whilecode->cpool(where the key is copied from) IS scanned. So a key goes stale across a collection while its cpool slot stays correct. Found 2026-07-30 while checking the site-identity landing, which had briefly made it reachable on the collect path; that arm is fixed (identities resolve in their own pass before any key is assigned). The PIC path is still exposed —mach_resolve_shape_specallocates after the key assignment — and is left alone deliberately: the fix is either GC scanning for these descriptors or the same reordering, and it is not part of naming sites. Note thatcheck-arms’force-gc/validate-gcarms do NOT catch it, because the C suite never drives a consumed PIC. - Five stack-walk sites asking “has a register?” instead of “is Pit?” (
scheduler.c:370/439,crash.c:113,runtime.c:29/10555). - The mach sidecar key collision (
mach_debug_key/mach_origin_keykeyed on mach hash alone) — scoped derivation-contract change, not a drive-by. - Carried opens for John, none gating:
$rootvs$shop;$fdoff the floor;$self_idaccessor; cold-bootstrapbuild_idreads the clock.
Hazards (each cost real time once — carried from the retired plans)
- A payload-form change can wedge past
make nuke— nuke’s escape IS the reseed. Under the bootstrap contract the escape is: last green (source, cart) pair from git +sh boot/manifest/bootstrap.shthencp cold-build/pit ./pit. Prove any payload-form change on a scratch program BEFORE it reaches a reseed. - Two-phase crossings are normal. Phase files must be untracked
phase1_<name>.txt— the.txtmatters;SEED_INPUT_HASHglobs*.cm/*.ceat the repo root and a bare one poisons the seed hash. - The daemon reads env levers, not the client — any measurement without
./pit down &&first is vacuous. - The first
./pit test runaftermake allcan time out — it now says so by name and states the budget it hit (“Test realization timed out after Ns … rerun before reading the test”), but it is still a red line in the summary. Rerun before believing it. — FIXED in SQ2; it exits nonzero and prints the compiler’s own diagnostics../pit compile compile <file>exits 0 silently even on syntax errors- A binary without the
stop_watchersleaf lock deadlocks its own build. If a tree ever needs building with a pre-SQ1 runtime,PIT_WORKERS=1 makeis the escape: one worker cannot hold two actor turns, so the cross-actor mutex cycle cannot form. - Floor units sit at the 255-slot ceiling (
bootstrap_artifactsat exactly 255/255); overflow counts appear only in.pit/log/observe.jsonlunder"channel":"error". - Agent worktrees spawn at stale bases — every brief says “verify the base sha and reset”; most agents last run found theirs wrong.
- Other agents share
~/.pit’s build store; a wedged foreign daemon can stall a build.
The drill — every landing merges this way
Commit source-only in the agent’s worktree (never boot artifacts/stamps — regenerate
yourself), cherry-pick, grep for conflict markers, rm -f boot/boot.cart boot/cart.stamp,
make smoke, then under set -o pipefail: make check, make check-cli twice (cli
BEFORE arms; a first post-reseed run can transiently fail), make check-arms, make lint,
make manifest twice (byte-stable; git status --short boot/manifest empty after the
second), make budget, make check-playdate, and ./pit down && ./pit test all on a
fresh daemon. Then commit the reseed. NOTHING LANDS ON RED. Baseline: 2705 passed /
1 failed (network/http2).
Source: plans/archive/finalization-arc.md