Files
LithosAnanake/FABRIC-3.md
T
Robert Allan JamesandClaude Sonnet 5 cbe7b49a59 Documentation debt sweep: 5 of 6 items resolved, 1 confirmed accurate
- docs/lithosananke/ROADMAP.md + M7.1.md: fixed stale "Branch: lithosananke"
  (no such branch post-split), M7.1's "Design Complete" status (shipped
  and live, redirected to FABRIC*.md), the M8/success-criteria
  self-contradiction (OBSOLETE marking vs. unqualified live criterion),
  and the stale AHCI/SATA claim for M9 (real implementation is
  virtio_blk.c) -- also corrected BLOCK/BUFFER/UPDATE/FLUSH and block
  device abstraction to [x] since both are confirmed live in
  src/word_source/block_words.c and block_subsystem.c.
- Top-level ROADMAP.md: marked OBSOLETE (Captain Bob's call -- more than
  "stale," the architecture/branch topology/terminology it describes no
  longer exist), pointing to docs/lithosananke/ROADMAP.md and
  FABRIC*.md for current status.
- docs/03-architecture/word-acl/DESIGN.md: fixed the ACL Phase 7
  contradiction -- Phase 7 (LithosAnanke kernel parity) is independently
  verified complete per .claude/CLAUDE.md, not "remaining"; removed the
  stale lithosananke-branch-parity framing.
- VM-FLEET-ATTRACTOR-DESIGN-20260705.md's doe-campaign.4th "broken" claim:
  investigated, ran SMOKE-CAMPAIGN live (completes clean, fleet heat
  conserved) -- initially read as contradicting the claim, corrected
  directly by Captain Bob: a clean execution trace doesn't disprove the
  doc's actual argument (no real controlled-experimental-factor
  mechanism). Confirmed accurate, left untouched.
- Isabelle/HOL pipeline-metrics model/C-struct mismatch: confirmed a real
  proof-modeling gap (pm_last_accuracy_num/den has no analogue in the
  real PipelineGlobalMetrics struct), not stale prose -- tracked here
  rather than fixed, matching the .thy file's own scope boundary and
  this project's standing caution that each Isabelle gap needs its own
  subsystem model.

ACL-RWT DoE overhead re-measurement (the 6th item) intentionally not
started -- a full multi-architecture DoE campaign, not a doc-text fix,
holding for explicit confirmation given the scale.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn
2026-08-26 06:30:36 -04:00

38 KiB
Raw Blame History

FABRIC-3.md — the Stadium, continued again

Status: Living working document, opened 2026-08-25 as the successor to FABRIC-2.md (now closed/archival — see its own header). This document does not repeat FABRIC-2.md's design argument or history; it restates only outcomes, with pointers back to the section that derived them. Read FABRIC.md for the original "why," FABRIC-2.md for everything derived through 2026-08-25, this document for what's left as of that date onward.

Provenance. Everything in Section A below is a full, non-sampled carry-forward of every open (- [ ]) item in FABRIC-2.md as of 2026-08-25 — 51 items, confirmed by grep -c '^- \[ \]' FABRIC-2.md, none dropped (Section A itself holds 49: the other 2, FABRIC-2.md §F.3's own two checkbox lines, were pure summaries cross-referencing items already listed individually elsewhere — 4.4s/1.11/4.3/§17.4 and 5.1/ACL-RWT re-measurement, both of which are carried forward as their own individual items above — not distinct content, confirmed by diffing item text programmatically before writing this document, not assumed). Extracted mechanically (a script pulling each checkbox item's own text, stopping at the first blank line rather than the next checkbox, to avoid pulling in unrelated already-resolved narrative that happened to sit between two open items in the source document) and spot-checked against the original. Item numbers/labels are carried forward unchanged, for traceability — this is not a renumbering or a re-prioritization. Section groupings match FABRIC-2.md's own (documentation debt, xHCI WRITE(10), Milestone 39 punch lists, etc.) — items are relocated, not reorganized.

How to use this document going forward. New findings, new punch-list items, and new decisions get added here, not to FABRIC-2.md. Follow the same discipline FABRIC-2.md §(intro) established for how work gets picked up, closed, and recorded.


A. Carried forward from FABRIC-2.md (51 items, all still open as of 2026-08-25)

From FABRIC-2.md §A — Blocked or scoped, not started

  • 1.11 — Dirty-event granularity. Leaning region-based. Blocked on item 4.3 — settled as part of the console migration, not speculatively before it. Refs (FABRIC.md): §17.5, §23.2, §23.4 #1.

  • 4.3 — Console. Umbrella item; settles 1.11 as part of the work. Nearly everything under it (4.3.14.3.7f, 4.44.4ac) is done — the parent stays open only because 4.4s below is still blocked and nothing has formally closed the umbrella. Refs (FABRIC.md): §17.5, §27.

  • 4.4s — (user) prompt segment. Scoped, blocked, not started. Extends 4.4's prompt format. Refs (FABRIC.md): §27.8, 4.4.

  • 5.1 — Re-run the DoE on the new substrate. A green POST suite is not evidence that determinism holds under the Stadium migration — needs its own campaign. Not started.

From FABRIC-2.md §D — Design questions still genuinely open

  • §17.4 — the framebuffer utility's internal heat/decay dynamics are undesigned. Explicitly "Open, deferred": not a Stadium patron, but what physics (if any) governs it internally was never designed. Not blocking anything. Blocked on item 1.11 specifically (dirty-event granularity), not "the framebuffer work" in general — see FABRIC-2.md §D's own 2026-08-13 refinement of this item before assuming it's ripe.

From FABRIC-2.md §E — Documentation debt

  • ACL-RWT DoE overhead re-measurement. The measured overhead numbers in .claude/CLAUDE.md ("+0.0054%+0.0088%") were all captured at -O0, before item 4.5 enabled real compiler optimization. Nobody has re-measured, or even confirmed the old and new numbers are comparable at all. Flagged in passing during item 4.5f, never formally scoped. Ruled 2026-08-19 (FABRIC-2.md §F.3): wait for Artemis to land before re-running — Artemis has since landed; this is now unblocked but still not started.

From FABRIC-2.md §J — Maintainability sweep (2026-08-18)

  • docs/lithosananke/ROADMAP.md and M7.1.md — stale Branch: lithosananke (no such branch exists post-split), M7.1.md's "Status: Design Complete" (shipped and live, not just designed), ROADMAP.md's self-contradiction (M8 marked OBSOLETE in one place, still a live success criterion in another), and its stale "AHCI driver" claim for M9 (real implementation is virtio_blk.c) — not fixed, flagged.

  • Top-level ROADMAP.md (StarForth-era, "Phase 0 Complete... Phase 1 Starting," dated 2025-12-14) — badly stale, no historical/superseded banner to warn a reader. Not fixed.

  • docs/03-architecture/word-acl/DESIGN.md says ACL Phase 7 (LithosAnanke kernel parity) is still "remaining" — direct contradiction with .claude/CLAUDE.md, which states Phase 7 is independently verified complete. Not fixed.

  • Confirmed accurate, not stale (2026-08-26): VM-FLEET-ATTRACTOR-DESIGN-20260705.md's claim that doe-campaign.4th is "broken and being superseded." Live-ran SMOKE-CAMPAIGN from the current capsule (amd64) — it completes without error and correctly conserves fleet heat (VM-PHYSICS: conserved=CONSERVED, fleet_heat_sum=65536). Initially read that as contradicting the doc's claim; Captain Bob corrected this directly — it's broken. "Doesn't crash" and "runs" are not the same claim: the doc's actual argument is that the capsule has no real controlled-experimental-factor mechanism (no manual heat-injection point under the current design, so it cannot drive the fleet through controlled scenarios the way a DoE campaign needs to), a methodological gap a clean execution trace doesn't surface or disprove. Doc's claim stands; not touched.

  • Tracked (2026-08-26) — real proof-modeling gap, not just stale prose, so not fixed here. Confirmed by reading StarForth_Loop4_Pipeline.thy's own comment (lines 127137): pipeline_metrics_state's pm_last_accuracy_num/pm_last_accuracy_den fraction pair doesn't correspond to anything in the real C struct — include/vm.h's PipelineGlobalMetrics has a single double last_checked_accuracy field, no num/den pair anywhere. The .thy file's own comment already scopes the real fix correctly: "a full field-level pass over pipeline_metrics_state is its own separate task" — matches this project's standing caution that each remaining Isabelle gap needs its own subsystem model, not a documentation-sprint patch. The punch-list ask was tracking this outside the buried .thy comment, which it now has here — the actual re-model stays unattempted, on purpose.

From FABRIC-2.md §X, Milestone 2 — USB hardware stack

  • Decide and implement where the hotplug event surfaces to the rest of the kernel — likely a callback registered by whatever owns the home-blocks logic, not xHCI code calling into block_subsystem.c directly (matching the existing "kernel/Artemis decoupling boundary" pattern already documented in block_subsystem.c). Partially addressed by Milestone 2h's blkio_usb.c/connect-time attach wiring (FABRIC-2.md, 2026-08-25) — worth re-checking whether that closes this item outright before treating it as still fully open.

  • Implement CBW/data/CSW for SCSI WRITE(10) — this is where the earlier "read/write, unquestionable" requirement actually gets satisfied. Still the single biggest functional gap in the xHCI driver — blocks writing to a real USB thumb drive at all (blkio_usb.c is read-only today specifically because of this).

  • Implement basic error/stall recovery (CSW failure status, endpoint stall clear) — at minimum enough to not wedge the controller on a single bad transfer.

From FABRIC-2.md §X, Milestone 3 — Block subsystem extensions

  • Implement the CA-signed-cert verification path (Milestone 6 dependency — the cert chain validator doesn't exist yet either).

  • Implement the first-touch allocation function: given a verified identity pubkey and a requested block count, either read an existing range from the drive's map or claim a new one at g.total_user_lbn and write it back. (Single-block relocation itself — the mechanism this would allocate ranges for — is done: blk_subsys_relocate_block()/ RELOCATE-BLOCK, FABRIC-2.md, commit 36d832f. This item is about the identity→range allocation that decides what to relocate blocks into*, still unbuilt.)*

  • Design the on-drive block-map format (Section U item 4) — what it records (block ranges claimed? individual block liveness? something else), how it's serialized.

  • Implement writing the block-map to a drive.

  • Implement reading/validating the block-map from a drive on insertion.

  • Design the migration state machine (Section U item 5) — states, transition triggers. Session direction, 2026-08-25: ACL manages when to relocate (capacity pressure, or a compudynamics heat/cold signal); migration itself is expected to be rare, not routine. The physics_hotwords_cache.c-reuse question is settled differently than originally framed — see this document's new §B below (Stadium unification), which reframes block/word placement as a compudynamics.c-driven decision generically, not a physics_hotwords_cache.c (DictEntry*-hardcoded) reuse question specifically.

  • Decide and implement unclean-removal handling (Section U's explicitly flagged open question — never answered) — at minimum, detect a mid-flush disconnect via Milestone 2e's disconnect signal and decide what state that leaves affected blocks in.

From FABRIC-2.md §X, Milestone 4 — Drive/credential security

  • Design the home-blocks drive signature format (Section U item 7) — reusing CAPSULE_MAGIC_PACK's pattern (magic + version in a fixed header field) as the confirmed precedent, applied to a drive's reserved header block instead of a capsule.

  • Implement the signature check, called before any write path touches a newly-inserted drive.

  • Implement the warn-and-refuse behavior for blank/foreign/unrecognized media.

  • Extend acl_pinned's one-way-ratchet mechanism (already exists, already proven, just needs applying) to gate zuse credential minting specifically — confirm whether this literally reuses the existing acl_pinned bit on some relevant DictEntry, or needs its own analogous one-way flag on the credential data itself (the credential isn't a dictionary word, so the existing bit may not directly apply — open question, not yet resolved).

From FABRIC-2.md §X, Milestone 5 — Console/VM key-match binding

  • Settle the still-open question: reuse ACL-PIN/acl_allow directly, or build a separate key-matching primitive — ACL-PIN gates word execution specifically and nothing today gates console-session-to-VM ownership, so this decision needs to happen before any code gets written here.

  • Design the key/lock data shape (what the console presents, what the VM carries, how they're compared).

  • Wire drive insertion (Milestone 2e's hotplug signal, post-identity-authentication) to a call into capsule_birth_baby() (confirmed a real, callable, on-demand birth path already) to spin up or re-attach that identity's VM.

  • Implement the actual attach/bind step — extending sk_repl_set_active_vm() (confirmed to exist, currently an unguarded raw pointer-set) with the key-match check from above, so a console can only bind to the one VM whose lock matches its key.

  • Implement detach behavior on console disconnect or VM teardown.

From FABRIC-2.md §X, Milestone 6 — Kernel/capsule PKI signing chain

  • Generate (offline, outside the kernel/repo entirely) the real root CA keypair — "stays unrevocable," never embedded, never loaded by any kernel code.

  • Generate the "snakeoil" intermediate certificate, signed by that real root CA (this is a real CA-signed intermediate, not a self-signed/untrusted cert despite the name — "snakeoil" names its informal/private-project status).

  • Embed the already-CA-signed snakeoil intermediate as a capsule blob at build time (mechanically proven already via the font-capsule precedent — no new embedding infrastructure needed, just a new payload). Bootstrapping resolved: no kernel-boot-time verification of a hardcoded CA public key is needed at all — trust is established once, at build time, by whoever holds the real root CA and produces the build.

  • Add a signing step to the mkcapsule build tool (or a separate signing tool) that produces a signature alongside each capsule's existing xxHash64.

  • Extend MANIFEST_AUTO.md's generation to add a signature-status column, matching the existing xxHash64 column's generation pattern.

  • Implement magic-number-based content-type detection (Section U item 14) — a shared primitive, also usable for Milestone 4's foreign-drive check.

From FABRIC-2.md §X, Milestone 7 — Contributor capsules / trust tiers

  • Create the capsules/contrib/ directory (mechanically trivial, matches existing subdirectory convention — the directory itself is not the work).

  • Add a FLAG_CONTRIB bit to mkcapsule.c's flag system, assigned by path match (contrib/ prefix), same pattern as how init.4th already gets FLAG_MAMA_INIT.

  • Decide and implement one of the four spitballed trust-tier directions (signature- authority tiers / block-namespace sandboxing / QEMU-vs-real-hardware conditional enforcement) — none chosen yet, this is a real decision point, not just an implementation task.

  • If block-namespace sandboxing is chosen: extend mkcapsule's existing conflict- detection logic to also reject a contrib/-path capsule claiming blocks outside its reserved range.

From FABRIC-2.md §X, Milestone 8 — Bare-metal boot from physical USB

  • Build a fresh starkernel.iso via make -f Makefile.starkernel ARCH=amd64 clean + the ISO-build step.
  • Identify the exact block device path for the target USB drive on the host doing the flashing (lsblk/dmesg after insertion — care needed, wrong device = data loss).
  • dd if=build/amd64/kernel/starkernel.iso of=/dev/sdX bs=4M status=progress (or equivalent) — confirm dd is the right tool for an El Torito ISO vs. needing isohybrid first (open question, not yet verified).
  • Physically boot the real machine from the flashed drive (BIOS/UEFI boot-order menu, Secure Boot may need disabling — unknown until tried).
  • Capture what happens with no serial-socket log available (real hardware has no qemu-serial-*.sock to socat into) — decide the observation method.
  • Confirm POST reaches the same 1012/0/0 result on real hardware as every QEMU acceptance run.
  • Confirm ok> prompt is reachable and a basic command (e.g. HEARTBEAT-TICKS@ .) works identically to QEMU.
  • Document the result (pass/fail, and if fail, what diverged from QEMU) — first real external validation this project has ever had outside QEMU TCG emulation.

From FABRIC-2.md §X, Milestone 9 — Networking / capsule distribution server

  • (Deferred) Revisit and punch-list this milestone once Milestone 7 closes, not before.

B. Stadium unification — words/VMs/blocks/messages on the same engine

Raised 2026-08-25: "words are stadium patrons, VMs are patrons, blocks are patrons, messages are stadium patrons, all should be operated on by THE SAME ENGINE." Investigated before designing anything — the real state is more nuanced than "everything's a stub," verified via direct reads and git log, not assumed:

FABRIC.md §18.3 already decided the mapping (not invented here): blocks → MIGRATE, messages → DELIVER, ACLs → EXPIRE, words and VMs both → COOL. stadium_evict() (src/starkernel/vm/stadium.c) — real, tested infrastructure: bitmap tracking, pin/contains refusal, the Hera-patron-zero panic guard, heat-conservation back to the owner's reservoir on every reap — calls stadium_dispatch() for the actual payload action when a patron departs.

Per-behaviour status, as of 2026-08-25:

  • MIGRATE (blocks) — zero consumer, genuinely stub (stadium_dispatch()'s case prints "MIGRATE (stub)" and returns). This session already built the real mechanical primitive it needs: blk_subsys_relocate_block()/RELOCATE-BLOCK (FABRIC-2.md, commit 36d832f), live-verified (redirect + content survive an abrupt kill and reboot) but never wired to stadium_dispatch() — it's a separate, parallel, already-working mechanism today, not routed through Stadium at all.
  • COOL (words and VMs, same tag) — half real. Words are fully live, but via a separate, bespoke mechanism, stadium_word_dispatch() (stadium_words.c, item 4.1), wired directly into the real VM word-execution hot path (vm_core.c:690,885,896) — it does not go through the generic stadium_dispatch() switch at all. ONTOLOGY.md §IX claiming words are "not yet migrated" is itself stale documentation drift (same class of bug as the "glibc" misattribution corrected earlier this session — flagged as a small, separate fix below, not blocking). VM cooling has no evidence of ever being wired anywhere — still genuinely stub.
  • DELIVER (Hermes messages)FABRIC.md (~line 3452) records this explicitly as "Open, surfaced not resolved": Hermes's message/channel heat already integrates with Stadium's reservoir accounting (STADIUM-HEAT@, STADIUM-RES-PULL/PUSH), but which Hermes lifecycle event maps to DELIVER vs. EXPIRE was never decided, let alone wired. Real, substantial, Hermes-specific integration work.
  • EXPIRE (ACL TTL expiry) — no evidence of any wiring anywhere; ACL.4th/ acl_recheck() has zero Stadium involvement today. Also substantial, separate work.

Why DELIVER/EXPIRE aren't being resolved in the same pass as MIGRATE: each is a full subsystem integration (Hermes lifecycle mapping; ACL-to-Stadium wiring where none has ever existed) in its own right — attempting all four stubs at once risks exactly the rushed, shipped-but-incomplete outcome the no-stubs rule (below) exists to prevent. MIGRATE gets resolved for real because this session already has a tested primitive underneath it; the other three become honest, explicit punch-list items instead of being touched speculatively.

Punch list:

  • Investigated (2026-08-25): block-patron admission does not exist yet, but is architecturally straightforward, not blocked. Confirmed via stadium_admit()'s own doc (stadium.h) that it REFUSES any candidate with mass != 1, and that a mass > 1 multi-cell patron would need a continuation chain nothing has ever designed — this looked at first like a hard blocker for a 1024-byte block. It isn't: confirmed via stadium_word_dispatch()'s real candidate construction (stadium_words.c:245-252) that Stadium cells carry pure identity/heat/bookkeeping only — candidate.identity = word_id, payload[32] unused — the actual word content stays in the dictionary; Stadium never holds it. By the same pattern, a block patron's cell would carry identity = LBN, mass = 1, payload unused — the actual 1024 bytes of block content stays exactly where it already lives (block cache / disk via block_subsystem.c), unmoved. So mass != 1 is a non-issue; the real gap is just that nothing has ever built the LBN→cell_index residency map (the block-patron analogue of stadium_word_dispatch()'s resolve_resident_cell()) or the touch-on-access hook (the analogue of vm_core.c's three stadium_word_dispatch() call sites). Not yet built — this is real, scoped, buildable work, not a stub-around candidate. Still open, plan to be presented before implementation per the no-stubs/no-early-coding conventions.

  • Resolved (2026-08-25): real block-patron admission + real MIGRATE dispatch, both live. New stadium_blocks.h/stadium_blocks.c mirror stadium_words.c's shape (Option B starter-grant admission, redirected Loop #3 cooling, self-healing stale-entry detection) but key residency by (quota_slot, lbn) in a fixed-capacity open-addressing hash table sized off stadium_cell_count() (tombstone-based deletion, since LBN space isn't densely bounded like word_id), not a dense array. Wired into block_word_block()/block_word_buffer()/ block_word_update() (block_words.c), #ifdef __STARKERNEL__-guarded. stadium_dispatch()'s MIGRATE case now calls blk_flush(lbn) for real (confirmed blk_flush(), not blk_subsys_relocate_block(), is the right primitive — the latter is for compudynamics-driven relocation to a different LBN mid-residency, not ordinary reap write-back). Three new Kconfig tuning constants (STADIUM_BLOCK_HEAT_QUANTUM/STADIUM_BLOCK_COOL_RATE_Q48/ STADIUM_BLOCK_TRACK_CAP_MULT) mirror the word-patron ones exactly, same three-layer wiring. Verified: clean compile, zero warnings, on all three architectures; clean boot to zuse)ok>/ok> REPL on all three, conservation (resident_sum + reservoir == Q48_ONE) intact identically across all three; BLOCK/BUFFER touches exercised live from the REPL on amd64 and riscv64 with no crash; a 22,000-distinct-block flood loop (amd64, artificially shrunk to a 20,971-cell Stadium via a one-off smaller -m to make quota pressure reachable) ran clean under heavy admission-path load with no corruption. Live MIGRATE fire confirmed (2026-08-26): interactive flooding alone never triggered it — Hera's reservoir was already sitting exactly at the Q48_ONE / 3 floor from the boot-time self-tests, so every block-touch candidate pulled 0 heat, and a 0-heat candidate can never be strictly denser than an existing resident, so stadium_admit()'s eviction fallback correctly refuses rather than evicts once the free list is exhausted (a pre-existing reservoir-floor/density-eviction interaction, applies equally to word patrons, not introduced by this pass). Closed deterministically instead with a temporary boot probe (kernel_main.c, inserted into the existing Artemis 4.6 self-test block, reverted immediately after capture — no code left behind): 100 65536 0 STADIUM-ADMIT ... STADIUM-EVICT against the live Artemis VM. Captured live: Stadium: dispatch cell=63257 behaviour=MIGRATE lbn=100, followed by DBG err after MIGRATE probe=0blk_flush(100) fired for real, with the correct LBN threaded through from the departing patron's identity field exactly as designed. (Artemis's own reservoir went briefly out of Q48_ONE-balance during the probe — raw STADIUM-ADMIT doesn't debit the reservoir on its own, by its own doc, so a heat value handed to it directly is invented, not pulled; harmless here since Artemis is killed and her whole economy discarded immediately after, and Hera's own conservation was independently confirmed back at the normal 43691/21845/65536 baseline afterward.)

  • Bug found, reported, then fixed on request (2026-08-25/26): capsules/lib.4th:13-14 shadowed the C primitives USE/RUN. While chasing the live-MIGRATE test above, S" Artemis" USE (meant to redirect the REPL into Artemis's own vocabulary, mama_word_use()) instead printed EXEC: failed: Artemis. Root cause: capsules/lib.4th:13 defined : USE ( addr u -- ) EXEC ; — a FORTH word with the same name but a completely different meaning ("load/exec a capsule"), which shadowed the C-registered USE in dictionary search order since lib.4th loads after primitive registration. lib.4th:14 did the identical thing to RUN, which CLAUDE.md also names as an untouchable C primitive ("BIRTH, RUN, USE are primitives — registered in C exactly like DUP, BYE, EXEC"). Same bug class as the K-PUSH dictionary-shadowing issue (docs/working/architecture/K-PUSH-DICTIONARY-SHADOWING-BUG-20260704.md). Reported first per CLAUDE.md's rule against unrequested fixes; Captain Bob then explicitly asked for the fix. Fix: traced every real caller before touching anything — RUN's alias was dead (never called anywhere as bare RUN); USE's alias had exactly one real caller, capsules/hermes/init.4th:397 (S" common:msg.4th" USE, intentionally exploiting the shadow to load that capsule right after lib.4th itself loaded). Both aliases were pure EXEC wrappers with zero added behavior, so the fix deleted both definitions from lib.4th outright and changed the one real call site plus its matching doc comment (capsules/common/msg.4th:4) to call EXEC directly — no new names invented, the C primitives untouched, mkcapsule --lint clean (31/31 pass). Verified live: rebuilt and booted amd64 — Hermes still births and her COMMON-CH-eviction self-test (which depends on common:msg.4th having loaded) still passes exactly as before; interactively, S" Artemis" USE now correctly prints USE: now using Artemis and switches the REPL's console-name coloring, confirming the C primitive runs unshadowed. Clean compile and clean boot with conservation intact on all three architectures (amd64/aarch64/riscv64).

  • Resolved (2026-08-26): real VM-patron admission + real explicit-KILL eviction, both live. Re-scoped on request: confirmed capsule_vm_kill() had zero Stadium involvement (vm_cleanup()/sf_free() only) and child-VM birth only ever called stadium_grant_quota() (a resource pool for the VM's own future word/block patrons) — never stadium_admit() for the VM itself. The only precedent, stadium_birth_hera(), admits Hera into her own quota as a permanently pinned cell 0, which can never reach stadium_evict() — not a working example of COOL firing for a VM. On closer look this turned out NOT to be entangled with the still-iterating Tripod/Zuse/messaging vision after all (§D) — birth and kill already funnel through two single choke points, so the earlier 2026-08-25 deferral was overcautious. Design: added size_t stadium_patron_cell to VMRegistryEntry (capsule_run.h). At birth, right after the existing stadium_grant_quota() call (capsule_birth.c), admit a candidate into the new VM's own quota mirroring stadium_birth_hera()'s shape (identity=0, mass=1, behaviour=COOL) but deliberately unpinned — pinning would need a new "unpin" primitive (none exists) to ever evict it later, and adding a pin-bypass to stadium_evict()'s refusal logic isn't something to do casually; unpinned costs nothing since nothing wires COOL's dispatch body to actually kill anything, so the worst case of an unrelated natural eviction is stadium_patron_cell going stale, which is tolerated the same way stadium_word_forget() already tolerates staleness. At capsule_vm_kill() and capsule_vm_kill_all_nonmama(): stadium_evict() the tracked cell if still resident, silently tolerating refusal (already gone). stadium_dispatch()'s COOL case needed no new payload body — same as it already is for words, where COOL has no defined extra action beyond stadium_evict()'s own universal reservoir credit; the missing piece was admission and a genuine trigger, not dispatch-body logic. Verified live: a second, new Stadium: dispatch cell=... behaviour=COOL now fires immediately before every PARITY:KILL line, for both Hermes and Artemis, confirmed on amd64 (distinct from the pre-existing COMMON-CH word-eviction self-test's own COOL print). Conservation (resident_sum + reservoir == Q48_ONE) intact throughout. Clean zero-warning compile and clean boot on all three architectures (amd64/aarch64/riscv64).

  • **Re-scoped and resolved (2026-08-26): DELIVER (Hermes) was never actually a gap — the earlier "zero consumer, needs substantial Hermes lifecycle mapping" framing above was wrong, carried over unverified from FABRIC.md's old "open, not resolved" note about which Hermes event maps to DELIVER vs. EXPIRE. Item 4.2 already answered that in code (messages → DELIVER, channels → COOL) without the prose ever catching up — same documentation-drift class as the stale ONTOLOGY.md words note and the earlier glibc misattribution. Confirmed live: capsules/hermes/init.4th's MSG-ALLOC already admits every message with SB-DELIVER, and MSG-FREE-NODE (called from both MSG-ACK-LAST and heat-driven MSG-REAP) already evicts it — behaviour=DELIVER (stub) has been printing on boot logs since at least 2026-08-05. Checked whether the dispatch body needed a real payload action the way MIGRATE did: MSG-DELIVER (the FORTH word) already runs the actual delivery (VM-EXEC of the payload) before eviction, decoupled from Stadium reap — so by dispatch time delivery is already done, same shape as COOL, which needs no extra action beyond stadium_evict()'s own universal reservoir credit. Fix: stadium_dispatch()'s DELIVER case now prints the departing message's real identity (DELIVER msg_idx=N, same shape as MIGRATE's lbn= print) instead of a misleading (stub) label — confirmed live via a forced MSG-SEND/MSG-DELIVER-ALL/MSG-ACK-LAST sequence from Hermes's own REPL context (Stadium: dispatch cell=73653 behaviour=DELIVER msg_idx=1). COOL's case was in the identical situation (real for both words and VMs, no extra action needed) and, on request, got the same fix (2026-08-26): now prints COOL identity=N (word_id for a word, 0 — the patron-zero convention — for a VM) instead of (stub). Confirmed live: both shapes fired correctly on the same boot — COOL identity=0 at Hermes's/Artemis's own explicit channel-eviction self-test and again at their VM-patron eviction at PARITY:KILL, COOL identity=1 at a second channel eviction — conservation intact throughout. Clean zero-warning compile and clean boot on all three architectures for both fixes.

  • EXPIRE (ACL) — confirmed genuinely unscoped (2026-08-26), not a case of stale documentation like DELIVER turned out to be. Two findings below, then four open questions — decided directly on request (2026-08-26), decision recorded after the questions, no code written (a decision isn't a green light to build, per this session's own convention):

    Finding 1 — acl_ttl and Stadium's ttl are different things wearing the same name. DictEntry.acl_ttl (vm_core.c:756) is a per-word countdown that batches how often ACL-RECHECK runs — when it hits 0, acl_recheck() calls the FORTH word ACL-RECHECK (ACL.4th:50), which always renews: STRICT mode sets allow=1, ttl=0 (recheck every time); TTL mode computes a fresh heat-based TTL and sets allow=1. Denial isn't a live path anywhere in current policy. This is a renewal cycle, not a residency-ending event — nothing about it resembles "leaving the Stadium floor."

    Finding 2 — StadiumPatronHeader.ttl is completely inert. Every candidate constructor across the whole codebase (stadium.c, stadium_words.c, stadium_blocks.c, Hermes's MSG-ALLOC/CH-ALLOC) sets candidate.ttl = 0, and nothing anywhere ever reads, decrements, or reaps on it. The generic TTL-expiry mechanism EXPIRE would need to fire from doesn't exist in Stadium's own engine at all — a gap one level deeper than "ACL isn't wired to Stadium."

    Open questions:

    1. Is "ACL patron" even the right model, or was FABRIC.md's original "ACLs → EXPIRE" mapping a category mismatch from the start — conflating acl_ttl's recheck-amortization counter with Stadium's residency ttl?
    2. If it is the right model: what gets admitted as a patron? One per ACL-guarded word would be redundant with the word's own patron cell item 4.1 already tracks. A different unit (e.g. per zuse session) might fit better once PKI/session auth lands (Phase 8, still open per .claude/CLAUDE.md's ACL section).
    3. Building this for real means building Stadium's generic ttl-decrement/reap-on-zero mechanism first — nothing to hook EXPIRE into today. Is that in scope here, or its own separate item?
    4. What should the reap action actually do, given current ACL policy never revokes — would EXPIRE force an ACL-RECHECK, or something else entirely?

    Decision:

    1. Not at the per-word level. acl_ttl-hits-zero always renews, never revokes — forcing EXPIRE's residency-ending tag onto it would misuse the tag. But "ACLs → EXPIRE" isn't wrong in spirit, just aimed at the wrong unit: the one place in this system where something ACL-related genuinely has a lifetime and should be revoked is a zuse superuser session (Phase 8, not yet built) — authenticate, hold elevated privilege for a bounded time, then actually drop back to non-zuse. That's a real residency-ending event; per-word recheck isn't.
    2. A zuse session, not a per-word ACL entry — a session is the thing with a genuine start/lifetime/end. A word already has its own patron via item 4.1; a second one for ACL purposes would be redundant bookkeeping, not a new concept.
    3. Not in scope now. This is the decision that actually resolves the item: Phase 8 doesn't exist yet, so there is no session to admit as a patron regardless of any other choice made here. Building the generic ttl-decrement/reap mechanism now, with nothing real to feed it, would be speculative infrastructure ahead of its only consumer — close in spirit to what the no-stubs standard exists to prevent, just inverted (a real mechanism with no real caller, instead of a fake mechanism with a real caller).
    4. Revoke the session's elevated privilege and drop the console back to its non-zuse state — a real action, unlike DELIVER/COOL, which needed none.

    EXPIRE stays explicitly deferred until Phase 8 (PKI/zuse session minting) lands — not abandoned, not left ambiguous: revisit it as part of that work, admitting the session itself (not a word) as the patron, once there is something real for it to represent.

  • Fixed (2026-08-26): ONTOLOGY.md §IX's "words (dictionary, warehouse-resident today, not yet migrated)" line was stale — corrected to state words are fully migrated and live via stadium_word_dispatch(). Doc-only, no build/boot verification needed.


C. Standing rule: no stubs or TODOs, ever

Stated directly, 2026-08-25, after the stadium_dispatch() stub investigation above: "I've never allowed stubs before." Saved as a persistent memory (feedback_no_stubs_or_todos.md) so this applies across sessions, not just this one. Full statement: no stub function that prints a placeholder and returns, no TODO-and-move-on comment in place of real logic, in any language, ever committed as if it were finished work. Small, honest increments are still fine and encouraged — each increment just has to be a complete, real implementation of whatever slice it covers, never a placeholder for a later slice. A pre-existing stub found while working nearby (as here) gets flagged and resolved, not built on top of or left in place.


D. Tripod final shape — minting, one-time Zuse fuse, messaging-only (vision, not yet scoped)

Stated directly by Captain Bob, 2026-08-25: "we're going to have to iterate because I know what the final shape of the tripod will be." Recorded here as a forward-looking vision, not yet broken into implementable items — captured so near-term Stadium/VM work (§B above) is made against the right end-state rather than in ignorance of it. Full detail also saved as memory project_tripod_final_shape_vision.md.

  • Thumbdrive presentation → legality check → mint. A newly presented thumbdrive is checked for legality (against the CA-root-derived identity/certificate scheme already decided earlier this session, FABRIC-2.md). If not legal, it is "minted" — formatted for system use — which (1) spins a new user VM and (2) attaches the console VM to it.
  • One-time Zuse mint + fuse-blow on first install. A brand-new system instance ("Install"/"Try It") mints exactly one Zuse superuser, then irreversibly "blows a fuse": direct quote, "we mint one and only one Zuse user and blow a fuse. The only way around is a new system." Post-fuse, no further Zuse can ever be minted on that instance — but the system is NOT bricked: the existing Zuse superuser keeps working, and ordinary users can still "thumb in" via regular thumbdrives.
  • Messaging-only once Tripod is fully live. All inter-VM interaction becomes Hermes messaging, not direct calls/shared state — a stated end-state, not the current implementation.
  • Polymorphic block-boundary behavior for user VMs. Stated in one sentence, not yet elaborated: a user VM needs to behave polymorphically when crossing block boundaries. Needs a dedicated follow-up conversation before this is actionable.
  • ClaudeEXPORT/ (repo root, new as of 2026-08-25) — a prior Claude data export the user pointed at for "concepts and thoughts as guidelines" on this vision, explicitly flagged as possibly containing superseded/conflicting ideas, not authoritative. conversations.json is ~66MB; mine selectively (grep or a subagent) rather than reading wholesale. See memory project_claude_export_archive.md.

Why this affects §B: VM-COOL (and any inter-VM Stadium wiring) sits close to this still-iterating design. Building it now risks conflicting with or being thrown away by the Zuse/messaging shape once that gets its own pass — hence §B's punch list defers VM-COOL explicitly rather than resolving it in this pass. Block-patron MIGRATE work does not obviously depend on this vision and is not deferred for this reason.

Next step: no implementation here yet. When this gets its own design pass, start by clarifying "polymorphic block-boundary behavior" and the exact legality-check mechanism (presumably the CA-root certificate scheme), then scope minting as its own capsule-birth-style protocol (mirroring the existing capsule birth writeup's rigor) before writing any code.