refs/heads/v2.0.1's tip is the exact merge-base with master -- a strict
ancestor, fully subsumed, confirmed by its own commit message ("master
fast-forwarded to v2.0.1, verified"). Stale ref hygiene debt, safe to
delete, not deleted here -- needs Captain Bob's explicit go-ahead.
PR #1 turned out not to be a stray reference at all: it's a PR from this
same branch against master, already closed (not merged) 20 seconds after
it was opened on 2026-09-18, before any of this reshuffle's work existed.
Nothing to resolve beyond recording what it is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Advisor review of the Phase 5 close-out commit caught two real issues:
- Task 5.1's write-up claimed the Isabelle pass "confirms no regression" --
overstated. Nothing in proof/'s scope changed, so the pass isn't
regression evidence, it's a build-completeness formality; the boundary
argument alone already supports the real conclusion. Sharpened.
- sbom.spdx was regenerated before the 5.4 version bump, so it briefly
understated the engine version. Root cause: the hosted Makefile carries
its own separate hardcoded VERSION (Makefile:17, still 3.1.0), distinct
from Makefile.starkernel's copy that 5.4 bumped -- duplication CLAUDE.md's
own "two independently tracked version strings" note doesn't document.
Bumped to 3.2.0 to match, regenerated (PackageVersion now correct), and
rebuilt the hosted starforth binary so lfs/amd64/starforth reflects it.
Also recorded why 5.1-5.4 landed as one commit (deviation from this
document's per-task-commit discipline) and sharpened the riscv64 log
entry so the FAILED run and the accepted retry aren't ambiguous to a
future reader grepping logs/.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.1: Isabelle/HOL pass (52 theories, clean) -- restated the boundary rather
than just citing the green build: proof/ scope was already entirely
outside this reshuffle's footprint (src/starkernel/, capsules/*.4th),
so the boundary is unchanged, not moved.
5.2: Documentation sweep. CLAUDE.md's stale WIP banner and Tripod fleet
description updated now that Phases 0-4 have actually landed (Hera/
Artemis/Hestia, no Hermes). MANIFEST.md rides the strip -- hermes/init.4th's
block table replaced with a deletion note, init.4th/doe-campaign.4th/
hestia/init.4th entries corrected to match the post-strip live files.
Confirmed the TRIPOD.md/0.1 contradiction was already resolved (2026-08-13).
Settled the superseded-docs call explicitly: archive as-is, do not rewrite.
Fixed experiments/bare_metal/README.md's block-size framing (still said
1024-byte budget; real rule is 64 chars x 16 lines). K-qualification
checked clean against the two living documents; full retroactive sweep
of the closed archival FABRIC corpus explicitly declined as disproportionate.
5.3: make sbom. Installed syft (user-local, approved). Found and fixed a
real Makefile bug while at it -- the sbom target hardcoded
--source-name StarForth, so DocumentName was wrong even after regenerating.
5.4: LITHOS_VERSION 2.0.0 -> 2.1.0, engine VERSION 3.1.0 -> 3.2.0 (minor,
per the dictionary-visible-only rule). Replaced the stale version-comment
block in Makefile.starkernel (had the odd/even LTS rule backwards) and
docs/lithosananke/ROADMAP.md's retired versioning-policy section with the
ratified ladder. Verified on all three architectures; riscv64's first pass
hit a transient virtio_blk timeout during boot-time Zuse genesis mint,
reported and confirmed non-reproducing on an immediate clean retry.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
All FORTH-owned message types were cut over to kernel-Hermes in Phase 3
(tasks 3.8-3.10). This removes the now-dead FORTH messaging layer and
the Hermes VM itself: capsules/common/messaging.4th, capsules/hermes/init.4th,
the slot-3 VM-NAME-REG pairing convention, and every load-site/birth-site
reference across capsules/init.4th, artemis/init.4th, hestia/init.4th,
doe-campaign.4th (Artemis-only now), capsule_console.c, capsule_mint.c,
capsule_wirebind.c, capsule_birth.c, and kernel_main.c.
Verified on all three architectures: clean boot, mkcapsule --lint clean
(36 files, 0 violations), zero UNKNOWN WORD, identical dict_hash across
amd64/aarch64/riscv64 for every VM, and a full mint -> WIREBIND-attach ->
USE -> relay round-trip exercising the two highest-risk edits
(capsule_console.c/capsule_mint.c).
Found, not fixed: deleting messaging.4th removes SEND-ELEVATE-REQUEST,
which was the only caller of KH-ELEVATE-SEND and the only path to
ELEVATE-GRANT (zuse-eligibility.4th, still loaded at boot) -- Phase 8
PKI's own elevation entrypoint. Needs a decision before Phase 5 close-out.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reachability verified live before writing any code, per this
project's own standing rule (grep cannot establish reachability
alone): FIND SEND-ELEVATE-REQUEST / FIND ELEVATE-GRANT / FIND
CH-REQUEST all resolve on a live Hera boot, though grep across
capsules/experiments/docs found zero callers of SEND-ELEVATE-REQUEST
-- a real, complete, directly-callable entrypoint (H.5/H.8's own
design) with no current automatic trigger, not dead code.
Correction to a prior finding, made in the course of this check: task
3.8's write-up claimed "Hera's own pre-existing inability to load
common:messaging.4th" -- false. capsules/init.4th (Hera's own
MAMA_INIT capsule) loads it directly, and SEND-ELEVATE-REQUEST lives
and works in her dictionary right now. Task 3.8's own actual scope is
unaffected by this correction.
Cutover: SEND-ELEVATE-REQUEST (messaging.4th) no longer ends in
CH-REQUEST's COMMON-CH/MSG-SEND path; it now calls KH-ELEVATE-SEND
(repl.c), a new C word wrapping sk_hermes_send_one(), registered
unconditionally for every VM. from/to are derived from the calling VM
and sk_get_mama_vm() directly in C, never taken from the stack -- a
real correctness improvement over CH-REQUEST's own initiator-only
gate, which only existed because a caller COULD pass the wrong from
value; deriving it in C makes that spoof structurally impossible.
SK_HERMES_MSG_TYPE_ELEVATE_REQUEST reuses ELEVATE-REQUEST's own value
(8), same partition-rule reasoning as tasks 3.8/3.9. Delivery is
unchanged task 3.4 machinery. No new static-buffer lifetime caveat --
the payload-aliasing fix landed before this task started.
CH-REQUEST (messaging.4th) is now dead code, its one real caller just
removed -- found, not fixed, per Captain Bob's Law.
Verified live on all three architectures: 0 0 0 0 S" DUP"
SEND-ELEVATE-REQUEST (deliberately-invalid pubkey, so ELEVATE-GRANT
correctly refuses -- the check is the pipeline running, not a grant
succeeding) fires the evidence line and completes cleanly, DUP
unaffected afterward. Zero UNKNOWN WORD, dict_hash identical across
all three architectures (changed uniformly from prior runs -- one new
word registered -- not diverged, matching SXXXIV.6's own rule).
This closes task 3.11 (Phase 3 gate): all of messaging.4th's live
FORTH-owned message types (BLK-ATTACH-EVENT, CONSOLE-CMD-EVENT,
ELEVATE-REQUEST) are now real kernel-Hermes cutovers. Phase 4 may
begin.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two separate fixes, found and closed together after task 3.9/prompt-
format landed (Captain Bob: "finish the work first then we'll do
cleanup before 3.10 begins").
1. Logging noise (confirmed live via QMP screendump): three sources
were cluttering ordinary interactive console sessions.
- INFERENCE "Output validation failed, ignoring results"
(vm_runtime.c, kernel; vm_time.c, hosted mirror) and xhci "CSW
status = FAILED"/"unit not ready -- retrying" (xhci.c) were
already log_message(LOG_WARN/LOG_ERROR, ...) calls, just visible
at the default runtime LOG_WARN level -- downgraded to LOG_INFO,
all three fire routinely and self-resolve (xhci.c's own existing
comment already documents the retry as expected SCSI UNIT
ATTENTION behavior, not a driver defect).
- Stadium: dispatch cell=... (stadium.c's stadium_dispatch()) was a
genuine defect: an unconditional console_puts()/console_println()
sequence with no level gating at all, printing on every single
dispatch. Rewritten through log_message(LOG_DEBUG, ...).
2. CRLF double-submit (found while investigating why the cleaned-up
noise still didn't look like a normal single-VM session):
sk_console_readline() (repl.c) breaks on '\r' OR '\n' as independent
terminators, so a line sent as both bytes submits twice -- the real
line, then an immediate empty-line submit on the second byte, each
printing its own " ok". Pre-dates Stage D entirely, not an async-
relay artifact. Fixed with a single non-blocking peek-and-discard
for the paired byte right where the line terminates.
Verified live on all three architectures for both fixes: zero UNKNOWN
WORD, dict_hash identical to every prior acceptance run in this
document (both changes are display/interaction-only).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
sk_hermes_send_one() -- the single funnel every sender, including
sk_hermes_publish(), already goes through -- used to store the
caller's own payload_addr pointer as-is. Two sends before either
drains meant both messages pointed at the same caller-owned buffer,
whichever send wrote last silently winning: real, confirmed live (a
second identity thumbdrive attached at boot alongside Zuse's own left
its WIREBIND pairing silently never happening).
SkHermesMessage gains an inline payload_buf[SK_HERMES_CHUNK_MAX_
PAYLOAD] field; sk_hermes_send_one() now memcpy()s the caller's
payload into it and points payload_addr at that copy instead. No
sender or reader call site needed to change -- every existing reader
already only ever reads through payload_addr, which still points at
valid bytes of the same length, now message-owned. g_kh_blk_attach_buf/
g_kh_console_cmd_buf (repl.c, tasks 3.8/3.9) no longer need to survive
past their own send call; their doc comments, which had claimed the
old aliasing shape was benign, are corrected.
Verified the original bug is actually gone: reproduced the exact
original scenario (a real, sequentially-minted rajames identity
attached at boot alongside Zuse's own, two simultaneous BLK-ATTACH-
EVENT sends in one idle-loop pass) -- WIREBIND pairing, USE, and the
Stage D relay all now work where WIREBIND previously silently failed.
Standard three-ISA acceptance also clean: zero UNKNOWN WORD, dict_hash
identical across all three and matching every prior acceptance run in
this document.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
sk_console_user_prefix() (repl.c) previously always returned "zuse"
(or the WIREBIND-attached username) as the left side of the bracket
prefix, regardless of which VM the console was actually pointed at --
"[zuse@rajames]" after USE rajames, always showing the authenticating
superuser rather than the active identity.
Changed on Captain Bob's direct instruction: once the console is
redirected into a WIREBIND identity's own console VM
(console_get_vm_name() != "Hera"), show that same name on both sides
-- "[rajames@rajames]" -- since WIREBIND births the console VM
literally named after the identity, so the identity IS that VM, not a
separate label. At the top level (still on Hera, nothing has
redirected yet), the original zuse_session/WIREBIND-username logic is
unchanged.
An earlier, more ambitious attempt (separate identity/machine tracked
state across every console_set_vm_name() call site) regressed live to
a wrong [zuse@Artemis] prompt and was fully reverted before reaching
any acceptance run -- the landed fix needed none of that new state,
just this one function.
Verified live on all three architectures: [zuse@Hera] at the top
level and after a live WIREBIND attach (before USE), [rajames@rajames]
after USE rajames, with task 3.9's Stage D relay still firing
correctly on top of it. Zero UNKNOWN WORD, dict_hash unaffected
(display-only change).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Send-side cutover: sk_repl_dispatch_line()'s FORTH-string
"CONSOLE-CMD-EVENT 0 3 S\" ...\" 0 MSG-SEND" interpret is replaced with
a direct sk_hermes_send_one() call (SK_HERMES_MSG_TYPE_CONSOLE_CMD,
kernel_hermes.h, deliberately reusing CONSOLE-CMD-EVENT's own value 7,
same partition-rule reasoning as task 3.8's BLK-ATTACH-EVENT cutover).
Real finding along the way: kernel-Hermes's drain only ever runs as a
side effect of vm_interpret() being called on the target VM. Task
3.8's target (Hera) is always being interpreted via the interactive
REPL loop; task 3.9's target is a WIREBIND identity's own ~user VM, a
passive receiver nothing else drives. The existing idle-loop pump only
ticked VMs with the old FORTH MSG-TICK word ACL-allowed -- a VM minted
with the STD79-lockdown personality never has it, so the pump silently
skipped it forever and queued messages never delivered. Fixed by
adding an unconditional, direct sk_hermes_drain_checkpoint() call in
the same pump loop, independent of the MSG-TICK gate.
Verified live on all three architectures: WIREBIND-attach a real
identity, USE into it, type a plain console line, confirm the Stage D
evidence line and correct relayed execution result. Zero UNKNOWN WORD,
dict_hash identical across all three ISAs and matching task 3.8's own
baseline. The FABRIC-3.md-documented USE/BINDSTEP crash did not
reproduce in any of these live sessions (recorded as a finding, not
chased further).
Depends on the zuse_root_pubkey_known fix already landed in ac4d431 --
without it, WIREBIND cannot attach any identity on a fresh boot at
all, which blocked this task's own verification until found and fixed
separately.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
capsule_zuse_boot_load_root_pubkey() is the only writer of
zuse_root_pubkey_known, and it only ever runs once, synchronously, at
Artemis's boot-time virtio-blk attach -- before genesis mint has
happened on a fresh artemis.img, so it finds no marker yet and leaves
the flag 0. Nothing re-triggers it after genesis mint completes.
Silent result: capsule_wirebind_try_attach()'s own
`if (!mama_vm->zuse_root_pubkey_known) return;` gate then refuses
every identity attach for the rest of that boot, with no message at
all -- meaning WIREBIND was silently dead on any boot that reset
artemis.img fresh, which is exactly what this project's own
acceptance convention does before every run.
Found live verifying FABRIC-3.6.md task 3.9's console-session
acceptance. Fixed at the source: install_and_activate() already has
the root pubkey in hand (from genesis mint or an already-Zuse
re-attach) -- activate it directly there instead of relying on a disk
re-read that may never happen in-session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
While orienting for task 3.9 (CONSOLE-CMD-EVENT cutover, per Captain
Bob's ruling to verify via a real QMP/serial-socket console session),
booting with a second real identity drive attached alongside Zuse's
own exposed a real defect in the already-closed task 3.8 code:
g_kh_blk_attach_buf (repl.c) is one static buffer, and
sk_hermes_send_one() stores payload_addr as a caller-owned pointer,
not a copy. Two real USB-MSC attaches in one sk_repl_idle() pass send
before either drains, so both messages alias the same buffer -- only
one identity ever completed.
Task 3.8's own write-up claimed this "carries the same single-buffer-
reuse shape ATTACH-ACK-BUF itself already had... not a new hazard" --
that claim was wrong and is amended in FABRIC-3.6.md's findings log.
FORTH's own MSG-SEND had the identical pointer-aliasing shape but
never hit the window: MSG-TICK drained from the same sk_repl_idle()
pass that queues attaches. Kernel-Hermes drains at interpret
checkpoints, which don't fire during that pass at all -- the cutover
changed not just how delivery happens but when, opening a window
FORTH's own design never had.
Reported, not fixed here, per Captain Bob's Law: this is a defect in
closed task 3.8 code, found while scoping a different task. A real
fix changes SkHermesMessage's own shape to own its payload bytes
rather than reference a caller's pointer -- bigger than a repl.c
patch, touches every existing sender, needs its own three-ISA
acceptance.
Task 3.9's own send-side cutover (kernel_hermes.h, repl.c) is written
but deliberately left uncommitted -- it would inherit the identical
defect shape if shipped now. logs/20260922-111840/amd64/ is the
reproduction.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The reply leg (Artemis -> Hera ack) that used to flow through
common:messaging.4th's MSG-SEND/MSG-TICK now goes through
kernel-Hermes's sk_hermes_send_one()/sk_hermes_drain_checkpoint()
instead -- FORTH Hermes never sees a BLK-ATTACH-EVENT message again
(SXXXIV.2's partition rule). The request leg was never real FORTH
messaging traffic to begin with (a direct VM-EXEC, no type tag,
forced by Hera's own inability to load common:messaging.4th), so it
is untouched.
New KH-BLK-ATTACH-SEND (repl.c) wraps sk_hermes_send_one(), reached
from capsules/artemis/init.4th's HERA-BLK-ATTACH-REQ. Delivery reuses
task 3.4's already-wired sk_hermes_drain_checkpoint(); BLK-ATTACH-ACK
itself is unchanged, just reached by a different layer.
SK_HERMES_MSG_TYPE_BLK_ATTACH deliberately reuses BLK-ATTACH-EVENT's
own value (9) to document this as a cutover of the same message, not
a new one.
Two real bugs found on the way, both recorded in FABRIC-3.6.md's
findings log:
- A popped FORTH CREATE-buffer address was raw-cast to a host pointer
instead of going through vm_ptr() -- silently read all-zero memory,
no crash, no error, just a message that arrived and did nothing.
Fixed; the rule and its exception (repl.c's own dev-addr is
legitimately a raw pointer, formatted that way by its own pushing
code) are written up for the next FORTH-facing C word.
- A separate, genuine hang on the very first live exercise of this
path, never reproduced across ten subsequent boots. Reported, not
chased -- not blocking, per the task's own check being otherwise
fully satisfied.
Also found live: log_message() is invisible in this build's actual
serial-log capture at every level -- settled on a single
console_println in the real drain target instead, one line per real
USB attach, not a hot-path.
Final acceptance (logs/20260922-105501, -105758, -110304, disk images
reset before each): dict_hash identical across all three
architectures for every VM, zero UNKNOWN WORD, mkcapsule --lint
clean, real ledger+stadium_conserved(Artemis)=true evidence on every
boot.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Added HERMES-CHANNEL-OPEN? ( req-hi req-lo -- allow? ) at
capsules/ACL.4th block 4008 (default: approve everything) -- the one
word policy authors edit. sk_hermes_channel_open_policy(VM*, VMUuid)
(kernel_hermes.h/.c) is the C-side query that calls it via plain
word-dispatch against the target VM's own dictionary/stack, never
vm_interpret() (avoids task 3.4's input-buffer cursor hazard entirely)
and never decides the answer itself. Fails closed: no policy word,
a policy error, or stack underflow all deny, matching CLAUDE.md's
posture that absence of policy must never mean "always allow."
Two real bugs found and fixed before this was called done:
missing current_executing_entry assignment before calling the word's
func pointer (colon words silently no-op without it, vm_core.c:730 --
no crash, just a wrong answer); and a second FAIL with debug
instrumentation still in place whose precise cause isn't
reconstructable, since no intermediate commit exists for that attempt.
Self-test proves the task's check four ways against the same
unchanged C function: default approve, live redefinition to deny
(zero C change), restore, and a VM with no ACL.4th loaded at all
(fail closed). A fifth check wires the result into task 3.6's
sk_hermes_channel_respond() end to end: a denied policy produces a
NACK and no channel, ledger/stadium_conserved() holding throughout.
Scope, per Captain Bob's ruling: closes with the query built and
proven; sk_hermes_channel_respond() still takes a caller-supplied
approved bool rather than calling the policy internally. Wiring a
real channel-open call site to only this query is deferred to
whichever later task first needs a live decision.
dict_hash identical across amd64/aarch64/riscv64 for every VM, zero
UNKNOWN WORD, mkcapsule --lint clean (38 files, 0 violations).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extracted sk_hermes_send_one() from sk_hermes_publish()'s own
per-subscriber body -- one code path for both point-to-point and
fan-out delivery, so the ledger can never diverge between them.
Point-to-point addressing turned out to be load-bearing, not
incidental: sk_hermes_publish()'s fan-out sets msg->to to whichever
member it is iterating, so a negotiation message "published" to the
common channel would spuriously reach every member, not just the real
target (checked with advisor() before building the naive version).
"Over the common channel" means every VM is reachable from birth (task
3.2), not that the exchange itself fans out -- messaging.4th's own
CH-REQUEST carried an explicit `to` for the same reason.
sk_hermes_channel_request/respond/close build the mechanics: request ->
grant (creates a private channel, subscribes both parties, sends
CH_GRANT + one ACK) or NACK ("a deny is a NACK", SXLV.1 -- no separate
type); close authorized by membership alone. The grant/deny decision is
a plain caller-supplied `approved` bool -- task 3.7 replaces the call
site that produces it with a real ACL.4th query, not this signature.
Self-test covers the task's own three checks plus a sibling advisor()
flagged: an approved respond() whose channel creation itself fails
(table exhausted) must still fall through to NACK, not a silent false
grant or half-open channel -- verified by exhausting the whole channel
table and confirming the fallback.
Bug found and fixed before this was called done: the first draft
dropped a message via pending_pop() alone, without releasing it first,
leaking its Stadium heat and failing the self-test's own ledger
baseline check (logs/20260922-065946/amd64/, kept as audit trail).
Fixed and re-verified PASS on all three architectures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
sk_hermes_publish() now enforces SK_HERMES_CHUNK_MAX_PAYLOAD (1024) on
every message's payload_len uniformly, chunked or not -- closing the
gap task 3.3 explicitly parked. A chunk carrier is
[SkHermesChunkHeader][content slice], slice capped at
1024 - sizeof(header) rather than 1024 itself, so every message on the
wire satisfies the same one-block bound vm_interpret()'s own drain
limit already requires -- a future chunk-aware drain never has to
special-case a carrier that can't be handed to vm_interpret() as-is.
Deliberately no chunking-sender API: building one would need
kernel-Hermes to own chunk-buffer memory with a real lifetime it has no
way to track (kept alive until every subscriber drains it). Sending is
a loop pattern a caller writes with sk_hermes_chunk_count() +
sk_hermes_publish(), demonstrated by this task's own self-test.
sk_hermes_reassemble() is pure and memory-agnostic: validates msg_id
agreement, exact seq coverage, and per-chunk slice sizes before a
single memcpy, with the total length computed once and checked against
the caller's buffer once -- never order-dependent on which chunk
happens to overflow.
Verified live on all three architectures: a 1024-byte payload as one
message, a 1025-byte send refused outright with the ledger untouched,
and a 3000-byte payload split into 3 chunks, drained, and reassembled
byte-exact against the original. dict_hash unmoved and identical
across architectures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
sk_hermes_drain_checkpoint() interprets one queued payload per checkpoint
(ruled: one message per checkpoint), reusing sk_vm_at_outermost_interpret()
and placed before the switch-signal block in vm_core.c's existing
cooperative checkpoint (sk_vm_context_switch() doesn't return until
switched back to, so drain must come first or it silently never runs on
a switching checkpoint).
Amends FABRIC-3.5.md SXLIII.5, caught by advisor() before writing the
naive version: "recursive drain is prevented for free" via
g_vm_interpret_depth is true but only for same-message re-drain -- it
doesn't cover the separate same-VM reentrancy hazard FABRIC-3.md SXX
already named for Hera specifically (VMCallState saves rsp/exit_colon/
ecw_nesting only, never input_buffer/input_length/input_pos). Draining
calls vm_interpret() on the same vm whose own vm_interpret() call is
still paused mid-word at the checkpoint; without saving and restoring
the cursor by hand, the enclosing REPL line or LOAD block would be
silently truncated. sk_hermes_drain_checkpoint() snapshots and restores
input_buffer/input_length/input_pos/mode/error/abort_requested around
the call. Not a divergence from the ruling -- cursor preservation is the
implementer's own obligation inside the ruled mechanism.
Gated behind a system-wide pending-total counter so the common
no-message-in-flight case costs one integer read per word dispatch, not
a stadium_max_vm_count()-sized queue scan (also flagged by advisor() as
a real hot-path cost, not deferred).
Verified live on all three architectures: a self-test publishes a real
payload to Hermes, proves the depth gate via VM-EXEC-ing an existing
harmless colon word into Hermes (genuine nested vm_interpret(), depth 2,
must not drain), then drains directly from genuinely-outermost context
and confirms exactly one clean drain. dict_hash unmoved and identical
across architectures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
sk_hermes_publish() allocates one SkHermesMessage per channel member
(heat-cost ruling 2026-09-21: one message per subscriber, funded by
the publisher's own reservoir) and enqueues each onto a new
per-subscriber SkHermesPendingQueue -- found-or-created lazily by
vm_id, sized from stadium_max_vm_count() like the channel/switch
tables. Best-effort across subscribers: a failed allocation or full
queue skips and rolls back just that one subscriber, not the whole
publish -- the natural reading of "ledger and stadium_conserved() hold
across N publishes to M subscribers" (the task's own check), not a
separate ruling.
Dispatches nothing -- sk_hermes_pending_count()/peek()/pop() are the
read/drain primitives task 3.4's real checkpoint-driven drain will
build on; this task's own self-test uses them directly since no
checkpoint hook exists yet.
Verified live on all three architectures: pending-queue table sized
50/202/50 slots (tracking the channel table's own per-arch sizing), a
synthetic publish self-test (2 publishes to 3 subscribers) confirms
exact per-subscriber delivery counts, and ledger/stadium_conserved()
invariants hold both mid-publish and after manually draining every
queue back to baseline.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds SkHermesChannel: a channel is an index into a boot-time,
stadium_max_vm_count()-sized table (same sizing pattern task 3.1
established for the switch table -- no separate numeric rule was ruled
for this table, so task 3.1's bound is extended directly, flagged as
such rather than restated as a new ruling). No name field, mirroring
messaging.4th's own nameless CH-ARENA.
The common channel (index 0) is created at boot and permanent. Hera
subscribes explicitly in kernel_main.c (she is the one VM never born
through capsule_birth_baby()); every other VM -- Tripod fleet and
future WIREBIND identities alike -- subscribes inside
capsule_birth_baby() itself, the single choke point every other birth
already passes through.
Inert: no publish, no dispatch, no ACK/NACK, no ACL hook (tasks 3.3,
3.6, 3.7). Verified live on all three architectures: channel table
sized to 50/202/50 slots (matching switch-signal's own per-arch
sizing), common-channel fleet self-test confirms all four Tripod
members are members, and a synthetic create/subscribe/unsubscribe/
destroy round-trip against a private topic passes, including refusing
to destroy the common channel.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces the fixed SK_SWITCH_MAX_SLOTS=16 compile-time array with a
boot-time, RAM-derived allocation via a new sk_vm_switch_signal_boot_init(),
kmalloc'd to stadium_max_vm_count() entries -- the same pattern
session_boot_init() already established for Stadium-derived sizing.
Every switch-signal participant is a Stadium VM, so this reuses that
bound directly rather than deriving a separate one.
Verified live on all three architectures: switch table sized to 50
slots (amd64), 202 slots (aarch64), 50 slots (riscv64) -- all well
past the old fixed cap. All three boot to [zuse@Hera] ok> cleanly;
dict_hash for Hermes/Hestia identical across architectures, unmoved
from pre-task values.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Captain Bob ruled all five §XLV.4 sub-items plus task 3.3's heat-cost
design point: ACK on channel-open+delivery only; the channel-open ACL
hook is a new word in ACL.4th; switch-table sizing mirrors Stadium's
stadium_max_vm_count_val; chunks carry (msg_id, seq, is_last); drain
one message per outermost-interpret checkpoint; publish costs one
message per subscriber. Tasks 3.1-3.7 may now be written precisely.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FABRIC-3.5.md SXL.4's ledger: held == pulled - returned - consumed,
epsilon zero. Added sk_hermes_ledger() (an accessor, not a mutator)
plus four static counters in kernel_hermes.c.
Each counter has exactly one increment/decrement site: held/pulled
both move at sk_hermes_alloc()'s single success path, after every
refusal branch has already returned; held/returned both move at
sk_hermes_release()'s single success path. consumed is declared and
always reads 0 -- its one increment site doesn't exist yet, and won't
until task 2.5 gives decay something to record.
sk_hermes_release() now reads the Stadium cell's live header.heat
immediately before calling stadium_evict(), rather than assuming the
original pulled amount -- stadium_evict() zeroes the header as part of
freeing the cell and its own return value is a success code, not the
credited amount, so this is the only point the true remaining heat is
available. Today this always equals the original Q.SLOT pull; once
task 2.5's decay exists, this is what keeps returned correct without
touching this function again.
Self-test (kernel_main.c) extended: snapshots the ledger before
running so it checks its own deltas, verifies held/pulled grow by
exactly got_n * Q_SLOT on allocation with returned/consumed untouched,
then verifies held returns to its starting value and returned grows by
the same amount on release, and checks the audit invariant itself as a
bonus (task 2.6 formalizes this properly).
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD, dict_hash unmoved. All three print PASS with
identical final ledger: held=0 pulled=65536 returned=65536 consumed=0.
No compiler warnings.
Authorized by Captain Bob ("keep going with rhe 6.5 document").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Real finding, caught before building release on a foundation that
couldn't support it: task 2.2's first cut of sk_hermes_alloc() pulled
reservoir heat but never admitted a real Stadium-floor patron -- just a
local in_use flag. FABRIC-3.5.md SXXXIII.4 item 1 says MSG-FREE-NODE
returns heat "via STADIUM-EVICT", which only means something if
allocation admitted something. SXL.4's own invariant, Sigma(resident
patron heat) + reservoir + consumed == Q48_ONE, cannot balance if held
heat is invisible to every term while held. Flagged to Captain Bob
before proceeding; authorized to correct 2.2 in the same pass as
building 2.3 on top of the fix.
sk_hermes_alloc() now calls stadium_admit() with heat = the pulled
amount, behaviour = STADIUM_BEHAVIOUR_DELIVER (matching messaging.4th's
own SB-DELIVER STADIUM-ADMIT exactly), and identity = the message's own
slot index (matching the FORTH precedent -- caught live in the first
boot of this fix that omitting this made stadium_dispatch()'s existing
DELIVER diagnostic print msg_idx=0 for every message instead of a
distinct value). The returned cell index is stored in the message's
own stadium_cell field. Stadium-floor refusal (independent of reservoir
affordability) rolls back the pull the same way the other refusal
paths already do.
sk_hermes_release() -- the function task 2.3 actually asks for -- calls
stadium_evict() on that cell, which itself returns the departing
patron's remaining heat to its owning VM's reservoir, matching
MSG-FREE-NODE's exact shape. Release does not touch the reservoir
directly.
Self-test (kernel_main.c) extended: keeps every allocated message's
pointer, allocates to exhaustion as before, releases all of them, and
checks the reservoir returns to precisely its starting value.
"Undecayed" is true by construction (no decay/TTL logic exists yet,
task 2.5) -- exact restoration, not approximate.
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD, dict_hash unmoved from task 2.2. All three print
identical PASS arithmetic: reservoir0=65536, reservoir_after_alloc=0,
reservoir_final=65536. No compiler warnings.
stadium_dispatch()'s DELIVER-case console output (one line per
eviction) is pre-existing instrumentation, not new -- confirmed real
and load-bearing per stadium.c's own comment, verbose but expected.
Authorized by Captain Bob ("yes").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The piece FABRIC-3.5.md SXXXIII.6 calls "what remains genuinely hard,"
built and proven first per its own recommendation. Added
src/starkernel/vm/kernel_hermes.c (wired into Makefile.starkernel's
LOADER_EXTRA_SRCS -- this repo lists vm/*.c files explicitly, no glob)
and sk_hermes_alloc()'s declaration in kernel_hermes.h.
Checks stadium_reservoir_peek(vm_id) >= SK_HERMES_Q_SLOT before
touching the reservoir at all -- refusal this way needs no rollback,
since nothing was pulled -- with an explicit rollback path
(stadium_reservoir_push) kept defensively for the pull-then-short case,
though nothing in this single-core kernel is expected to reach it.
SK_HERMES_Q_SLOT = Q48_ONE / SK_HERMES_MSG_MAX (2048), deliberately
simpler than messaging.4th's own formula, which reserves a Q.1/3 floor
for COMMON-CH's own Stadium heat -- kernel-Hermes has no such object
(SXXXIII.4/SXXXIII.5's flat membership list carries no heat of its
own), so there is nothing left for that floor to protect.
Self-test in kernel_main.c, same diagnostic-only synthetic-VM pattern
as the existing Stadium quota grant self-test (lo=3, distinct from
that test's lo=1): reads back the actual granted reservoir rather than
assuming a number, derives expected_n from it, allocates to refusal,
and checks the refusal lands at exactly expected_n, the reservoir
doesn't move on the refused attempt (rollback proven, not assumed),
and the final reservoir is exactly reservoir0 minus got_n times
Q_SLOT.
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD, dict_hash unmoved from task 2.1 (pure C, no FORTH
touched). All three print identical self-test arithmetic: reservoir0=
65536 Q_SLOT=2048 expected_n=32 got_n=32 reservoir_after=0. No compiler
warnings.
Noted, not fixed: Q_SLOT's divisor and SK_HERMES_MSG_MAX are the same
32, so reservoir and arena exhaustion land at exactly the same count by
construction -- this test can't distinguish which refusal reason
fired, only that refusal is correct and rolls back correctly.
Deliberately not evidence for stadium_conserved(): allocating alone
(no release yet, task 2.3) leaves pulled heat held off the Stadium
floor, so the two-term check would correctly read false right now if
run mid-hold. That's expected, not a bug -- Stage B (task 2.7) is
defined as "before and after the alloc/free cycle," not "continuously
during." This task's self-test checks reservoir arithmetic directly
instead.
Authorized by Captain Bob ("Yes continue").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Phase 2, task 2.1 only: type definitions, wired to nothing, drawing no
heat -- no allocator, no protocol logic, no registration anywhere.
FABRIC-3.5.md SXXII.4: Phase 2 structures come first and prove nothing
until the allocator is built on top (task 2.2 onward, each its own
commit).
Added include/starkernel/vm/kernel_hermes.h:
SkHermesMessage -- field-for-field mirror of messaging.4th's live
9-cell MSG-* layout (type/from/to/payload addr+len/Stadium cell
index/seq/channel/orig-type), per SXXXIII.4 item 1 ("roughly half the
file is accessors that become struct fields"), plus an explicit
in_use flag for task 2.2's allocator. Deliberately no separate heat
field: per SXL.4, a message's heat IS the Stadium cell it occupies,
not a value copied alongside it -- one source of truth for the
conservation invariant stadium_conserved() (task 0.7) checks.
SkHermesMembership -- one flat broadcast membership list, SXXXIII.4/
SXXXIII.5's recommended replacement for messaging.4th's 28-word channel
abstraction (traced to exactly one live caller, CH-ADD-MBR). Item 27
(negotiation vs. broadcast, Phase 3 blocker B1) is not answered by this
structure and isn't meant to be -- a flat list is correct either way.
Genuinely wired to nothing: no .c file, no Makefile change, no include
from any compiled source. Syntax-checked standalone (gcc -std=c99
-Wall -Wextra -Werror -fsyntax-only) before touching the real build.
Boot byte-identical to task 1.9's baseline on amd64 (same dict_hash
triple, zero UNKNOWN WORD). Did not repeat aarch64/riscv64 -- the file
compiles into no object on any architecture, so there is no mechanism
by which it could diverge.
Authorized by Captain Bob ("yes").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Audited first: neither capsules/hestia/init.4th nor her birth block in
kernel_main.c references g_wirebind_attached_username, CONSOLE-ATTACH,
MINT, or any proxy-minting mechanism -- the invariant already held
structurally, by absence. Stated it explicitly anyway, per the task:
added a comment at Hestia's birth site quoting FABRIC-3.5.md SXVIII.6's
invariant verbatim, warning future edits not to add console/wirebind/
proxy code there without re-reading it first.
Verified live with the actual no-thumbdrive boot
(ARCH=<arch> qemu ZUSEDISK=), not the default. All four VMs born
successfully on all three architectures, zero UNKNOWN WORD, and zero
ok> occurrences anywhere in any of the three full logs -- genuinely
silent, matching sk_repl_headless_wait()'s own documented "no banner,
no prompt, no input surface at all." Watched each log's line count
post-birth for 8-10s to confirm it stayed flat rather than eventually
printing something late.
Confirmed no regression on the standard (with-thumbdrive) path on
amd64: dict_hash identical to task 1.8's baseline. Did not repeat that
check on aarch64/riscv64 -- the change is a comment only, cannot
diverge by compiler, and the headless invariant itself was already
proven identically on all three.
Phase 1 is now fully closed (tasks 1.1-1.9). Tripod is Hera/Artemis/
Hestia plus Hermes (retained through Phase 1-3 per SXXXIV.4); Hestia
owns the drawing fabric exclusively; headless-until-login intact with
Hestia in the fleet. Phase 2 is next.
Authorized by Captain Bob ("yes").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Real fix for task 0.8's finding. register_framebuffer_words() was
called unconditionally from register_forth79_words() (word_registry.c),
itself called unconditionally from vm_init_with_host() -- the generic
per-VM bootstrap every VM goes through, with no way to know a VM's
name at that point.
Removed the unconditional call. Added a name-gated call instead in
capsule_birth_baby() (capsule_birth.c), beside the existing
is_fleet_foundation check -- the one place in the birth path where
capsule_name and the newly-allocated VM* are both in scope together:
Hestia gets register_framebuffer_words(), nobody else does.
Positively verified live, exactly as the task's own check demands:
FB-WIDTH via VM-EXEC returns UNKNOWN WORD in Hermes and Artemis, 1280
in Hestia. Confirmed at the fundamental level too: Hera's own base
PARITY:M7.1a word count dropped from 531 to 528 -- exactly the three
words removed -- identical across all three architectures.
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD during boot. mkcapsule --lint capsules/ clean, 38
files / 0 violations (pure C change, no capsule content touched). No
compiler warnings.
Side effect on the vendored hosted build, expected and not a
regression: capsule_birth.c is kernel-only, so the hosted starforth
binary has no Hestia concept and now never registers these words at
all -- confirmed live. framebuffer_words.c's own top comment already
calls this surface "kernel-only, no-op on hosted builds," so the prior
stub registration was already vestigial. Ran a plain `make` sanity
build per CLAUDE.md's own stated purpose for that target; regenerated
lfs/amd64/starforth included here rather than left stale.
Authorized by Captain Bob ("yes").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Tasks 1.6 and 1.7 are not independent, and the punchlist's split was
wrong: font.4th calls G-LINE/G-ELLIPSE, which are fabric.4th's own
words. Confirmed live before committing to an approach -- removed only
fabric.4th's EXEC from init.4th, left font.4th's in place, booted
amd64: Hera's boot floods with UNKNOWN WORD: 'G-LINE'/'G-ELLIPSE' the
moment font.4th loads (logs/20260919-171047/amd64/, kept as evidence).
Reverted that partial state, asked Captain Bob how to proceed given
neither task can independently pass its own three-arch-boot check, and
was told to use best practices.
Moved both together, in their original relative order, into a new
capsules/hestia/init.4th block 4988 -- a deliberate, documented
deviation from "one task, one commit," not a bundling of convenience.
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD. Hestia's dict_hash identical across all three
architectures. Verified the shrink/grow live, not just inferred from
hash movement: HERE reads 20008 in Hera, 63040 in Hestia post-move.
CART-PLOT in Hera is UNKNOWN WORD; the identical call routed into
Hestia via VM-EXEC reaches the word and fails on a stack underflow
instead, proof it exists there since an unknown word can't underflow.
mkcapsule --lint capsules/ clean, 38 files / 0 violations. MANIFEST.md
updated: init.4th's block 2049 entry no longer lists fabric.4th/
font.4th; hestia/init.4th's entry gains block 4988.
Authorized by Captain Bob ("Use best practices.").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Added a fourth capsule_vm_find_by_name_nocase("Hestia", ...) +
sk_vm_switch_signal_register(...) block in src/starkernel/kernel_main.c,
same shape as the existing Hermes/Artemis blocks, placed after all four
fleet members are confirmed born -- the existing comment on this block
already states why: no critical-section protection during setup, so
registering earlier risks the signal firing mid-birth.
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD, hashes identical to task 1.4's baseline (this is
pure C runtime state, doesn't touch any FORTH dictionary). No compiler
warnings.
Took FABRIC-3.5.md SXXXV.0's "invisible by default" warning literally
rather than trusting a clean boot log alone: SXXVIII.2's own recorded
switch-storm signature is "QEMU pinned near 100% CPU, serial log frozen
solid," not an error message. Confirmed normal wall-clock boot time on
all three (~30s) and, since TCG itself always shows ~100% CPU
regardless of guest workload, watched each serial log's line count at
the idle prompt for 5-10s and confirmed it stopped growing rather than
flooding or silently stalling.
Authorized by Captain Bob ("yes").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Added a birth block immediately after Hermes's own, same shape:
S" Hestia" BIRTH followed by a registry-lookup confirmation. Fleet is
now Hera/Hermes/Artemis/Hestia, four VMs, through Phase 1-3
(FABRIC-3.5.md SXXXIV.4) until Phase 4 retires Hermes.
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD. Registry shows all four (BIRTH: Hermes live, BIRTH:
Hestia live, PARITY:BIRTH for all three non-Hera VMs). Hestia's
dict_hash identical across all three architectures (0x31cab513929eea89).
Hera/Hermes hashes unchanged from task 1.3; Artemis's vm_id shifted
(now the 4th birth instead of 3rd -- sequence-derived, not identity-
derived, so expected) but its dict_hash is unchanged and still
identical across arches. No compiler warnings.
Noted, not a regression: Hestia's birth log shows the same
"( Unterminated comment" HADES warning Artemis's birth has shown since
task 0.0's first baseline.
Authorized by Captain Bob ("yes").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fourth vm_name_prefix_eq_nocase(capsule_name, "Hestia") check alongside
Hera/Hermes/Artemis in src/starkernel/capsule/capsule_birth.c's
is_fleet_foundation local -- Hermes retained, per FABRIC-3.5.md
SXXXIV.4 (he stays live and fleet-foundation through Phase 1-3). The
flag's only consequence is session_set_pinned(vm_id, 1) for whichever
VM name matches.
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD, byte-identical to the pre-change baseline -- expected,
since nothing births anything named "Hestia" yet (task 1.4), so the
added name never matches. Hermes confirmed still present in the check
and still born normally in all three logs.
Authorized by Captain Bob ("yes").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The third Tripod leg's first real file (FABRIC-3.5.md SII/SIV). Blocks
4986-4987 of the 4986-4996 allocated in task 1.1 (b624133a): WELCOME
banner, then common:messaging.4th load + MSG-CD-INIT + COMMON-CH join
on slot 11, modeled on hermes/init.4th's equivalent shape. No fabric.4th/
font.4th yet -- tasks 1.6/1.7. Not yet birthed -- task 1.4.
Slot 11 is a fresh routing-table slot, not Hermes's slot 1: Hera/
Hermes/Artemis hold 0/1/2 and identities hold 3-10 (messaging.4th:
78-85), and Hermes stays live and fleet-foundation through Phase 1-3
(SXXXIV.4) so his slot isn't free yet.
Deliberately did not add a VM-NAME-REG entry for "Hestia" to
messaging.4th's VM-NAMES-INIT -- Phase 1's own gate is "messaging
untouched" and that table lives in messaging.4th. Consequence: once
birthed, Hestia is COMMON-CH-reachable by raw slot but not yet
VM-EXEC-addressable by name. Flagged in FABRIC-3.6.md rather than
decided -- whichever task first needs name lookup settles where that
one-line addition goes, and it will need its own authorization since
it touches a file Phase 1 promised not to.
mkcapsule --lint capsules/ clean, 38 files / 0 violations. MANIFEST.md
given a matching entry. Three-arch boot clean: amd64/aarch64/riscv64
all reach [zuse@Hera] ok>, zero UNKNOWN WORD, byte-identical to task
0.7's baseline (same dict_hash triple) -- exactly as expected, since an
unbirthed capsule is inert.
Authorized by Captain Bob ("YES").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Documentation only, no capsule file created yet (that's task 1.2).
Checked against actual current occupancy rather than trusting
MANIFEST.md's own stale blanket "4853+ OPEN" line: fabric.4th already
occupies 4900-4924 and font.4th 4925-4985 (both standalone capsule
files EXEC'd by init.4th, block-numbered independently of it -- tasks
1.6/1.7 relocate which capsule EXECs them, not their own ranges), and
4997 is the console proxy's hardcoded Block 4997 string literal
(capsule_console.c:27-29). Allocated 4986-4996, the gap between the
two, avoiding 4997 as the task requires.
Split the Unassigned Ranges table's single blanket line into an
explicit claim for 4986-4996 plus a corrected "OPEN" line that excludes
the ranges actually in use. Recorded in MANIFEST.md rather than
tools/capsule-reserved.txt -- that file is for blocks owned by
non-capsule infrastructure per its own header comment; a real capsule
allocation belongs in MANIFEST.md alongside every other infrastructure
capsule's entry.
mkcapsule --lint capsules/ clean, 37 files / 0 violations, unchanged.
Authorized by Captain Bob ("YES").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Read-only audit, no code changed. Finding: reachable from every VM
today, not confined to one table, contrary to item 33's premise.
FORTH level matches expectation: fabric.4th/font.4th are EXEC'd only
from capsules/init.4th (Hera). C level does not: register_framebuffer_
words() (src/word_source/framebuffer_words.c:60-65) is called
unconditionally from register_forth79_words() (src/word_registry.c:
139), itself called unconditionally from vm_init()
(src/starkernel/vm/vm_bootstrap.c:263) -- the generic per-VM bootstrap
every VM goes through, no identity check.
Verified live rather than trusting the source trace alone: booted
amd64 and ran `S" FB-WIDTH ." S" Hermes" VM-EXEC` and the same against
Artemis -- both returned 1280, not UNKNOWN WORD. Neither loads
fabric.4th, so the raw C primitive itself is answering.
Not fixed here, per the task's own read-only scope. Gives task 1.8 a
concrete starting state: its own check ("a non-Hestia VM calling PLOT
gets UNKNOWN WORD") currently fails, and register_framebuffer_words()'s
call site will need to become conditional or move out of the universal
bootstrap -- not just the FORTH-level relocation tasks 1.6/1.7 already
plan for.
Phase 0 gate met across tasks 0.2-0.7 (three-arch boot, stadium_
conserved() true, zero UNKNOWN WORD, repeatedly). Phase 0 is closed;
Phase 1 (Hestia, messaging untouched) is next.
Authorized by Captain Bob ("Continue.").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Boolean analogue of vm_physics_conserved(), for the Stadium per-VM
quota invariant rather than fleet-wide execution heat
(FABRIC-3.5.md SXXXIX.4). int stadium_conserved(VMUuid vm_id), in
src/starkernel/vm/stadium.c alongside stadium_resident_sum()/
stadium_reservoir_peek() that it's built from, declared in
include/starkernel/vm/stadium.h.
Implements the two-term form -- resident_sum(vm_id) +
reservoir_peek(vm_id) == Q48_ONE -- not the three-term form SXL.4
rules for the eventual system. That ruling's `consumed` term is a
Phase 2 kernel-Hermes ledger deliverable that doesn't exist yet:
nothing draws on any VM's Stadium quota today (task 2.2 is literally
where that wiring gets built), so consumed is honestly zero right now.
Folding it in as a placeholder would be inventing Phase 2 state ahead
of it existing -- the doc comment says so explicitly, so whoever
builds Phase 2's ledger extends this function rather than working
around it.
Wired into the existing per-VM boot diagnostic
(stadium_words_print_boot_diagnostics(), kernel_main.c:810, Hera
only -- the sole existing call site) rather than adding a new one,
printing CONSERVED/DRIFTED the same shape vm_physics_status() already
uses.
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD, all print "Stadium conservation: CONSERVED" with
identical resident_sum=47641 reservoir=17895 sum=65536=Q48_ONE. No
compiler warnings on either edited file (forced recompile checked).
Authorized by Captain Bob ("Yes.").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Added 4055-4059 (former common/msg.4th) and 4300-4399 (former
process.4th) as reserved, each noted as freed by this reshuffle's
strip rather than owned by non-capsule infrastructure -- the file's
usual purpose (Artemis's own block usage, etc.). Framed explicitly as
lifted, not permanent, once someone deliberately wants a range back,
per FABRIC-3.5.md SSXVIII.4/XXII.4: freed ranges should be returned
here rather than silently available for a future capsule to reclaim
without anyone noticing.
mkcapsule --lint capsules/ clean, 37 files / 0 violations.
check_reserved_conflicts() -- the hard build-gate that actually reads
this file (tools/mkcapsule.c:974) -- passes clean on a real
`make -f Makefile.starkernel ARCH=amd64 all`, confirming the new
entries don't collide with anything currently baked. Registry/
documentation only, no capsule content touched, so no 3-arch boot run
for this task.
All of Phase 0's strips and documentation corrections (0.1-0.6) are
now done. stadium_conserved() (0.7) and the PLOT/FB-WIDTH/FB-HEIGHT
registration audit (0.8) remain.
Authorized by Captain Bob ("Go for it.").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Block 2049's justification claimed init.4th loads compudynamics,
common:msg, fleet-k and process. Read the live file: it loads none of
these. compudynamics.4th/fleet-k.4th were already deleted (9323f776,
2026-07-05); common:msg.4th/process.4th are this reshuffle's own
Category A strips (tasks 0.2/0.3, a0e97258/aafcce43) and were never
EXEC'd from this block even before that -- the manifest entry was
already wrong prior to this pass, just not yet caught.
Removed the standalone common/msg.4th (former block 4055) and
process.4th (former blocks 4300-4301) sections, since both files no
longer exist, and folded them into "Deleted capsules (historical)"
alongside the existing compudynamics.4th/fleet-k.4th entry -- same
convention, same section. Noted that common/msg.4th's own entry had
claimed it was an immutable ABI "every messaging VM loads at birth",
a claim FABRIC-2.md:2773 had already flagged stale before this
correction landed. Updated the Unassigned Ranges table so 4055-4059
and 4300-4399 read as former-file ranges rather than "extension space"
for files that no longer exist.
Documentation only -- no capsule content touched, mkcapsule --lint
capsules/ still clean (37 files, 0 violations). Verified every
remaining ### `*.4th` section in the manifest names a file actually
present on disk.
Authorized by Captain Bob ("Keep going.").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Dead per task 0.1's reachability check (156d1642): zero references by
name anywhere in the tree outside its own definition -- process.4th's
own SPAWN/PAUSE/RESUME/KILL-VM calls passed bare numeric literals
(1/2/3/4), never this constant's name. Removed the single line only;
PAUSE-EVENT/RESUME-EVENT/KILL-EVENT are left in place, matching the
punchlist's stated scope -- they are equally dead-by-name but that
widening was flagged in 0.1's findings, not folded into this task.
mkcapsule --lint capsules/ clean, 37 files / 0 violations, unchanged
(one-line edit, no file added or removed). Three-arch boot clean:
amd64/aarch64/riscv64 all reach [zuse@Hera] ok>, zero UNKNOWN WORD.
dict_hash moved for Hermes/Artemis (both load messaging.4th) and is
identical across all three architectures; Hera's PARITY:M7.1a snapshot
unchanged as in the prior two strips.
Phase 0's three strips (0.2-0.4) are now all done. capsule-reserved.txt
and MANIFEST.md's stale block-4055/2049 entries remain for 0.5/0.6.
Authorized by Captain Bob ("Yes, please continue.").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
process.4th is dead per task 0.1's reachability check (156d1642): zero
EXEC sites anywhere, and its four words (SPAWN/PAUSE/RESUME/KILL-VM)
have zero callers outside the file itself. Deleted outright.
EVENT-EMIT/EVENT-WAIT/EVENT-DRAIN (messaging.4th Block 5030) had
exactly one live caller -- process.4th, per FABRIC-3.5.md SXXXIII.3 --
so they go with it. Block 5030 was self-contained (nothing else in
it), so the whole block was removed rather than edited down.
mkcapsule --lint capsules/ clean, 37 files / 0 violations (was 38).
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD. dict_hash moved for Hermes and Artemis (both load
messaging.4th) but is identical across all three architectures, which
is the invariant that matters, not an unchanging absolute value
(SXXII.4). Hera's own PARITY:M7.1a snapshot is unchanged since it fires
before any capsule loads.
capsule-reserved.txt and MANIFEST.md's stale block-4055/2049 entries
still untouched -- tasks 0.5 and 0.6, now unblocked since both strips
are done.
Authorized by Captain Bob ("Yes, you may continue.").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Dead per task 0.1's reachability check (156d1642): zero EXEC sites in
any boot-loaded capsule, and its only two words (HERMES-ACK/
HERMES-NACK) have zero callers anywhere -- messaging.4th's own block
5033 comment already says outright that this file's ACK/NACK
indirection is retired.
mkcapsule --lint capsules/ clean, 38 files / 0 violations (was 39).
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD, dict_hash unchanged from the task 0.0 baseline on
all three -- expected, since the file was never loaded into any VM's
dictionary in the first place.
capsule-reserved.txt and MANIFEST.md's stale block-4055 entry are left
untouched here, per the punchlist's own ordering -- 0.6 returns the
freed block range and 0.5 corrects the manifest once 0.3's strip is
also done.
Authorized by Captain Bob ("Go ahead.").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Checked the three §XXII.2 routes for capsules/common/msg.4th,
capsules/process.4th, and the SPAWN-EVENT constant, never by grep
count alone: a boot-path trace of every capsule init.4th loads at
birth, a tools/experiments/docs invocation search, and confirmation
that mere presence in the baked capsule directory doesn't make a name
reachable if nothing constructs it at runtime. All three are dead by
every route -- zero EXEC sites, zero callers of their exported words.
Two findings recorded, neither changing the punchlist's plan:
tools/hermes_smoke.sh calls EVENT-EMIT/EVENT-WAIT/EVENT-DRAIN directly,
a caller FABRIC-3.5.md SXXXIII.3 missed when it said the only other
reference was MANIFEST.md. Doesn't make them live -- the script is
already broken on its own terms (references capsules/core/init.4th and
build/amd64/standard/starforth, neither exists; calls the pre-rename
CD-INIT word). Task 0.3's plan to strip these three words alongside
process.4th stands.
PAUSE-EVENT/RESUME-EVENT/KILL-EVENT are exactly as dead-by-name as
SPAWN-EVENT -- process.4th's own calls pass bare numeric literals, never
the constant names. Task 0.4 only names SPAWN-EVENT; flagged for
whoever picks up Category A stripping next rather than expanding this
task's scope.
Investigation only, no code changed.
Authorized by Captain Bob ("begin.").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
amd64/aarch64/riscv64 all boot clean to [zuse@Hera] ok>, zero UNKNOWN
WORD, and dict_hash identical across all three: Hera (PARITY:M7.1a)
0x6824fe5993239838, Hermes 0x062252c4da6858da, Artemis
0xed80117724c26f36. This is the gate task 0.0 exists for -- every later
task's acceptance in this document assumes this baseline is known-good.
Found and worked around a real confound along the way: disk/artemis.img
is deliberately shared across all three ISAs' qemu targets (FABRIC-3.md
SXXXV.2), so a same-order rerun has run 2 and 3 silently resume run 1's
already-formatted disk instead of formatting their own. The first
attempt (logs/20260919-124835 amd64, logs/20260919-124952 aarch64,
both kept for the record) shows exactly this: Artemis's dict_hash
diverges between the two runs even though Hera's and Hermes's do not,
because only Artemis's birth path branches on disk state. Restored
disk/artemis.img and disk/thumbdrives/zuse-thumb-ident.img to their
committed blank state before each of the three reruns that produced
the clean, matching baseline above, and recorded the finding in
FABRIC-3.6.md so a later task doesn't mistake the same confound for a
real architecture divergence.
capsules/BLOCK_MAP.md's timestamp header is regenerated by the build,
per .claude/CLAUDE.md's documented behavior for that generated file.
Authorized by Captain Bob ("begin", "clean new disk",
"document, commit, and push").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Authorized by Captain Bob. Each claim re-verified against source
immediately before editing rather than from this session's notes.
Theory files: 23 becomes 52, all listed in proof/ROOT, with a pointer to
COVERAGE.md's own statement that the deliverable is the verified boundary
rather than a green build. LITHOS_VERSION: 2.0.1 becomes 2.0.0, noting
§I.2 rolled it back because 2.0.1 names the SER5 hardware-track line and
claims progress not yet verified, and that the policy is semantic rather
than sequential.
ACL pinning carried two mutually inconsistent rules, neither matching the
code: policy never in C with no vm_find_word plus field assignment, and
separately that kernel-only words should be pinned in a kernel-specific
capsule. kernel_main.c:771-782 pins BIRTH and CAPSULE-BIRTH exactly the
forbidden way, deliberately, and ACL.4th's block-4005 comment explains
why -- so ACL.4th stays host-portable. Now stated as the rule plus its
one sanctioned exception.
The mkcapsule block rule was described as a 1024-byte budget verified
with wc -c. Reading tools/mkcapsule.c shows validate_forth_blocks
enforces 64 chars by 16 lines and a block number in [2048,5120). 64 times
16 is 1024, which is where the figure came from, but the enforcement is
per-line: eight lines of 128 chars is 1024 bytes and still fails. Note
that FABRIC-3 §XXXII.2's own correction of this claim was itself
incomplete, fixing the number while missing the line-length rule.
Adds a WORK IN PROGRESS pointer directing a fresh session to
FABRIC-3.6.md's START HERE, since CLAUDE.md is what auto-loads. It states
no reshuffle code exists and that the file's current Tripod descriptions
stay correct until the reshuffle lands, deliberately not pre-writing the
post-reshuffle state.
FABRIC-3.6.md: trap 5 rewritten, since it told a fresh session to
distrust four claims that are now fixed; it now names MANIFEST.md and
FABRIC-0 §25.7 as the documents that still drift. Also removes a quoted
commit id from the handoff that will always be stale.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Captain Bob confirmed execution happens on his PC with the toolchain
installed, not in a container. Two lines of the handoff would have
misdirected the session they exist for.
Task 0.0 said it must be run by Captain Bob on a machine with the real
toolchain. On the intended machine the session can run it itself, and the
old wording would have had it wait for a human who was expecting it to
proceed. Now it says to run it, after verifying the toolchain is actually
present, and to stop and say so rather than improvise a partial test if
anything is missing. Adds a note that the unchecked state is
not-yet-attempted rather than a prior failure, since a fresh reader could
reasonably assume the latter.
The working-directory warning was written around this container's own
layout and named a path that will not exist on the target machine.
Replaced with the check that actually matters -- git remote -v must show
the Gitea repo -- keeping the container case as a parenthetical rather
than the headline.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
The GitHub reference came from this session's own harness, not from
LithosAnanke: the session was started in /home/user/StarForth, a separate
empty repo wired to github.com/rajames440/StarForth, and the task
instructions named that as the build target. The mirror claim came from
another GitHub repo's own description, which is itself stale -- per
Captain Bob, GitHub has been removed from everything and is not a mirror.
Both statements are wrong and were about to mislead exactly the audience
the handoff exists for, so they are replaced rather than softened.
The underlying hazard is real and kept in accurate form: a session
launched the same way will land in a directory that is not this project
and be told to build there. The note now says that plainly -- nothing
outside Gitea is this project, and a directory that is not a clone of
admin/LithosAnanake is the wrong place.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Written so a cold session told "go build it" can begin without
re-deriving anything. Records where the code actually is -- the Gitea
instance, not the empty GitHub StarForth repo that cost this session time
at the outset, and not the read-only mirror -- plus the branch, its head,
and that no code has been written yet.
States the two-document split and the rule that 3.5 is authoritative and
a task proving a ruling wrong is a finding to record rather than a
divergence to make quietly.
Carries the five traps this project's own history proves are real, since
a fresh session would otherwise walk into each: silent failure is the
dominant mode and a green boot is weak evidence; grep cannot establish
capsule reachability, having put six live capsules at zero references;
fleet_conserved cannot see Stadium heat, so a leaking allocator leaves it
reporting fine; dict_hash changing is expected while diverging is the
stop condition; and CLAUDE.md carries four known-stale claims to trust
the code over.
Names the three blocked decisions and the standing prohibitions -- no
branches, no stashing, no fixing in passing, no bundling, nothing without
authorization.
No credentials committed; the handoff names the host and repo only.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Records the branch state execution starts from: head 3a4e5cf, working
tree clean, pushed, 33 commits ahead of an unmoved master, all
documentation and no code.
Adds task 0.0, the three-ISA baseline smoke test on the unmodified
branch, and states why it is a gate rather than a formality. Every task
here takes the three-architecture boot as its acceptance, so without a
known-good baseline captured first the first red boot is ambiguous --
pre-existing fault, or something the task just did. This project has been
bitten by that exact ambiguity before, per FABRIC-3 §XXVI where the
lockdown was never broken and the wrong VM had been tested. The recorded
dict_hash triple also becomes the reference every later divergence check
compares against.
Records that it was not runnable in the authoring container and was
deliberately not faked: no qemu-system for any of the three targets, no
aarch64 or riscv64 cross compilers, no OVMF or AAVMF firmware, only host
gcc, clang and lld. It must be run on a machine with the real toolchain
and its result recorded here before task 0.1 begins.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
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
Agreed to move the punchlist to its own document, on the series' own
rule rather than preference. FABRIC-1 closed at 4,420 lines because "the
still-open work [was] hard to find" among hundreds of resolved ones;
FABRIC-3.5 stands at 4,452 with 40+ open items in the same shape.
Annotating 25 tasks there, commit by commit, would bury the design record
it exists to be.
The split is strict and stated firmly, because §XXXI found this series'
documents losing track of each other. 3.5 holds the design record and
stays authoritative on conflict; 3.6 holds the work. Rulings are cited in
3.6, never restated, since duplication is how two documents begin to
disagree -- and a task that proves a ruling wrong is recorded as a
finding there and amended here, never silently diverged.
3.6 restores the checkbox convention. §XXXI.2 found the carry-forward
discipline was mechanically auditable through FABRIC-2 and broke at
FABRIC-3, which has no checkboxes, after which "is anything still open"
stopped being a grep and became a reading exercise -- which is how six
design items went quiet without anyone deciding to drop them. The next
document in the series does not repeat the defect the gap analysis found
in it.
§XLI stays in 3.5 as the source 3.6 instantiates: the phase structure,
why Phase 2 sits early, and the three Phase 3 blockers with their
justification. What moved is the checklist, not the argument. 3.6 also
carries an explicit out-of-scope section so deliberately excluded work is
not absorbed by an executing session.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Answers the question asked: yes for the first three phases, no for the
fourth, and the reason is three named things rather than general caution.
Phase 0 is preparation with no behaviour change -- Category A
reachability established per §XXII.2's three routes rather than by grep,
the strips themselves, MANIFEST corrected as its files go, freed blocks
returned, stadium_conserved() added, and the framebuffer registration
audited. Phase 1 is Hestia with messaging untouched, sequenced per
§XXXIV.4 so Hermes is retained and is_fleet_foundation holds four names.
Phase 2 is the allocator and its audit, inert, ending in the Stage B
proof that §XXXIX.4 corrected: the ledger plus stadium_conserved(), with
fleet_conserved explicitly not evidence.
Twenty-five tasks across those three phases, each with its own check,
which also discharges item 34. Phase 2 is deliberately reachable early,
since §XXXIII named it the only genuinely hard part and it should fail
cheaply with nothing built on top.
Phase 3 is not buildable pending item 27 (channels: negotiation or one
membership), item 32 (SK_SWITCH_MAX_SLOTS is 16 and §XXXII made the
switcher the sole mover, so whether this is a constant bump or a table
redesign changes the phase's shape), and §XXXII.5.1, the delivery
hand-off, which is the last genuine design question in the reshuffle and
sits on the critical path.
Records a caution: phases 0-2 can run without answering those, which is a
feature, but it means arriving at a working audited allocator with the
cutover still undesigned. Better to settle the hand-off before phase 2
finishes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
New evidence found while settling this reverses §XXXVIII's framing.
messaging.4th block 5025, immediately above MSG-REAP, states outright
that K reap only fires at heat=0 so the freed contribution is 0, and
anticipates that a force-reap would require explicit K redistribution.
The zero-return at natural reap is a documented design note, not an
oversight: heat is modelled as consumed over a message's life rather than
held as a refundable deposit.
So §XXXVIII's trace stands exactly -- decay drains the field, reap
returns nothing, the eviction guard is bypassed -- but its interpretation
does not. The word leak is withdrawn, along with its recommendation to
change the economics as part of this reshuffle.
Two further roles for message heat turned up and both corroborate the
model: MSG-NACK-LAST halves it, so a rejected message ages twice as fast,
a penalty in the same currency; and delivery order does not consult it at
all, so changing it cannot perturb sequencing.
Ruling: kernel-Hermes preserves the economy exactly and additionally
records what it consumes, making the Stadium invariant read patron heat
plus reservoir plus consumed equals Q48_ONE. That is what makes item 41's
stadium_conserved() possible at all -- without a consumed term a checker
over a designed sink reports non-conservation as normal operation, which
forces a tolerance that hides real bugs. §XXXVII.3's counter is therefore
vindicated rather than deleted, and for a better reason than it was
introduced for.
Declines to separate reservation from age now, despite it being the
better architecture, on scope, measurement comparability, and the fact
that this subsystem yielded two new surprises while a third was being
settled.
Names but does not answer the real question: nothing replenishes a
reservoir, so a long-lived VM's send capacity declines monotonically, and
batched pumping accelerates it with fleet size. Fourth instance of latent
at Tripod scale, visible at fleet scale. Filed as outside this reshuffle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Traced both directions. capsule_vm_physics.c has zero references to
stadium and does not include its header; it has zero references to
reservoir. stadium.c's single vm_physics mention is a comment.
vm_physics_fleet_heat_sum() sums execution_heat_q48, whose only writers
are initialise, transfer between two VMs, and redistribute on death.
Message allocation cannot move it.
They are not even the same shape of invariant. StadiumVMQuota.reservoir's
own comment states Stadium conservation is per-VM -- resident patron heat
plus that VM's reservoir equals Q48_ONE each -- while vm-physics K is
fleet-wide across all VMs. Two scopes, two disjoint data sets, sharing
only the Q48.16 format, the constant, and the word heat. That shared
vocabulary is what made them look like one system.
Only one has a checker. vm_physics_conserved() is the sole *_conserved()
function in the kernel; the Stadium side has a documented invariant, a
human-readable diagnostic print, and MSG-K covering just the messaging
slice. So §XXXVIII's leak is invisible to every automated check -- not
because checks disagree, but because nothing is looking.
Corrects §XXXIV.6 in the dangerous direction: it made fleet_conserved the
tripwire for Stage B, but a kernel-Hermes allocator leaking every
reservation would leave it reporting a serene 1. That is §XXXV.0's
signature exactly, built into the plan, and item 39 existed to catch it
before it mattered. Stage B instead verifies kernel-Hermes's own ledger
and the Stadium per-VM invariant, and should add the missing
stadium_conserved() as its first act.
Two earlier rulings need consequent care. §XV.4's K must be qualified,
since the heat a dying arbiter holds is Stadium heat rather than the
verified fleet K -- substance stands, citation was wrong. And the
self-audit is promoted from safety net to sole instrument, which argues
for an independent Stadium checker rather than the arbiter policing
itself alone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Read the path end to end. Message heat is Stadium cell heat, since
MSG-HEAT@/! route through MSG-STADIUM-CELL@ STADIUM-HEAT@/!, so
MSG-COOL-ONE decays header->heat in the cell itself. stadium_evict
returns whatever remains to the owner's reservoir, and its own comment
says why: otherwise every reap leaks heat and the sum drifts below
Q48_ONE. But MSG-REAP fires exactly when heat reaches zero, so at
eviction time the field is already zero.
So the eviction guard that exists to stop reaps leaking is bypassed
completely for every aged-out message, and the whole Q.SLOT pulled to
send it is destroyed. That is worse than §XXXVII.6 anticipated, which
expected partial loss.
The root cause is one field carrying two incompatible meanings: an
activity metric, which should decay, and a reservation currency, which
must not. Decaying a budget token destroys budget. Whether that is
intentional is arguable -- pay to send, forfeit if uncollected is a
coherent backpressure economy -- but nothing replenishes reservoirs, so
the forfeit is permanent against a finite pool.
States a precision boundary rather than overclaiming: there are two heat
accountings, Stadium and VM-physics, and this leak is in the Stadium one.
Whether it is visible to VM-CONSERVED? was not established, so that is
filed as its own item to settle before §XXXIV.6's tripwire is relied on.
Recommends separating reservation from age as two fields, which costs
nothing in greenfield C, removes the leak, keeps reaping working and
preserves backpressure in an honest form. Notes the consequence for
§XXXVII: separating the fields restores §XXXVI.3's original invariant and
deletes the decayed counter §XXXVII.3 had to invent. Both earlier
sections were right about the destination and wrong about the obstacle --
it was never decay, it was decay applied to the wrong field.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Checking the numbers first showed §XXXVI.3's proposed invariant cannot
hold. MSG-COOL-ONE multiplies a message's heat by Q-DECAY and writes it
back, returning the difference to nothing, so heat held diverges from
heat pulled with no bug involved -- that is Loop #3's decay working.
§XXXVI's ruling that the sinking condition is a failed self-audit stands;
its arithmetic is corrected in place.
The numbers settle the epsilon on their own. Q48_ONE is 65536,
VM_PHYSICS_EPSILON_Q48 is 3277, and Q.SLOT is about 929 today or 1365
once channels are retired. So the existing epsilon is three and a half
messages' worth of heat -- loose enough to hide the exact failure the
audit exists to catch. It is not wrong, it was sized for fleet heat,
which is genuinely noisy; it must not be reused here.
Corrected invariant: held equals pulled minus returned minus decayed. All
four terms are exact integers, Q.SLOT is a compile-time constant, and
decay's delta is computable at the moment it is applied. Nothing is a
measurement, so there is no noise to tolerate and epsilon is zero -- any
divergence is a bug and the audit fires on the first unit. The one change
required is the whole trick: today decay is an invisible overwrite, so
kernel-Hermes must record what it destroys. One counter converts an
unauditable quantity into an exact one, which is why the epsilon can be
zero rather than guessed.
Cadence is running counters rather than recomputation, since
MSG-TOTAL-HEAT scans the whole arena and as a fleet-wide kernel structure
that is §XXII's O(N) wall a third time, after the pump and the switch
slot table. The check becomes one comparison of four integers, O(1), on
every mutation. A scan stays useful for verifying the counters
themselves, in Stage B and diagnostics rather than the hot path.
Flags without resolving that if decayed heat is destroyed and eviction
returns only what remains, fleet heat drifts downward over a long run --
the same shape F0 §25.7 warned of for a different, since-fixed cause.
States the limit of the trace honestly: STADIUM-EVICT's return semantics
were not read, so this is reported, not established.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
§XXXV.1 found §VIII's latch had no trigger, since kernel-Hermes is not a
Stadium patron and §VII's ladder cannot reach it. §VIII survives with a
trigger that is concrete, cheap and already precedented twice here.
Enumerates what can actually go wrong and finds only one state between
healthy and gone: corrupted internal state, still executing but unable to
route correctly and possibly unaware. kmalloc failure and budget
exhaustion are refusals, which is the economy working; a panic is not
sinking because it is already gone.
Keeps §VIII rather than retiring it, for a concrete reason: the window
buys a flush. block_subsystem.c carries dirty RAM blocks a hard halt
loses, and §XV.4 already ruled payloads may vanish, so the window's value
is that dirty blocks reach the disk.
The sinking condition is a failed self-audit: the sum of heat held in
live messages must equal total pulled minus total returned. An arbiter
that has lost track of the heat it holds is exactly one that will break K
for everyone else, and this is the earliest honest moment it can know.
Not a new mechanism -- MSG-K already audits the FORTH messaging layer's
own heat, and vm_physics_conserved() is the C shape verbatim. Notes the
honest adaptation, that MSG-K is per-VM and sums a reservoir
kernel-Hermes does not have, so the idea ports rather than the code.
Records the resulting property: K is both the thing §XV.4 says must
survive a death and the quantity whose violation announces it.
States the accepted loss rather than engineering around it. Panics raise
nothing and flush nothing, because the corruption that caused the panic
may be in the block layer and flushing corrupt data over good data is
worse than losing it. The audit narrows the window of silent loss without
closing it.
Bounds §VII explicitly, since §XXI.3's table reads as universal: rung 2
is a from-scratch BIRTH and BIRTH births VMs, so kernel-Hermes has a
two-state life, correct or stopped. The bound was always implicit in
authority flowing down the birth graph -- a thing never born has no place
on it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Assume failure six months on and work backwards, grounded in this
project's own failure record rather than generic risk.
Puts the dominant failure mode first, with four independent instances:
things here fail without saying anything -- an identical serial log for a
switch storm and a healthy idle REPL, a console-name mismatch that
quietly never relays, 6 of 9 identities silently not attaching, and FORTH
silently dropping a definition that references an undefined word.
The principal finding is a design gap rather than a risk. §VIII's sinking
latch was conceived when Hermes was a Stadium patron that could degrade.
Kernel-Hermes is not admitted -- stadium_admit() runs only on birth -- so
it has no heat, no TTL, no eviction and no compudynamic death. Its real
failure modes are a panic, which halts instantly and gives the fleet no
window at all, or allocation refusal, which is degraded but not dying and
would be wrong to latch irreversibly. §VII's ladder does not apply either,
since rung 2 is a from-scratch BIRTH and BIRTH births VMs. So the failure
story for the one component that cannot emit SOS is undesigned, and
retiring §VIII is a legitimate option rather than a tidiness problem.
Second finding: SK_SWITCH_MAX_SLOTS is 16, which was reasonable when the
switcher was one of two ways a VM got control. §XXXII made it the only
one, so 16 becomes a hard ceiling on the fleet the moment that ruling
lands, against §XXII's own note that the fleet is headed toward hundreds
of VMs. This is the pump story repeating one layer up, findable on paper
this time.
Records a hypothesis that weakened on inspection rather than pushing it:
K verification is vm_physics_conserved() in capsule_vm_physics.c, exposed
as VM-CONSERVED?, independent of the DoE, which only reports it. The
capability survives a DoE rewrite; only the continuous per-tick record
does not, so Stage B needs its own deterministic check loop.
Also flags that Hestia's ownership is conventional and therefore
checkable -- the framebuffer primitives must be registered to her alone
-- and that the Category A strip should precede the DoE rewrite while the
campaign that would catch a bad strip still runs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
§XXII.1 says Category B is load-bearing until the replacement boots,
which implies a window where both messaging layers are live, and nothing
said how they behave in it.
The hazard is not control but heat. Two allocators draw on the same
per-VM Stadium reservoir, and the budget is explicit: Q.SLOT is the
reservoir less COMMON-CH's third, split across 32 messages plus 15
channel slots. Conservation itself is not at risk, since K holds as long
as each layer honestly pulls and returns; the budget is, because a pool
sized for one layer is oversubscribed by two. The failure mode is
allocation refusal rather than silent drift, and fleet_conserved makes it
visible. Notes that §XXXIII's channel retirement removes 15 of those 47
slots, recovering roughly a third of the per-VM budget precisely when two
layers share it.
Rules the coexistence: every message type is owned by exactly one layer,
and no message crosses. Double-accounting becomes structurally impossible
rather than carefully avoided, which is the heat-side analogue of
§XXXII's control-side ruling.
Reuses §XXVIII's staging discipline rather than inventing one, since it
solved the structurally identical problem of standing up a second
control-transfer mechanism beside a live one. Five stages: inert
structures, prove the allocator against fleet_conserved, cut over one
narrow type, cut over the rest, then strip. Stage A feels like wasted
work and is what makes every later stage a one-commit revert.
Surfaces a coupling nobody had noticed. §XIX.6's obvious reading, swap
Hermes for Hestia, would break the middle stages on first boot, because
FORTH Hermes must keep routing every type not yet cut over. So
is_fleet_foundation temporarily holds four names and both births run,
with Hermes removed only at the strip. This sequences §XIX rather than
contradicting it.
Names the acceptance cost honestly -- at least eight full three-arch
cycles -- and states three tripwires that would stop the plan rather than
be pushed through.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
§XIV.1 made kernel-Hermes greenfield, which quietly exited this
document's own reorganize-don't-rewrite scope discipline, and nobody
counted the result. messaging.4th is 504 lines and 85 words; that number
turns out to be misleading in the reassuring direction.
Roughly 34 of the 85 are field accessors that vanish as C struct members.
28 are channel machinery -- and the entire CH-NEGOTIATING to CH-OPEN to
CH-CLOSING handshake has no caller anywhere in capsules, src or
experiments. CH-REQUEST, CH-ACCEPT, CH-CONFIRM, CH-CLOSE and CH-MINT-ID
are referenced only by MANIFEST.md, which is documentation. The live
channel lifecycle is one static channel created at boot with three
members added via CH-ADD-MBR and never negotiated, closed or reaped, with
console proxies opting out of channels entirely. Kernel-Hermes needs one
broadcast membership list, not a channel subsystem.
The three event words die with process.4th, which is EXEC'd nowhere, and
take SPAWN-EVENT's unwired placeholder with them.
What must survive is narrow: the heat-coupled allocator, about nine
protocol words, one membership list, the live elevation path through
zuse-eligibility.4th, and the status words that make the subsystem
debuggable at all.
States the caveat plainly: unexercised is not unwanted, and deleting the
channel abstraction is Captain Bob's decision rather than an observation.
Notes the evidence here is stronger than §XXII.2's capsule search, since
FORTH words are invoked by literal text rather than runtime-assembled
names, but that a human can still type CH-REQUEST at a REPL. Also flags
MANIFEST.md describing the negotiation as live, another entry for the
documentation sweep.
Names the one genuinely hard piece and recommends building it first: the
heat-coupled allocator, because K is continuously verified and any drift
is visible. If K holds across alloc, free and evict under the new
arbiter, the rest is protocol plumbing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
§XXXI.5 found FABRIC-3 carrying an open defect where both the MSG-TICK
pump and the Stage-3/4 switcher can independently move control between
the same VMs, a live instance of what §XV.3 forbids as a principle.
Settled, and the reshuffle turns out to close it rather than inherit it.
Kernel-Hermes publishes eligibility and does not dispatch. The switcher,
which moves control by saving and restoring a per-VM native stack,
becomes the sole path; the pump, which moves control by nested
vm_interpret on the shared kernel C stack, retires.
Three reasons. The pump is already legacy by §XIV.1, existing only to
drain per-VM FORTH queues that kernel-Hermes will hold instead, so
retiring it is a consequence of a ruling already made rather than a new
decision. It is also a known scaling wall: §XXII records it as O(N) per
idle beat with no cap, caught live at just 9 VMs on riscv64 where a
campaign that finished in 200-340s elsewhere never finished at all, then
band-aided with batching that trades per-VM latency for bounded cost,
with its own comment naming hundreds of VMs as the endgame. And "not
observed failing" carries little weight here by FABRIC-3's own finding --
§XXVIII.1 records that Stage 3 emits nothing per switch by default, so a
switch storm and a healthy idle REPL produce an identical serial log, and
the corruption it fixed was live in every previously clean boot.
Reconciles with §XV.3 rather than amending it: moving control is a
mechanism, deciding whose turn it is is a policy. The switcher is the
mechanism; the compudynamics remains the policy and is still owned by
nobody. The defect was never about deciding but about two physical paths
for the same transfer. Also notes this makes §XXIII.4 implementable,
since checking the sinking latch "on giving a VM its turn" silently
assumed a single place where turns are given.
Verifies the dependency: the ruling would strand identity VMs unless the
switcher covers them, and §XXX records Stage 4's WIREBIND-scope extension
as built and verified live. It holds only because Stage 4 landed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Reopened by instruction to add the sweep. The design phase stays closed
and the prior close header is kept rather than overwritten, per the
series' own rule against silently rewriting a prior state.
A gap analysis of a series whose central rule is never silently drop a
stale claim is an audit of whether it kept that rule. Mostly it did.
The carry-forward chain held twice and then terminated. F0 to F1 held; F1
to F2 is independently verifiable, since F2 claims a non-sampled
carry-forward of 51 items and grep returns exactly 51 today; F2 to F3
broke, with every long-lived F0 item id returning zero references in
FABRIC-3. FABRIC-3 carries no checkboxes at all, which is a legitimate
style choice but ends the mechanical audit trail -- after FABRIC-2, "is
anything still open" stops being a grep and becomes a reading exercise,
which is how the orphaned items went quiet without anyone deciding to
drop them.
FABRIC-2's close header claims every item was closed or left open pending
a physical machine. Eight of its fourteen opens are the hardware boot
checklist and the claim holds for those; six are design items with no
hardware dependency and appear nowhere since.
FABRIC-0's seven opens all remain, and three were re-derived by this
document without either side knowing: 4.3's deferred tail is verbatim
"Console as a fleet VM under Hera's birth protocol," which is §XVII and
§XVIII; 5.2 already specifies the Isabelle target as one conservation
theorem, almost certainly the K that §XV.4 rediscovered independently;
and 5.3 already asked for §XXVI.1's subsystem-doc work.
The urgent finding is that FABRIC-3 holds an open item contradicting
§XV.3. The MSG-TICK/Stage-3 dual-ownership rough edge -- both mechanisms
can independently move control between the same VMs -- is still open and
not observed failing, which is a live instance of exactly the dual
ownership §XV.3 forbids as a principle. §XIV.5's scheduler concern was a
rediscovery of it, and kernel-Hermes lands directly on that seam by
becoming the caller of sk_vm_switch_signal_mark_work(). Filed ahead of
the kernel-Hermes work rather than alongside it.
Also re-homes §17.4's undesigned framebuffer heat/decay as Hestia's, and
records that three reported-not-scheduled registries exist with
inconsistent hygiene -- §25.7 leaves two resolved entries unstruck,
including a fleet heat leak closed in FABRIC-1 that still reads as live.
No ruling in §I-§XXX is invalidated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Closed by direct instruction. Every design question this document opened
is ruled; no code has been written and none is authorized.
Deliberately a narrower close than §XXVI.5 specified, and the difference
is stated rather than glossed. §XXVI.5 ruled that this document closes
when the tag exists, in FABRIC-2's CLOSED/ARCHIVAL form, naming the tag
it closed at. The tag does not exist, so claiming that close would be a
label running ahead of the real state -- the exact error §I.2 corrected
when it rolled LITHOS_VERSION back, and the same standard §XXIX applied
to the LTS question. The archival close still stands and still happens at
v2.1.0; this header is the provisional one the instruction's own "for
now" asks for.
Restates that nothing is carried forward and no successor is created,
since this document is not in the FABRIC-0 through -3 chain, and that
FABRIC-3 remains open and authoritative for bare metal boot. Names the
punch list as the live artifact the build works from, and records the
three items that are explicitly outside this reshuffle and still need
their own authorization: the CLAUDE.md and MANIFEST documentation
reconciliation, the src/*.c.bak hygiene question, and the stray v2.0.1
branch and PR #1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
§XXIX's recommendations ratified, written as the text to carry into
Makefile.starkernel and ROADMAP.md. Resolves the two places §XXIX left as
alternatives rather than leaving them hanging.
Conflict 1: X.5.0 semantics retired. The minor field stops carrying a
milestone meaning and means only "release within the line." The
milestones survive as roadmap entries rather than arithmetic, which has a
pleasing consequence that was not contrived -- the hardware bare-metal
release that was going to be v2.5.0 is exactly where the first LTS
criterion is satisfied, so the milestone formerly encoded in the minor
field becomes the natural candidate for the first LTS major. Scheme and
roadmap agree without being forced.
Conflict 2: resolved in favour of keeping the major for LTS parity, since
that is the scheme's point, and relocating the compatibility signal
instead of abandoning it. Breaking changes land on the even working line;
an odd LTS line takes non-breaking fixes only. The line tells you whether
breakage is possible, not the major. Corollary recorded: an LTS line that
needs a breaking fix has failed as an LTS, and the fix goes to the
working line -- no exceptions, since the exception is the failure mode
the rule exists to prevent.
Conflict 3: the two version strings are independent and may coincide.
CLAUDE.md already says they do not auto-sync and they describe different
artifacts, so LITHOS_VERSION reaching 3.0.0 beside an engine already at
3.1.0 is documentation, not renumbering.
Engine VERSION closed as a rule rather than a number, because the correct
bump is a function of the realised diff and the code is not written --
picking now would be guessing, which is what §I.2's rollback was about.
Major for FORTH-79-visible semantics change, minor for words added,
removed or relocated, patch for build-only. On the current plan this is a
minor bump, to be confirmed against the real diff.
Every decision this document required is now made.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Captain Bob proposed odd-major LTS / even-major working, minor for
release, patch for working builds, CI build number appended later, and
asked directly whether this release is the first fully production-ready
one, inviting disagreement.
Policy closed here; LTS designation deferred, with criteria written down
rather than left to judgement. The disagreement is recorded plainly
because it was asked for, and rests on five findings rather than taste.
§XIV is open and was reproduced live by §XXXI's rerun -- near-simultaneous
thumbdrive attach silently dropped 6 of 9 identities with every device
confirmed present at the host level, which is the identity-attach path
failing silently. It has never booted on real hardware, which is
FABRIC-3's whole topic and what v2.2.0 and v2.5.0 are reserved for. ACL
Phase 8 is the open item. At tag time the reshuffle is brand-new code, and
LTS normally means soaked rather than freshly rewritten. And §XXV.4
expects formal coverage to contract at this tag, which is the wrong
moment to claim maximum stability to a licensee or patent audience.
The precedent is the project's own: §I.2 rolled the version back for
claiming ahead of the real state, and "first fully production ready" is a
much larger claim than 2.0.1 was.
Offers the constructive form instead of a refusal -- make LTS a test, the
same move §XV.2 and §XXVII already used to better effect than judgement.
Five criteria proposed; the first tag meeting them takes the odd major.
Flags three conflicts in the new scheme while closing it: the minor
position already carries QEMU-versus-hardware semantics with v2.5.0
already planned, odd/even major collides with major-means-breaking, and
an odd major lands LITHOS_VERSION on 3.0.0 where the engine string
already lives. Recommends SemVer build metadata for CI numbers rather
than a fourth dotted field, since the SBOM is syft-generated SPDX and
X.Y.Z.N is not valid SemVer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
LITHOS_VERSION becomes 2.1.0 and the tag cut at close is v2.1.0.
Checked against the authoritative policy rather than §I.2's paraphrase of
it. Makefile.starkernel carries the roadmap table itself, and it is more
specific: X.0.0 is a QEMU release, X.5.0 a hardware bare-metal release,
with 2.0.0 QEMU, 2.0.1 the SER5 hardware-track line, 2.2.0 amd64
bare-metal and 2.5.0 the hardware release. 2.1.0 is unallocated, so the
ruling takes a real gap rather than overloading a rung, and it lands
below both bare-metal milestones -- correct, since this is QEMU-only work
and must claim no hardware progress, which was §I.2's whole concern when
it rolled the version back.
Notes the table must gain a 2.1.0 line or the ladder will step 2.0.1 to
2.2.0 with a released tag sitting in a gap it does not mention. Two live
places to edit; FABRIC-2 §G is the origin but is closed/archival, so it
is cited rather than amended -- amending a closed document to match a
later decision is what this series' discipline forbids.
Flags a trap for the documentation sweep with precedent. Makefile.starkernel
is not a .md file, and §I.2 records a FABRIC rename that looked complete
but silently skipped it along with Kconfig.kernel, a shell script, four
proof/*.thy files and an .S source, because the sweep matched four
extensions by inclusion. The sweep must grep by exclusion instead, reuse
§I.2's ordered placeholder technique, and keep §I.2's own deliberate
exclusions -- settings.local.json's command log and ClaudeEXPORT are
frozen audit records, and rewriting them would falsify an audit trail
rather than fix a citation.
Leaves the engine VERSION string explicitly unruled. It is independently
tracked, plausibly moves given the registration and messaging changes,
but is a separate decision at tag time.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Tested §XVI.7's premise against the code rather than designing to it, and
the premise is false. An empty Stadium floor requires Hera to be gone,
and every path by which she can cease already terminates the machine.
She cannot starve: she is pinned via session_set_pinned(), and stadium.c
refuses pinned patrons at :453 and skips them at :559. She cannot be
killed: mama_word_kill()'s own doc says Hera cannot be killed, and
capsule_vm_kill_all_nonmama() spares her by construction. BYE reboots.
Scuttle and panic both halt. There is no route to a live kernel over an
empty floor.
So the ruling is to build nothing, and §XVI.7's proposed shape is
withdrawn. An empty-floor halt would be defensive code for a state that
cannot occur, which CLAUDE.md names directly as a thing not to add.
Notes that the pinning result generalises -- no Tripod leg can die a
compudynamic death, since all three are pinned by the same
is_fleet_foundation line, which puts §XV.2's compudynamic death exactly
where it was always aimed: the unpinned population.
Records that sk_hal_panic() already executes while(1){arch_halt();},
character for character the idiom §XXIV.2 specified for the suicide word.
The two converge on one terminal state by different triggers: a scuttle
is a panic Hera chose.
Closes by stating the three invariants the ruling depends on, since a
future change could break them silently -- Hera stays pinned, KILL keeps
refusing her, and involuntary death always ends at a permanent halt. The
first is edited by this very reshuffle when Hermes leaves the
is_fleet_foundation triple and Hestia enters it.
Design phase complete. Every question this document opened is ruled.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Captain Bob, 2026-09-19: documentation sweep and SBOM/SPDX before making
it master's head and tagging, and that is when FABRIC-3.5.md closes. The
tail of the sequence is now fixed and the document has a defined end.
Consolidates item 20 into a real checklist rather than a hunt. CLAUDE.md
alone accumulated four traced errors during this design pass: 23 theory
files against an actual 52, LITHOS_VERSION 2.0.1 against an actual 2.0.0
that FABRIC-3 §I.2 corrected down and CLAUDE.md never followed, the
three-way ACL-pinning contradiction, and the stale 1024-byte mkcapsule
framing. Plus the Tripod membership itself. MANIFEST corrections ride the
strip so the manifest never describes a file that no longer exists, and
the superseded subsystem docs need a deliberate update-or-archive call
rather than a silent edit.
SBOM is a real syft target in the hosted Makefile, so the step is run and
commit -- but the committed output is dated 2026-07-24 and its
DocumentName says StarForth rather than LithosAnanke, plausibly a
monorepo-split artifact. A bill of materials naming the wrong product is
read literally by exactly the audiences it exists for.
Flags that the version for this work is a real decision and that 2.0.1 is
not available for it. Per §I.2 the policy is semantic: 2.0.0 is the QEMU
line, 2.0.1 is the SER5 hardware track, and §I.2 rolled the version back
precisely because claiming 2.0.1 implies hardware verification that is
still FABRIC-3's open topic. This reshuffle is QEMU-only work.
Reports two pieces of unexpected remote state without acting on either.
refs/heads/v2.0.1 still exists although §I.2 records deleting it and
concludes master is the only branch; and refs/pull/1/head exists against
this branch although this session opened no pull request and the repo's
workflow forbids them unless asked.
Records how the close is written: FABRIC-2's dated CLOSED/ARCHIVAL form,
naming the tag, creating no successor since this document is not in the
carry-forward chain, and leaving FABRIC-3 open and authoritative for its
own topic.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Captain Bob, 2026-09-19. The DoE's invocative paths are slated for
rewrite, so doe_log.c's six Tripod-hardcoded columns stop being the
expensive consequence §XIII.3 named -- the schema change is absorbed
rather than paid, and doe-campaign.4th's five by-name Hermes sites fold
into the same rewrite. Notes what this does not relax: §XXII.3's
strip-safety rule stands on its own footing, since rewriting how the
apparatus is invoked is not discarding the apparatus, and the audit and
patent-support roles are unchanged. Experiment capsules stay
live-by-default during the strip.
A formal re-verification pass runs at the end, giving the sequence a
symmetry -- the surgical strip opens it, Isabelle/HOL closes it.
Corrects CLAUDE.md while scoping it: it claims proof/ holds 23 theory
files, 18 core plus 5 ACL. There are 52, all listed in proof/ROOT, and
COVERAGE.md already says 52. Stale by more than a factor of two; added to
item 20's documentation reconciliation.
Records the standard the pass must meet, which COVERAGE.md already states
in Bob's own framing: a clean pass/fail is not the deliverable, the
boundary between formally verified and not-for-this-reason is. A suite
that still compiles while quietly covering less would satisfy the build
and fail the standard.
States a cost of this reshuffle not previously named anywhere. COVERAGE.md
scopes the suite to words over modelled per-VM state and explicitly flags
implementations that reach outside it via file-scope statics. Messaging
today is FORTH over per-VM dictionary state, inside the model;
kernel-Hermes makes it C with file-scope statics, outside it. So the
reshuffle is likely a net reduction in formal coverage of the messaging
area -- plausibly the right trade given §III.2's circular-dependency
argument, but a real one the pass must name rather than let the boundary
move quietly. Partially offset by the one-way latch being close to the
easiest thing here to prove.
Maps the affected theories, and carries forward the observation that
ACL_No_Escalation.thy already proves no-escalation at word level, making
§VI.1's central safety claim the same theorem one level up. The
reshuffle's most important safety property is already half-proven at a
different granularity.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Captain Bob, 2026-09-19. mama_word_bye() keeps its current behaviour
exactly -- reap children, cold-restart -- which is the right thing for an
operator typing BYE at Hera's REPL, the argument §XVI.2 flagged for two
words.
The larger consequence is that this reshuffle now modifies the behaviour
of zero live registered words. §XVI.2 had flagged Hera's BYE as the one
place the design proposed changing one; it is now not changed at all.
The two words share their first half and differ only in the terminal
action: cold reset versus a permanent arch_halt() loop behind disabled
interrupts. That idiom is not new either -- it is what amd64's own
arch_cold_reset() already uses as its unreachable fallback and what the
panic path uses, and arch_halt() exists on all three architectures, so
parity holds with no per-arch work. Hera-only registration, since §IX.1's
no-handoff rule means no other VM may stop the machine on her behalf.
All seven candidate names are unregistered. Recommends SCUTTLE without
deciding it -- to scuttle is to deliberately sink the vessel you command,
and it separates cleanly from Hermes sinking. Cautions against HALT,
since vm->halted already means something different and reusing the stem
invites the ambiguity §XIX renamed a Tripod leg to avoid.
Records a three-way contradiction found while placing the new word's ACL
pin. CLAUDE.md's hard rule forbids policy in C and specifically forbids
vm_find_word plus field assignment for pinning in kernel_main.c; CLAUDE.md
later says to pin kernel-only words in a kernel-specific capsule; and
ACL.4th's own block-4005 comment says they are pinned in kernel_main.c so
that file stays host-portable. The live code does the third:
kernel_main.c:771-782 is exactly the forbidden construct, deliberate and
working. Recommends matching the working code rather than the rule the
working code already breaks, and files the reconciliation as item 20 --
reported, not fixed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
The last open design question. The signal is a plain scalar in
kernel-Hermes's own translation unit, written only by Hermes, read by
every VM through a registered primitive. That invents nothing: it is the
same registration path §XX already established when register_child_vm_
words() hands the eight STADIUM-* primitives to every VM and Hera's two
sites mirror them. Reading a scalar needs no queue, channel, routing
table or working arbiter, which is precisely the requirement -- the
transport is the thing that failed.
Names it accurately. "Semaphore" was the original framing but the
mechanism is a one-way latch, and the distinction carries correctness.
Per §VII.1 a VM attempts its own recovery before announcing anything, so
raising the latch means recovery already failed and there is no path back
to not-sinking. Single writer, many readers, one irreversible transition,
both observable values valid -- which is the discipline heartbeat.c:33-34
already states verbatim and §XXVIII Stage 3 already reused once rather
than inventing new locking. This is the third such variable and takes the
same route for the same reason. Calling it a semaphore in code would
imply counting and blocking semantics it must not acquire.
Settles who reads it, which is the part §XXVIII's shared C stack
constrains: a VM has no thread and cannot poll. So the check happens at
the dispatch point, and the split is the design -- the kernel delivers
the fact, the VM runs its own flush-and-BYE in its own context. The
kernel publishes, never decides, so §XV.3 and §XIV.5 hold, and each VM
running its own shutdown is what §IX.1 already ruled. The window needs no
deadline because Hera's halt is physically terminal.
Completes §VIII.2's two-mechanism rule with no exceptions list: floor VMs
send SOS through routing, the arbiter beneath raises the latch, and which
one applies is decided entirely by which side of the routing layer the
sender sits on.
Every design question this document opened is now ruled. What remains is
build sequencing plus two small local decisions, items 17 and 18. No code
is authorized.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Captain Bob, 2026-09-19: strip the anticipated-dead FORTH before coding;
the real gaps reveal themselves as we code/test iterate, and further dead
FORTH falls out in the same loop. Re-orders the punch list to put the
strip first.
Draws the ordering distinction that protects it. Category A is dead today
independent of this reshuffle and can be stripped immediately. Category B
-- hermes/init.4th, most of messaging.4th, the routing table, the slot-3
pairing -- is dead only on arrival of kernel-Hermes and is load-bearing
until the replacement boots. "Strip before we code" must not be read as
deleting the working messaging layer with nothing behind it.
Records the trap this document nearly walked into while building the
inventory. A reference count over capsules/ and src/ put the six
init-l8-* personality capsules and sdk.4th at zero references. They are
not dead -- including experiments/, tools/ and docs/ finds 18-19 hits
each. Reachability cannot be established by grep in either direction: it
under-reports because capsules are birthed by name from runtime strings,
and over-reports because a capsule named for a common word returns prose
noise. So no strip list is authoritative, including any this document
produces; reachability is per capsule via boot path, tooling invocation,
or the baked capsule directory.
Names the asymmetry that makes "surgical" the right word. The three-arch
boot is the only acceptance test and it exercises the boot path, so a bad
Category A strip fails loudly. It does not exercise DoE capsules, so
deleting one passes all three architectures cleanly and surfaces months
later during a campaign -- and that infrastructure has an evidentiary
role, since CLAUDE.md records the logs as committed audit artifacts and
the campaign report as patent support material. Proposes treating every
experiments-reachable capsule as live by default.
Sets the loop: strip Category A in isolated commits, boot three arches
before writing anything new, then code, then strip each Category B item
in its own commit as it is proven dead. Notes dict_hash moves every time
and cross-arch identity is the property that must hold.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Actionability is a property of the relationship, not of the message.
To the sender's birther an SOS is actionable: the parent may re-birth,
exercising its own birth authority rather than inheriting the child's, so
§IX.2 is untouched. To every other VM it is advisory -- a peer may shed
its own dependence on the sender but may not act on its behalf. One rule,
no exceptions list: an SOS is actionable exactly where the birth graph
already grants authority.
Pressure-tested against the supervisor risk §XIV.5 warned about, and it
survives: message-driven rather than polling (the doctrine's own
sanctioned form), optional rather than obligatory, no declarer since the
sender reports its own trouble, and no authority transfer.
What this actually closes is larger than a message type. §VII.1's ladder
had no actor for rung 2, and now each rung has exactly one: the VM itself
warm-restarts while alive, the birther re-births on SOS, and nobody acts
on compudynamic death. That also shows §IX.1's fleet-shutdown rule is not
a special case bolted on for Hera -- she has no birther, so rung 2 has no
actor and the ladder falls through to rung 3. The ladder and §IX are the
same rule from two ends: rung 2 exists iff you have a birther.
Flags a hazard specific to this system. Payloads here are FORTH text
executed by the receiver, per messaging.4th block 5041's own comment. An
SOS carrying an executable payload would let a failing VM drive its
parent, inverting the authority direction at the moment the sender is
least trustworthy. SOS payloads are descriptive only -- a deliberate
departure from the convention, and the reason the message exists.
Number: 10, the next in sequence. Verified 5 and 6 are genuinely
unallocated, but monotonic allocation means a number in any log means one
thing forever, which matters while legacy constants and the new kernel
enum coexist during cleanup. Names the consumer explicitly so this does
not become another SPAWN-EVENT, which §I.4 recorded as having none.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
§XVIII.5 posed agent binding as widen-the-convention vs. add-a-word.
Both accept a premise the code does not support: the pairing is already
recorded explicitly at bind time, and the ~user reconstruction is a
redundant second derivation of information the mechanism already holds.
sk_repl_dispatch_line() does two separable things. The guard builds
console_get_vm_name() + "~user" and checks it is live. The actual relay
then sends to index 3 -- and repl.c's own comment says that slot is "set
once at pairing time," which PAIR-TEST's header confirms from the other
side. Routing never uses the derived string; only the liveness guard
does, and the stored pairing can answer that question without deriving
anything.
This is also the documented cause of a known silent failure.
CONSOLE-ATTACH's own comment records that a console named anything other
than the identity's base name reconstructs the wrong target and quietly
falls back to direct interpretation with no error. Its "takes exactly one
name" constraint is a workaround for that derivation, not a requirement
of the problem.
Ruling: the guard reads the recorded pairing, CONSOLE-ATTACH resolves the
target as given, and ~user survives as a naming convention rather than a
mechanism. Agent binding then stops being a special case -- a GPIO VM
binds by the same unchanged path, because nothing appends anything to its
name. Punch item 19 is retired rather than fixed: with no string to
mismatch there is nothing to make loud.
The six memcpy sites split cleanly -- three name a VM at birth and stay,
two re-derive a partner for lookup and go. Only the derivation sites
change, which is why the convention can survive without the mechanism.
Also separates what §V.1 conflated: reachability is kernel-Hermes's,
fabric access is vocabulary and ACL, and binding proper is the console
path. An agent VM does not routinely bind at all.
Scoped per §XIV.1: this states the principle kernel-Hermes must carry
forward, not a proposal to patch the legacy FORTH path in place.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
The Tripod is Hera, Artemis, Hestia. 99 occurrences renamed; three
verbatim quotations deliberately left saying Console.
Records the naming convention, which had never been written down and was
being re-litigated for want of it: a Greek deity name, evocative rather
than literal. Captain Bob's reasoning, and it generalizes -- Hera and
Hermes are close fits, Artemis for block storage and freemap arbitration
is a loose one that has never caused anyone a problem. Fidelity was never
the standard.
antiprosopos was considered first and rejected for being the wrong kind
of word: a common noun straining for literal accuracy, which is the
opposite of how the other three work. Hestia is a deity, is evocative --
the hearth is the fixed centre where everyone gathers, which is what a
bind point is -- and carries an incidental that was not the reason for
choosing it: Hestia is the one who never leaves, which is the pinned
session model FABRIC-2 §H.1 already describes. It also retires both costs
the longer name carried, restoring the pattern fully and cutting twelve
characters of unusual spelling to six in a path where a name mismatch
fails silently.
The rename itself is structural rather than cosmetic. §XVII.1 found three
distinct things all called console, and that collision is what led
§XIII.5 to the wrong conclusion about whether a singular Console existed.
"Hestia owns console.c" is unambiguous in a way "Console owns console.c"
cannot be.
Deliberately narrow: the HAL stays console.c, the proxies stay console
proxies, and CONSOLE-ATTACH keeps its name since CLAUDE.md's rule against
modifying a registered tested word applies with more force to renaming
one. Keeps the ~user suffix question separate and still open as item 3,
with the buffer analysis recorded so the two are not conflated later.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Console is three layers and only the middle one is new: the HAL fabric
(~98 KB of C) is unchanged, the per-attach proxies are unchanged, and
Console the VM is a capsule rather than a mechanism.
The finding that shapes the design is that 1,051 lines of Console
vocabulary already exist in the wrong dictionary. fabric.4th's own header
calls it "Console drawing-fabric coordinate machinery" and says outright
that it is the FORTH-side policy over the C pixel primitives -- and it
loads into Hera, along with font.4th, from init.4th block 2049. Moving
both to capsules/console/init.4th is a pure relocation and the clearest
evidence that the fabric wants an owner: it already has the vocabulary,
just attached to the wrong VM. PLOT/FB-WIDTH/FB-HEIGHT registration moves
with it; the implementations do not change.
Records the invariant that constrains everything else. console.h:220-224
states that console.c must have no dependency on capsule or WIREBIND
logic, calling it a real layering violation rather than a style
preference. So Console the VM cannot be built by making console.c
VM-aware: ownership flows one way, and where the HAL genuinely needs
something VM-shaped it takes the shape already established by
console_set_user_prefix_provider() -- a registered callback, never an
include. Console's ownership is therefore by vocabulary and registration,
not enforcement, which is weaker than it sounds and exactly right.
Carries the headless-until-login invariant onto this second path: Console
is born at boot, a console session is not, and Console's birth must not
touch g_wirebind_attached_username or mint a proxy. Same rule §XXXII.2
imposed on unattended birth, cheap to hold and easy to violate with a
well-meaning startup line.
Notes what Console does not own, since a component owning "the bind
point" attracts responsibilities belonging elsewhere, and records that
the three Tripod legs have three different failure characters -- Console
degrades gracefully because the fabric is HAL C and survives its owner,
which is why only Hermes needed the semaphore.
One genuinely open design question remains: whether agent-VM binding
widens the ~user convention or takes a sibling word.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Closes the largest remaining design item, the one gating §IV.
§XIII.5 got this wrong by comparing the wrong object. It measured the
Tripod legs against capsule_console_birth()'s per-attach proxies, found
Console "plural, ephemeral and capsule-less," and concluded a singular
Console did not exist -- so §IV.1 would be inventing one. Traced: there
are three distinct things called console here, not two. The HAL drawing
fabric (hal/console.c, framebuffer.c, vt100.c, font_8x16.c, ~98 KB of
kernel C) is already singular, already permanent, already kernel-resident.
It is not a VM and has no owner.
So the Console leg is that fabric given a VM face, not a promoted proxy.
The proxies stay plural and ephemeral, which is correct -- they are what
binding produces, not what Console is. §V.1's bind point then falls out
rather than being asserted: the VM owning the fabric is necessarily what
you bind to. §XIII.5's proposed shape was right; its justification was
wrong, and is corrected in place.
Records the evidence that the fabric actually wants an owner, which is an
argument for the reshuffle this document had not previously identified.
The ownerless console_set_vm_name() global has already caused a live bug,
documented in console.h:193-203 -- get_vm_name() is unsafe for
save-then-restore because an intervening set overwrites the saved
pointer's own bytes before the restore runs; found live 2026-08-28,
silently no-opping, with console_save_vm_name() existing solely to work
around it. That is the signature of shared mutable state with no owner.
Notes what the ruling commits to: a new capsules/console/init.4th (the
one genuinely new artifact, and it is a capsule rather than a mechanism),
the is_fleet_foundation triple changing membership, kernel_main.c's
Hermes birth becoming Console's, the DoE CSV schema change, and Console
needing BIRTH registered. §IV is unblocked.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Captain Bob, 2026-09-18. A clean shutdown is a VM termination where flush
is forced and the VM says BYE; Hera is the special case that halts the
processor instead.
Traced, and the ruling is almost entirely existing mechanism.
blk_flush(0) is already the documented flush-all sentinel
(block_subsystem.c:988-990) and already the Stadium's own write-back
action. A child's BYE already terminates it -- system_word_bye sets
vm->halted, which mama_forth_words.c's own doc comment describes as the
child path. arch_halt() exists on all three architectures
(amd64:207, aarch64:126, riscv64:170), so three-arch parity holds
without new per-arch work.
The one real finding is a behavioural change rather than a gap: Hera's
BYE does not halt today, it cold-restarts. mama_word_bye() reaps children
then calls arch_cold_reset(). The reaping half already matches the
ruling; the terminal action is the opposite of it. Flagged so the change
is made knowingly -- CLAUDE.md forbids modifying a registered tested word
to "fix" it, and this is a ruled change rather than a fix, but it should
not surface first in a diff. Whether suicide replaces BYE or becomes a
separate word is left open as item 17, noting cold-restart-on-BYE is
plausibly wanted for an operator typing BYE at Hera's REPL.
Records a constraint on where the flush goes: system_word_bye lives in
shared vendored source that must keep working in the hosted build, so the
forced flush belongs in the kernel-side shutdown path, not inside BYE.
§VIII.3's bounded-window question answers itself -- the window ends when
Hera halts, a bound by construction with no timeout to tune, which is
consistent with §XV.3's no-owner invariant. Item 9 is now half-answered.
Raises one residual edge as analysis only: if Hera is already dead,
nobody performs the halt, leaving a live kernel over an empty floor. An
empty floor is arguably a kernel-observable condition rather than a VM's
decision, which would terminate without any VM inheriting her authority.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Captain Bob, 2026-09-18. Any VM may birth a near-clone of itself carrying
its own ACL DNA; nobody declares a VM dead, it dies a compudynamic death;
nobody owns turn order, turn order is compudynamic.
These close six punch items. Birth-by-near-clone settles what "ACL/DNA"
refers to -- inheritance by copy, using the existing ACL-INHERIT
semantics, needing no fifth DictEntry field -- and settles "standing" as
decorative, confirming the minimal reading. The authority bound holds
literally rather than aspirationally: a child cannot exceed its parent
because it is built from it, so there is no gate to bypass.
Compudynamic death retires the largest scheduler-shaped risk in the
design. §VII's ladder appeared to need a supervisor watching liveness,
and a supervisor with a timer is a scheduler's twin; with death as a
physical outcome rather than a judgment, that component is not needed and
does not exist. §VII.3 and §IX.3 both asked the wrong question and are
marked withdrawn and premise-rejected in place.
Turn order gets a stronger invariant than the firewall previously
proposed: that one named an owner, and naming an owner is what a
conventional scheduler is. Nothing owns turn order. Flags one honest
seam for later, predating this work and belonging to §XXVIII:
SK_SWITCH_READINESS_THRESHOLD is a hardcoded 50, the same category of
fixed policy §XIX rejected.
Reframes the message-durability question that was posed badly enough to
be unintelligible. It is not persistence but conservation: a message
holds Q.SLOT of heat (MSG-ALLOC pulls, MSG-FREE-NODE evicts), so a dying
arbiter holds N x Q.SLOT of fleet heat. Payloads may vanish; the heat may
not, or K breaks. Nothing survives except K. This dissolves the
Artemis-at-death-time hazard entirely rather than trading against it --
there is nothing to persist, and since death is already a Stadium
eviction, returning heat is what dying consists of.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Captain Bob, 2026-09-18. The existing FORTH messaging layer is legacy --
kernel Hermes is greenfield C, not a migration of messaging.4th, and the
dead FORTH gets retired by a separate cleanup pass. Persistence goes
through Artemis wherever possible; that part is ratified.
Together these dissolve rather than answer four open questions. §III.4,
named the root dependency throughout §X, is resolved in the "full
centralization" direction but without the migration cost that made it
frightening: with the FORTH legacy there is no live state to port. §III.3
loses its subject, and §XIII.1's 14-site inventory changes purpose rather
than content -- it now feeds the cleanup. §IV.2 dissolves via its own
option 3, exactly as that section predicted. The MANIFEST corrections are
absorbed into the cleanup pass. All four marked superseded in place with
pointers rather than rewritten.
Traced the layering question the persistence ruling raises, and it
answers cleanly: storage is already two layers, so no inversion is
needed. The block subsystem is kernel C below the floor
(block_subsystem.c, blkio_*.c, virtio_blk.c; capsule_loader.c:98-100
calls it with no Artemis involved), while Artemis is the storage policy
arbiter on the floor. Fleet persistence goes through Artemis; anything
kernel Hermes needs uses the same block layer Artemis sits on.
Flags the one hazard that ruling carries: Hermes must not depend on
Artemis for persistence at its own death, since reaching Artemis requires
the mechanism that is broken -- the same trap §VIII solved for signalling
with a semaphore. Recommends a stateless arbiter holding only
registry-reconstructible state, offered as analysis, not a ruling.
Also records the scheduler firewall as raised-but-unruled, and why the
legacy ruling makes it more urgent: eligibility marking moves inside the
arbiter by default once kernel Hermes replaces FORTH MSG-SEND as the
caller of sk_vm_switch_signal_mark_work().
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Items 1 and 2 were the two pure-investigation entries, carrying no design
commitment, so they were executable without a ruling. Traced against the
tree at e56974e. No code written, no open design question answered.
Item 1 CLOSED: the by-name Hermes inventory is 14 FORTH sites across 5
files, 3 C-side sites, and a DoE CSV schema site -- not the two this
document's own §I.2 claimed. §I.2 corrected in place with a pointer
rather than rewritten, per the series' never-silently-drop rule. Good
news in the count: only 3 of the 14 are live production paths, which
materially lowers the estimated cost of §III.3.
Item 2 CLOSED: FABRIC-2 §H.1 (line 3699/3713), §H.12 (line 4057, also
cited from capsule_birth.c:785-788), and FABRIC-3 §XVIII (K held at
Q48_ONE) all verified against source. §V.3.4 and §VII.4 may now be
relied on.
Three findings not previously recorded. The Tripod is hardcoded as a
name-triple in C at capsule_birth.c:793 (is_fleet_foundation, the line
that makes a VM pinned), kernel_main.c:865 and :1007, plus six DoE CSV
columns in doe_log.c -- so changing Tripod membership changes the CSV
schema and bears on campaign comparability. Seven of the inventoried
FORTH sites are dead code in two capsules loaded by nothing, and
MANIFEST.md still carries a block-4055 "immutable ABI" claim FABRIC-2
declared stale and nobody corrected; reported, not fixed, as new punch
item 13.
Most consequential: Console today is plural, ephemeral and capsule-less
-- a 3-line C string literal, deliberately off the routing table, minted
fresh per attach. It is none of the five things the other Tripod legs
are, so "Console takes the vacated third slot" is not a relocation as
stated. Raised as punch item 12, gating §IV. A resolution shape is
offered as analysis, explicitly not ratified. Also notes that §V and §VI
are one change rather than two: Console cannot be the bind point while
BIRTH stays Hera-exclusive, since binding mints a proxy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
Opened by direct instruction as a standalone document rather than a
FABRIC-4 scratchpad entry: the reshuffle is a ratified architectural
direction needing full FABRIC discipline, but it is not bare-metal boot,
so it does not belong in FABRIC-3 and does not close it. FABRIC-3 stays
open, living and authoritative for its own topic.
Records what was decided 2026-09-18 (Hermes into the kernel as arbiter
rather than client; Tripod reconstituted as Hera/Artemis/Console; Console
as the fleet bind point; BIRTH general with authority bounded by
inheritance; one gentle -> from-scratch -> brutal recovery ladder; SOS as
a standard message type with Hermes excepted via a sinking semaphore; no
provisional handoff of a failed birther's authority), and separates it
from what is still open.
Grounded against the tree rather than recalled, with provenance marked
per claim. Notes the concrete consequences the move runs into: Artemis's
two by-name S" Hermes" VM-EXEC call sites, the routing table's written
"never renumbered" commitment on slots 0/1/2, and the per-VM CREATE/ALLOT
message arenas that decide whether this stays a reshuffle or becomes a
rewrite.
Design only. No code authorized, nothing else in the tree touched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
2026-09-18 21:21:38 +00:00
283 changed files with 1369497 additions and 1184 deletions
> progress.** Hermes moved into the kernel as kernel-Hermes (`src/starkernel/vm/kernel_
> hermes.c`); the Tripod is now **Hera / Artemis / Hestia** — **the Hermes VM, `capsules/
> hermes/init.4th`, and `capsules/common/messaging.4th` no longer exist in this codebase.**
> Design is complete and ruled in **`FABRIC-3.5.md`** (authoritative); execution is tracked in
> **`FABRIC-3.6.md`**. `FABRIC-3.md` remains open and authoritative for its own topic (bare
> metal boot); nothing in the reshuffle supersedes it. **The Tripod/fleet descriptions
> elsewhere in this file have been updated to match** (Hera/Artemis/Hestia, no Hermes) — this
> is no longer a "stays correct until it lands" caveat, it already landed. Remaining:
> Isabelle pass ✅, doc sweep (this note) ✅, SBOM/version bump/master-merge still open — see
> `FABRIC-3.6.md` Phase 5 for status.
> **Scope:** This repo is LithosAnanke — the bare-metal UEFI kernel that boots StarForth
> directly on hardware. StarForth (the hosted FORTH-79 VM) has its own separate repository
@@ -57,8 +76,9 @@ about "which line am I on."
- **BIRTH, RUN, USE are primitives** — registered in C exactly like DUP, BYE, EXEC. Use `' BIRTH` directly. Never reach for FIND, never add conditionals, never rename them to `CAPSULE-BIRTH` or anything else.
- **FIND is a proven, tested, registered word. Never modify it.** The implementation is intentionally non-standard (parses from input stream). It is tested. Leave it alone.
- **Never modify a registered, tested word to "fix" it.** If something seems wrong with a word, the problem is almost certainly in the caller, not the word.
- **ACL policy belongs in `ACL.4th`, never in C.** No policy logic in `kernel_main.c`, no `vm_find_word` + field assignment for pinning. Use `' WORD ACL-PIN` in FORTH exactly as IMMEDIATE works.
- **`' BIRTH` in shared capsules breaks the hosted build** — BIRTH is kernel-only. Pin it in a kernel-specific capsule, not in `ACL.4th` which is shared. `capsules/ACL.4th` itself documents this exclusion in a comment (line ~64) — it deliberately omits `BIRTH`/`CAPSULE-BIRTH` even though this IS the kernel repo, because `ACL.4th` is meant to stay portable/shared.
- **ACL policy belongs in `ACL.4th`, never in C — with one sanctioned exception, below.** Use `' WORD ACL-PIN` in FORTH exactly as IMMEDIATE works.
- **The exception: kernel-only words are pinned in C, in `kernel_main.c`, after capsule load.** Corrected 2026-09-19 — this file previously forbade `vm_find_word` + field assignment outright and separately advised pinning such words "in a kernel-specific capsule." Neither matched the code. `kernel_main.c:771-782` pins `BIRTH` and `CAPSULE-BIRTH` exactly that way, deliberately, and `capsules/ACL.4th`'s own block-4005 comment explains why: they are "kernel-only, not in hosted VM. Pinned in C (`kernel_main.c`) after capsule load instead, **so this file stays host-portable**." Follow the code. Do not add C-side pinning for any word that *could* live in `ACL.4th`.
- **`' BIRTH` in shared capsules breaks the hosted build** — BIRTH is kernel-only. `capsules/ACL.4th` itself documents this exclusion in a comment (line ~64) — it deliberately omits `BIRTH`/`CAPSULE-BIRTH` even though this IS the kernel repo, because `ACL.4th` is meant to stay portable/shared.
### On the Embedded VM vs. the Kernel
- **`src/vm.c`, `include/vm.h`, `capsules/`, `src/word_source/` are the shared/vendored VM
| 2049 | No | Hera VM init: loads compudynamics, VM-INIT, lib, common:msg, fleet-k, process; BIRTHs Artemis + Hermes. VM-TREE + VM-CHILDREN implemented (`b52281b9`), unrolled for v1 Tripod |
| 2049 | No | Hera VM init: `VM-TREE`/`VM-PARENT`/`VM-CHILDREN`; loads `ACL.4th`, `block-acl.4th`, `zuse-eligibility.4th`, `lib.4th`. **Corrected 2026-09-19 (three times)** — first pass removed a stale listing of `compudynamics`/`common:msg`/`fleet-k`/`process` (none loaded here; two already-deleted, two stripped in the same pass, FABRIC-3.6.md tasks 0.2/0.3). Second pass (same day): `fabric.4th`/`font.4th`**removed** from this list — relocated to `hestia/init.4th` (FABRIC-3.6.md tasks 1.6/1.7), Hera no longer loads either. Third pass (2026-09-22, FABRIC-3.6.md Phase 4 Stage E): `common:messaging.4th` + `MSG-CD-INIT`**removed** — the file itself is deleted (see "Deleted capsules" below); every FORTH-owned message type was cut over to kernel-Hermes first (Phase 3) |
| 2050 | No | BOOT-BANNER call — separated so the banner block can be swapped without touching init logic |
| 2051 | No | TRIPOD-TEST — 6 acceptance gates per TRIPOD.md. Dead EVENT-WAIT step replaced with HERMES-TICK liveness check (`3436d564`) |
| 2052 | No | TRIPOD-TEST invocation block |
@@ -308,14 +308,6 @@ Blocks: **4050**
|-------|-----------|---------------|
| 4050 | No | N. COMMA CRLF CSV-COL CSV-LAST Q.SHOW USE RUN. Not immutable: these are utilities; adding words is safe. Removing or renaming existing words requires audit of all callers |
| 4055 | Yes | HERMES-ACK / HERMES-NACK. Immutable: this is the cross-VM ACK/NACK ABI. Every messaging VM (Hera, Hermes, Artemis) loads this at birth. Changing the block or the word names breaks the Hermes delivery protocol |
### `doe-campaign.4th` — DoE campaign harness
Blocks: **4060–4065**
@@ -325,74 +317,51 @@ Blocks: **4060–4065**
matched the file at any point on record. Rewritten to match the actual
content, migrated this pass off the deleted `compudynamics.4th`/
`fleet-k.4th` primitives onto the `VM-PHYSICS-STATUS`/`VM-CONSERVED?`
mechanism — see `capsules/hermes/init.4th` block 4153 and
mechanism — see (historical) `hermes/init.4th` block 4153 and
`capsules/artemis/init.4th` block 4852 for the companion `LOAD-DOE` fix
this migration also required.
**Note (2026-09-22, FABRIC-3.6.md Phase 4 Stage E):**`SETUP-HERMES` and every
Hermes touch in `PHASE1-DOE`/`CD-TICK`/`SMOKE-CAMPAIGN` removed — Hermes no
longer exists. Every block below is now Artemis-only.
| Block | Immutable | Justification |
|-------|-----------|---------------|
| 4060 | No | `SETUP-HERMES`/`SETUP-ARTEMIS`/`SETUP-VMS`: `BIRTH` + remote `LOAD-DOE` VM-EXEC per child VM. Not immutable: setup sequence may grow |
| 4061 | No | `PHASE1-DOE`: real per-VM `DOE-WORK` baseline touches. Historical note: dead `CD-WORK` (index-dispatch, deleted VM-HERA/HERMES/ARTEMIS constants) removed here 2026-07-08 |
| 4062 | No | `CD-TICK`/`CD-DOE`: one real fleet-touch pass (`VM-EXEC "DOE-WORK"` on Hermes+Artemis), looped N times. `CAMPAIGN-STATUS`: `VM-PHYSICS-STATUS` + `VM-CONSERVED?` check. Not immutable: workload/status content may evolve |
| 4060 | No | `SETUP-ARTEMIS`/`SETUP-VMS`: `BIRTH` + remote `LOAD-DOE` VM-EXEC on Artemis. Not immutable: setup sequence may grow |
| 4061 | No | `PHASE1-DOE`: real per-VM `DOE-WORK` baseline touch, Artemis only. Historical note: dead `CD-WORK` (index-dispatch, deleted VM-HERA/HERMES/ARTEMIS constants) removed here 2026-07-08 |
| 4062 | No | `CD-TICK`/`CD-DOE`: one real fleet-touch pass (`VM-EXEC "DOE-WORK"` on Artemis), looped N times. `CAMPAIGN-STATUS`: `VM-PHYSICS-STATUS` + `VM-CONSERVED?` check. Not immutable: workload/status content may evolve |
| 4063 | No | `CAMPAIGN`: full campaign — baselines then 30 real fleet touches |
| 4064 | No | `SMOKE-CAMPAIGN`: 1-rep seed-1959 smoke test + 16 real fleet touches |
| 4065 | No | `THREE-VM-CAMPAIGN`: loads `doe.4th` locally on Hera, then 48 real fleet touches, all three VMs as Compudynamics peers |
### `hermes/init.4th` — Hermes VM (messenger)
### `hermes/init.4th` — DELETED 2026-09-22 (formerly Hermes VM, the messenger)
Blocks: **4100–4109, 4114–4121, 4142–4153**
**Deleted, FABRIC-3.6.md Phase 4 Stage E (task 4.1).** Formerly blocks
**4100–4109, 4114–4121, 4142–4153** (gaps 4110–4113 and 4122–4141 were always
Artemis's, hard-locked, never Hermes's — see the `artemis/init.4th` entry below,
unaffected by this deletion). Every FORTH-owned message type this file's
| 4102 | Yes | MSG-INIT-FREE + CH-INIT-FREE. Immutable: free-list init is called once from CD-INIT; structure change breaks allocator |
| 4103 | Yes | MBR-INIT-FREE + MSG-ALLOC + MSG-FREE-NODE. Immutable: allocator ABI; MSG-ALLOC is called by every MSG-SEND |
| 4104 | Yes | CH-ALLOC + CH-FREE-NODE + MBR-ALLOC + MBR-FREE-NODE. Immutable: same allocator ABI constraint |
| 4105 | Yes | Message field accessors (MSG-TYPE@/! through MSG-SEQ@/!). Immutable: field offsets are load-bearing; changing breaks every accessor caller |
| 4106 | Yes | Channel + member accessors (CH-ID@/! through MBR-VM@). Immutable: same field offset constraint |
| 4107 | No | VARIABLE MSG-LAST-MSG + IDX>NAME + MSG-DELIVER. Not fully immutable: MSG-DELIVER may evolve for async model. IDX>NAME now a table lookup over VM-NAME-ADDRS/LENS (see 4142) |
| 4108 | No | MSG-SEND. Not immutable: send protocol may evolve |
| 4109 | No | MSG-REAP. Ordering bug fixed (`8ca0cda2`) — type cleared before free-node prepend |
| — | — | 4110–4113: ARTEMIS. NEVER TOUCH. |
| 4114 | No | VARIABLE CH-SCAN + CH-COOL-ALL + CH-TOTAL-HEAT. Not immutable: channel cooling may evolve |
| 4115 | No | CH-REAP-SAFE. Not immutable: channel reaping logic may evolve |
| 4116 | No | VARIABLE COMMON-CH + COMMON-INIT + HERMES-TICK. Not immutable: HERMES-TICK drives the event loop; may grow |
| 4117 | No | EVENT-EMIT + EVENT-WAIT + EVENT-DRAIN (backward compat). Not immutable: EVENT-WAIT is a diagnostic peek only per HERMES.md |
| — | — | 4118: reserved, currently empty (former HERA-NOTIFY-SPAWN/KILL, removed `9323f776` — notification is automatic via `vm_physics_init`/`retire`) |
| 4119 | No | CH-MINT-ID + CH-REQUEST. Not immutable: channel negotiation protocol may expand (multi-party invite deferred) |
| 4120 | No | CD-INIT. Not immutable: init sequence may grow |
| 4121 | No | MSG-ACK-LAST + MSG-NACK-LAST. Not immutable: NACK-requeue (reduced heat, retry) deferred; block may need extension |
| 4142 | No | VM name routing table: VM-NAME-ADDRS/LENS + VM-NAME-REG + VM-NAMES-INIT. Not immutable: table-driven, grows via VM-MAX |
| 4143 | Yes | MSG-CH@/! + MSG-ORIG-TYPE@/! + MSG-NACKED. Immutable: field offset constraint, same as 4105 |
| 4144 | No | MSG-SEND. Not immutable: send protocol may evolve |
| 4145 | No | MSG-TOTAL-HEAT + MSG-REDELIVER-NACKED. Not immutable: cooling/redelivery logic may evolve |
| 4146 | No | MSG-DELIVER-ALL. Not immutable: delivery logic may evolve |
| 4147 | No | HERMES-K + WELCOME. Not immutable: may grow |
| 4148 | No | CH-ACCEPT + CH-CONFIRM + CH-CLOSE. Not immutable: channel negotiation protocol may expand |
| 4149 | No | HERMES-STATUS (MSG-USED + CH-USED). Not immutable: status may expand |
| 4150 | No | Member management: MBR-NEXT!/VM! + CH-ADD-MBR + HERMES-MSG-TEST. Not immutable: may grow |
| 4151 | No | Phase 2 real multi-member broadcast: MSG-BROADCAST + BC-* variables. Not immutable: broadcast protocol may expand |
| 4152 | No | Phase 2 COMMON-CH membership + test: BCAST-GOT/BCAST-RECV/REGISTER-COMMON-MEMBERS/SEND-BROADCAST-TEST. Not immutable: test harness may grow |
| 4153 | No | `LOAD-DOE` ( -- ): `S" doe.4th" EXEC`. Added 2026-07-08 — `doe-campaign.4th`'s `SETUP-HERMES` calls this remotely so Hermes gets `DOE-WORK`; was missing entirely before this pass, so `doe-campaign.4th` could never have worked even pre-dating the VM-fleet-physics redesign. Not immutable: trivial wrapper |
**2026-07-05 collision fix:** blocks 4142–4150 were previously numbered
4122–4131 (interleaved with the blocks below them in this list), directly
colliding with Artemis's real block range. The collision was introduced
`fca7b09a` (Jul 1) when the VM-name routing table was added without
checking MANIFEST.md, and went undetected for 4 days because `mkcapsule`'s
build path doesn't run conflict detection (only `--manifest` does). Found
via `mkcapsule --manifest` while syncing this document; renumbered into
genuinely free space and reverified with a clean amd64 TRIPOD-TEST boot
`PASS: E2E msg flow` — Hermes's kill/rebirth soak test reproduced an
identical `dict_hash` before and after, confirming the reload path is now
clean). amd64-only per the rev-f iteration rule; three-arch not yet run for
this fix. See Conflict Register entry C5.
**2026-07-05 collision fix (historical, still relevant to Conflict Register
C5 below):** blocks 4142–4150 were previously numbered 4122–4131, directly
colliding with Artemis's real block range, introduced `fca7b09a` (Jul 1),
found and resolved via `mkcapsule --manifest`. See Conflict Register entry C5.
### `artemis/init.4th` — Artemis VM (flat pool disk manager v2)
@@ -439,14 +408,19 @@ Hermes (see above). The table below reflects the actual current footprint.
| 4851 | No | ART-PING ( -- ): cheap O(1) touch target for Phase 3 fleet DoE workload, unlike ART-TICK/ART-STATUS which scan all ART-DATA-BLKS. Not immutable: may extend |
| 4852 | No | `LOAD-DOE` ( -- ): `S" doe.4th" EXEC`. Added 2026-07-08 — same missing-word fix as `hermes/init.4th` 4153; `doe-campaign.4th`'s `SETUP-ARTEMIS` calls this remotely. Not immutable: trivial wrapper |
### `process.4th` — VM process management
### `hestia/init.4th` — Hestia VM (third Tripod leg, bind point)
Blocks: **4300–4301**
Blocks: **4986–4988** (allocated 4986–4996, FABRIC-3.6.md task 1.1; 3 of the 11 used so far)
Added 2026-09-19 (FABRIC-3.6.md tasks 1.2/1.4/1.6/1.7, Tripod/kernel reshuffle — see
FABRIC-3.5.md §II/§IV). Reconstitutes the Tripod as Hera/Artemis/Hestia. Birthed (task 1.4)
and owns the drawing fabric (`fabric.4th`/`font.4th`, tasks 1.6/1.7 — see block 4988).
| Block | Immutable | Justification |
|-------|-----------|---------------|
| 4300 | No | Event code constants + SPAWN + PAUSE lifecycle operators. Not immutable: process model may evolve |
| 4301 | No | RESUME + KILL-VM. Not immutable: process model may evolve |
| 4986 | No | `WELCOME` banner word + call. Not immutable: message text may change |
| 4988 | No | `S" fabric.4th" EXEC` + `S" font.4th" EXEC` — moved here from `init.4th` (Hera), tasks 1.6/1.7, **merged into one commit rather than done separately as the punchlist originally split them**: `font.4th` calls `G-LINE`/`G-ELLIPSE`, which are `fabric.4th`'s own words, so moving one without the other strands the dependency in whichever VM keeps only half — confirmed live, moving `fabric.4th` alone floods Hera's boot with `UNKNOWN WORD: 'G-LINE'`. Order preserved (`fabric.4th` before `font.4th`), matching `init.4th`'s own prior load order. Not immutable: may grow if more of the drawing/font surface relocates here |
| 4987 | No | **Rewritten 2026-09-22, FABRIC-3.6.md Phase 4 Stage E.** Originally joined `COMMON-CH` on slot 11 (Hera/Hermes/Artemis held 0/1/2, identities held 3–10, `messaging.4th:78-85`) — that whole `COMMON-CH` concept no longer exists, `messaging.4th` is deleted. Now just `S" lib.4th" EXEC` + `LOG-INFO" Hestia: ready"` + `STARTUP-BANNER`. Not immutable: startup sequence may grow |
### Deleted capsules (historical)
@@ -458,6 +432,31 @@ generalized VM fleet physics mechanism (`capsule_vm_physics.c`/`.h`,
and `docs/working/archive/session-logs/2026-07-05-worklog.md`. Their
former ranges are now UNASSIGNED (see Namespace Map).
`common/msg.4th` (formerly 4055) and `process.4th` (formerly 4300–4301)
were deleted 2026-09-19 (FABRIC-3.6.md tasks 0.2/0.3, part of the
Tripod/kernel reshuffle's Category A surgical strip — see FABRIC-3.5.md
§XXII). Both were confirmed dead by all three of §XXII.2's reachability
routes: zero `EXEC` sites in any boot-loaded capsule, and zero callers
anywhere of their exported words (`HERMES-ACK`/`HERMES-NACK` for the
former; `SPAWN`/`PAUSE`/`RESUME`/`KILL-VM` for the latter).
`common/msg.4th`'s block-4055 entry had claimed it was an immutable ABI
"every messaging VM loads at birth" — `FABRIC-2.md:2773` had already
flagged that claim as stale before this correction. `process.4th`'s
removal also took `messaging.4th`'s `EVENT-EMIT`/`EVENT-WAIT`/
`EVENT-DRAIN` (former block 5030) with it, its only live caller. Their
former ranges are now UNASSIGNED (see Namespace Map).
`hermes/init.4th` (formerly 4100–4109, 4114–4121, 4142–4153) and
`common/messaging.4th` were deleted 2026-09-22 (FABRIC-3.6.md Phase 4 Stage E,
tasks 4.1/4.3), the final step of the Tripod/kernel reshuffle — the Hermes VM
itself and its FORTH messaging layer, superseded by kernel-Hermes
(`src/starkernel/vm/kernel_hermes.c`) after every live message type was cut
over in Phase 3 (see the `hermes/init.4th` entry above for the full account).
Their former ranges are now UNASSIGNED (see Namespace Map). This is the same
"cut over first, delete after, one commit per Category B item, verified on all
three architectures" discipline `compudynamics.4th`/`fleet-k.4th` and
`common/msg.4th`/`process.4th` were deleted under above.
---
## Unassigned Ranges
@@ -476,14 +475,16 @@ former ranges are now UNASSIGNED (see Namespace Map).
| 4853+ | OPEN | Future capsules — claim here first |
| 4986–4996 | CLAIMED | `hestia/init.4th`, allocated 2026-09-19 (FABRIC-3.6.md task 1.1, Tripod/kernel reshuffle) — file not yet created (task 1.2). Sits between `font.4th`'s last block (4985) and the console proxy's hardcoded `Block 4997` (`capsule_console.c:27-29`) — chosen specifically to avoid 4997, per §XVIII.4 |
| 4853–4985, 4998+ | OPEN | Future capsules — claim here first (excludes 4900–4985, already `fabric.4th`/`font.4th`, and 4986–4996, now claimed above) |
---
@@ -536,3 +537,15 @@ before this doc pass. Not re-verified this pass: whether every other
still-unflagged row elsewhere in this document matches source exactly —
this was a targeted correction of the rows touched by recent work plus
`doe.4th`/`ACL.4th`, not a full line-by-line re-audit of all ~250 blocks.*
*Doc-debt pass, 2026-09-22 — MANIFEST.md updated to ride the Tripod/kernel
reshuffle's Category B strip (FABRIC-3.6.md Phase 4 Stage E, task 5.2 close-out
sweep, §XXII.5). `hermes/init.4th`'s block-by-block table replaced with a
deletion note (git history retains the detail); `common/messaging.4th`'s
deletion recorded in "Deleted capsules"; `init.4th` block 2049,
`doe-campaign.4th` blocks 4060–4062, and `hestia/init.4th` block 4987 updated
to match the post-strip live files (verified by direct read, not by this
document's own prior claims, same discipline as every other pass here).
Former Hermes range (4100–4153, excluding Artemis's hard-locked 4110–4113/
4122–4141) marked UNASSIGNED. Not re-verified this pass: the ~230 other rows
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.