The last genuine design question in the reshuffle, and once again the mechanism already exists, built for the structurally identical problem and corrected twice in the field. Delivery must execute the payload inside the target, but §XXXII retired the pump so kernel-Hermes may not VM-EXEC into anyone. The payload must wait somewhere the target consumes under its own power, at a moment when doing so is safe -- and safe is the hard part, since interpreting mid-word, mid-unwind or nested inside someone else's dispatch is the reentrancy class this codebase has been bitten by repeatedly. vm_core.c already has all of it: sk_vm_at_outermost_interpret() as the predicate, a per-word checkpoint in execute_colon_word() gated on it, a defer-don't-lose discipline for the nested case whose own comment explains that a nested word does not own the stack it is running on, and placement before the error and unwind checks so it only acts when the VM is in a clean resumable state. That is the exact predicate, placement and deferral semantics delivery needs, because the switcher had to answer the same question. Ruling: kernel-Hermes enqueues and publishes the fact, dispatching nothing; the target drains its own queue in its own context at its own outermost checkpoint. One published fact, two independent consumers -- the switcher for eligibility, the VM itself for drain -- and neither dispatches into anyone. Notes this is §XX's proven pattern generalized: the pump's defect was never draining but draining from outside, and Hera was special-cased into safety. Retire the pump and every VM does what she already does, so the special case disappears by becoming universal. Also notes recursive drain is prevented for free, since interpreting increments the same depth counter that defines the boundary. Names three constraints rather than leaving them to be discovered: INPUT_BUFFER_SIZE is 1025 so a payload above 1024 bytes cannot be interpreted in one drain, starvation is real but is the switcher's existing risk rather than a new one, and draining one message per checkpoint rather than the whole queue keeps the work bounded. FABRIC-3.6.md: B3 cleared, B4 added for the payload bound. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
11 KiB
FABRIC-3.6.md — the Tripod/kernel reshuffle: execution log
Status: LIVING WORKING DOCUMENT, opened 2026-09-19. This is the execution record for
the reshuffle designed in FABRIC-3.5.md. Work is tracked, annotated and closed here.
Why this is a separate document, per the series' own rule. FABRIC-1.md closed at 4,420
lines for a stated reason: "continuing to append here made the still-open work hard to find."
FABRIC-3.5.md stands at 4,452 lines with 40+ open punch items scattered among hundreds of
settled rulings — it has crossed the same threshold, for the same reason. Annotating a task
list inside it, commit by commit, would bury the design record it exists to be.
The split of responsibility is strict, and stated because FABRIC-3.5.md §XXXI found that
documents in this series lose track of each other:
FABRIC-3.5.md |
FABRIC-3.6.md (this) |
|
|---|---|---|
| Holds | The design record — every ruling, its reasoning, its evidence | The work — tasks, results, dates, commits |
| State | Design phase closed; archival close at v2.1.0 (§XXVI.5) |
Living until the work is done |
| On a conflict | Authoritative | Defers, and records the discrepancy |
Design rulings are never restated here, only cited (§XXXIV.2, §XL.4, …). If a task needs
a rule explained, read FABRIC-3.5.md. If executing a task proves a ruling wrong, that is a
finding: record it here and amend FABRIC-3.5.md there — never silently diverge.
The checkbox convention is deliberate. FABRIC-3.5.md §XXXI.2 found the series'
carry-forward discipline was mechanically auditable (grep -c '\- \[ \]') up to FABRIC-2.md,
and broke at FABRIC-3.md, which uses no checkboxes at all — after which "is anything still
open?" stopped being a grep and became a reading exercise. This document restores the
convention, so that question stays answerable by machine.
Standing rules
Apply to every task, from .claude/CLAUDE.md and FABRIC-3.5.md §XXXIV:
- One task, one commit. No bundling.
- Acceptance is the three-architecture QEMU boot —
clean qemu, amd64/aarch64/riscv64, one at a time, in the foreground. Logs committed. There is no other acceptance test. dict_hashchanging is expected.dict_hashdiverging between architectures is a stop condition (§XXXIV.6).mkcapsule --lint capsules/clean after any capsule change.- No task starts without explicit authorization. Captain Bob's Law.
- Report, don't fix. A defect found while doing a task is recorded here, not repaired in passing.
Annotation convention
- [x] 0.2 — Strip common/msg.4th
2026-09-DD · commit abc1234 · 3-arch boot clean, lint 0 violations
note: <anything surprising; a finding gets its own entry below>
Mark [~] for started-not-finished, with what is outstanding. Never mark [x] on a task
whose check did not actually run — FABRIC-3.5.md §XXXV.6 records that declaring done too
early is this project's most repeated failure.
Phase 0 — Preparation (no behaviour change)
Gate: all three architectures boot to zuse)ok>, stadium_conserved() true, no
UNKNOWN WORD.
- 0.1 — Establish Category A reachability by §XXII.2's three routes (boot path, tooling, baked capsule directory). Never by grep alone. Check: a written list naming the route that proves each entry dead.
- 0.2 — Strip
capsules/common/msg.4th. Check: 3-arch boot; lint clean. - 0.3 — Strip
capsules/process.4th(takesEVENT-EMIT/-WAIT/-DRAINwith it, §XXXIII.3). Check: 3-arch boot; lint clean. - 0.4 — Strip
SPAWN-EVENT. Check: 3-arch boot; lint clean. - 0.5 — Correct
capsules/MANIFEST.mdblocks 4055 and 2049 as their files are stripped (§XXII.5). Check: manifest describes no file that no longer exists. - 0.6 — Return freed block ranges to
capsule-reserved.txt. Check: lint clean. - 0.7 — Add
stadium_conserved():Σ patron + reservoir + consumed == Q48_ONE, epsilon zero (§XL.4, item 41). Check: true on a clean boot, all three arches. - 0.8 — Audit that
PLOT/FB-WIDTH/FB-HEIGHTare registered nowhere but the table Hestia will own (item 33). Check: read-only; a second site is a defect to report.
Phase 1 — Hestia (messaging untouched)
Gate: Tripod is Hera/Artemis/Hestia plus Hermes; drawing works from Hestia and only Hestia; headless policy intact.
- 1.1 — Allocate Hestia's block range vs.
capsule-reserved.txt, avoiding 4997. Check: lint clean. - 1.2 — Create
capsules/hestia/init.4th: messaging load,MSG-CD-INIT, banner. No fabric yet. Check: boots; Hestia not yet birthed. - 1.3 — Add Hestia to
is_fleet_foundation(capsule_birth.c:793-796) — four names, Hermes retained (§XXXIV.4). Check: 3-arch boot; Hermes still live. - 1.4 — Birth Hestia in
kernel_main.c, alongside Hermes's existing birth. Check: registry shows both;dict_hashidentical across arches. - 1.5 — Register Hestia for switch signals (the
:1007pattern). Check: boot clean; no switch storm (§XXVIII.2's shape — and note it is invisible by default, §XXXV.0). - 1.6 — Move
fabric.4thfrominit.4thtohestia/init.4th. Check: Hera's dict shrinks, Hestia's grows; cross-arch identity holds. - 1.7 — Move
font.4thlikewise. Check: same. - 1.8 — Move
PLOT/FB-WIDTH/FB-HEIGHTregistration to Hestia's table only. Check: positively verify a non-Hestia VM callingPLOTgetsUNKNOWN WORD. - 1.9 — Assert §XVIII.6's headless invariant: Hestia's birth sets no
g_wirebind_attached_usernameand mints no proxy. Check: boot headless, no thumbdrive, no prompt appears.
Phase 2 — Allocator and audit (inert) — the real go/no-go
Gate: task 2.7. If it fails, stop and re-plan. Do not proceed to Phase 3.
- 2.1 — Kernel-Hermes message/membership structures, wired to nothing, drawing no
heat. Check: boot byte-identical;
dict_hashunmoved. - 2.2 — Allocate: pull
Q.SLOTfrom the caller's reservoir; roll back on refusal. Check: N allocs against a known reservoir; refusal at the right count. - 2.3 — Release: return remaining heat via the eviction path. Check: reservoir restored exactly for an undecayed message.
- 2.4 — The four counters:
held,pulled,returned,consumed(§XL.4). Check: each increments at exactly one site. - 2.5 — Decay: apply, and record the delta into
consumed(§XXXVII.3). Check:consumedgrows by exactlyheat_before − heat_after. - 2.6 — Self-audit:
held == pulled − returned − consumed, epsilon zero. Check: holds across the cycle; deliberately corrupt a counter → audit fires on the first unit. - 2.7 — Stage B proof (§XXXIV.3 as corrected by §XXXIX.4): alloc/free cycle
verifying (a) the ledger and (b)
stadium_conserved()before and after. Check: both true, all three arches.fleet_conservedis not evidence here — it cannot see Stadium heat (§XXXIX.1). - 2.8 — Scan-based cross-check of the counters, diagnostics only, off the hot path (§XXXVII.4). Check: scan agrees with counters.
Phase 3 — Cutover — BLOCKED
Not buildable until all three clear. Shape once unblocked (§XXXIV.3): cut over
BLK-ATTACH-EVENT alone, then remaining types one at a time, under §XXXIV.2's partition rule —
one message type owned by exactly one layer, no message shared.
- B1 — Item 27: channels — negotiation, or one broadcast membership? (§XXXIII.5 recommends the latter; unruled.)
- B2 — Item 32:
SK_SWITCH_MAX_SLOTSis 16 and §XXXII made the switcher the sole mover of control. Constant bump or table redesign? - B3 — CLEARED 2026-09-19 by
FABRIC-3.5.md§XLIII: the target drains its own queue at its own outermost interpret checkpoint; kernel-Hermes publishes and never dispatches. Reusessk_vm_at_outermost_interpret()and the Stage 3 checkpoint. - B4 — Item 44: payload bound or chunking against
INPUT_BUFFER_SIZE1025 (§XLIII.6.1). Decide before Phase 3.
Phase 4 — Category B strip
Only after every live type is cut over. Hermes leaves is_fleet_foundation and
kernel_main.c here, not earlier (§XXXIV.4).
- 4.1 — Strip
capsules/hermes/init.4th. - 4.2 — Strip the FORTH routing table and slot-3 pairing convention.
- 4.3 — Strip
capsules/common/messaging.4th. - 4.4 — Remove Hermes from
is_fleet_foundationand its birth fromkernel_main.c.
Phase 5 — Close-out
- 5.1 — Isabelle/HOL pass. Deliverable is the restated boundary, explicitly including §XXV.4's coverage loss — not a green build (§XXV.3).
- 5.2 — Documentation sweep, grepping by exclusion (§XXVIII.3): CLAUDE.md's four
errors,
MANIFEST.md, theTRIPOD.md/0.1 contradiction, item 42's K-qualification, the superseded subsystem docs' update-or-archive call. - 5.3 —
make sbom; checkCreated:andDocumentName(§XXVI.2). - 5.4 —
LITHOS_VERSION = 2.1.0; engineVERSIONper §XXX.6's rule; roadmap table gains its2.1.0line in two live places (§XXVIII.2). - 5.5 — Resolve the stray
refs/heads/v2.0.1and PR #1 (§XXVI.4). Investigate, do not delete. - 5.6 — Merge to
master; tagv2.1.0. - 5.7 — Archival close of
FABRIC-3.5.md(§XXVI.5) and of this document.
Findings log
Defects and surprises found while executing. Reported, not fixed (unless the task was to fix
them). A finding that contradicts a FABRIC-3.5.md ruling must also be amended there.
(none yet — work not started)
Out of scope, carried for visibility
Recorded in FABRIC-3.5.md, deliberately not part of this reshuffle. Listed so they are not
absorbed by accident:
- Item 43 — should a Stadium reservoir ever replenish? (§XL.6) A VM's send capacity declines monotonically today.
- Items 23–26 — the
FABRIC-3.5.md§XXXI gap-analysis follow-ups: re-homeFABRIC-2§17.4, consolidate the three reported-not-scheduled registries, decideFABRIC-2's five orphaned design items, reconcileFABRIC-0's seven opens. src/*.c.bak— tracked-but-stale, reported in three places and actioned in none (§XXII.5).