dd23c3aa45c41f8e32967c45cf2450e19e05964c
59
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
dd23c3aa45 |
Stage I: strip dead code -- pump drain loop, SWITCH-MARK-WORK, global interpret depth, native_switch_back_to (FABRIC-3.8 §4 row I)
Each shown dead three ways (FABRIC-3.5 §XXII): the pump loop by its condition, by construction (birth cap == switch table capacity, every birth registers) and at run time (probe silent through boot, G interleave, an 8-worker DoE trial and the relay); SWITCH-MARK-WORK by callers, capsules (source and baked), experiments/tools/logs; the depth counter and back_to field by having no readers. Kept: Hera's own pump drain (last timer-paced delivery, Hera only) and the always-0 readiness CSV columns (a DoE schema call). Acceptance on amd64/ aarch64/riscv64: G interleave unchanged, cfg-0 trial fail=0 2/2 dead, relay 3, idle growth 0; dict_hash set changed by the removed word (word_count 531) and identical across ISAs. FABRIC-3.8 stages A-I done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
81def7b4a5 |
Stage G: SEND, and two workers interleave; decision compares work heat (FABRIC-3.8 §3.8, §3.13.7)
SEND ( paddr plen name-c name-u -- ok? ): general kernel-Hermes send to a named VM, from = calling VM, one block max (refused, not truncated), every VM. New SK_HERMES_MSG_TYPE_SEND (30). Ruling C-4 (beta), Captain Bob: the switch decision compares work heat -- the heat each VM pulled in by its own per-beat charges -- not absolute heat, so Hera's structural endowment no longer makes her hottest. Before it, the two-worker test ran serial (AAAA...BBBB); after it, byte-identical on amd64/aarch64/riscv64: AAABBBBBBBBAAAAAAAABBBBBAAAAABBBBBAAAAABBBBBAAAAABBBBBAAAAABBBBBAAAAABBBBBAAAABB (40 A, 40 B, 16 runs; ordinary words only, nothing yields). Acceptance: relay 3, idle growth 0; dict_hash set changed by the new word (word_count 533) and identical across ISAs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
06bcab504d |
Stage C-b: compudynamic switch decision, idle point, timer path removed (FABRIC-3.8 §3.13.3-§3.13.5)
Who runs next is now sk_vm_switch_decide(): among participants with work (pending kernel-Hermes mail, parked mid-task, or Hera with console input), the coolest by VM-physics heat; ties with the incumbent keep the incumbent, ties among candidates go to the longest waiting (unique wait stamps). No scheduler state, no timer: heartbeat_tick() no longer touches switching, readiness/threshold/has_work/take_pending removed. Decided at owned word checkpoints (colon words and, new, between top-level words) after a beat or new mail, at the trampoline between messages, and at Hera's idle point in the console poll loops. The idle pump skips participants (G3 closed for them): the WIREBIND relay is delivered by a real switch to rajames~user. Probe (restored away): relay via switch Hera->rajames~user->Hera on all three ISAs, parked saved_sp inside own stack, walk-order disagreements 0 over tens of millions of decisions, 0 bytes idle log growth. Acceptance: dict_hash unchanged, relay 3, no faults. Rulings: C-3, C-5 as proposed; C-4 taken as (alpha), consequence recorded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
3e201c82a5 |
Stage E: Category B strip -- remove Hermes, messaging.4th, old routing (FABRIC-3.6.md task 4.1-4.4)
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> |
||
|
|
cab9b5a31f |
Stage D: ELEVATE-REQUEST real cutover -- FABRIC-3.6.md task 3.10
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> |
||
|
|
d4f8a568ee |
Silence routine diagnostic noise; fix a real CRLF double-submit bug
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>
|
||
|
|
5c07745c18 |
Fix SkHermesMessage payload-aliasing defect (task 3.8 findings log)
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> |
||
|
|
21734b1a52 |
Console prompt: identity/machine both sides once redirected off Hera
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> |
||
|
|
3975dc61cd |
Stage D: CONSOLE-CMD-EVENT real cutover -- FABRIC-3.6.md task 3.9
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
|
||
|
|
bbfd9103f4 |
Stage C: cut over BLK-ATTACH-EVENT alone -- FABRIC-3.6.md task 3.8
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> |
||
|
|
5417ffb2bf |
Stage D batch 1: fix silent error sites in repl.c and inference_words.c (FABRIC-3.md §XXXII.6)
repl.c's sk_word_blk_attach_ack() and inference_words.c's array_ptr() helper + infer_word_run()'s allocation guard now log a diagnostic via log_message(LOG_ERROR, ...) before setting vm->error, matching defer_words.c's own gold-standard pattern (§XXXII.3) -- these are internal/background conditions (a malformed message-callback, a bad array reference or allocation failure), not interactive usage mistakes, so the fix keeps the fault and reports it rather than dropping it like USE's own fix did. Batched together (4 sites total, smaller combined than the next file) rather than two separate acceptance cycles for negligible size. Three-arch clean qemu acceptance passed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K |
||
|
|
5c5896fbc1 |
Stage A: fix USE's silent stack-underflow/bad-address guards, and the Hera fault-scoping gap they exposed (FABRIC-3.md §XXXII.1)
mama_word_use()'s two silent vm->error=1 guards (dsp<1 stack underflow, NULL from vm_ptr()) now print a diagnostic and return, matching the function's other five guards. Live verification of that fix alone surfaced a bigger problem: with the guard no longer silent, the REPL proceeds to interpret the leftover token as an unrecognized word, which independently sets vm->error, and sk_repl_step()/sk_repl_run()'s Hera-branch still hard-halted on that. Investigated kernel_main.c's boot/capsule-load paths directly: they already catch and clear mama->error entirely separately, before sk_repl_run() is ever entered -- so the "no fallthrough surface" halt in these two REPL functions was never protecting a boot-time fault, only an ordinary interactive REPL-turn one. Both functions now recover unconditionally on any VM's error, Hera included, matching how a redirected (WIREBIND/USE'd) identity's session already recovered. Removed the now-fully-unused sk_fault_handler(). Verified live, amd64: USE rajames at Zuse's own console prints the new diagnostic, then "VM fault -- session recovered, resuming", console stays interactive afterward. Three-arch clean qemu acceptance passed before and after the Hera-fault-scoping change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K |
||
|
|
d9da82b065 |
Stage 4 increments 2+3: WIREBIND VMs as switch-signal participants + mark-and-defer tombstone reap (FABRIC-3.md §XXVIII Stage 4)
Increment 2: WIREBIND user VMs (the ones that actually run FORTH work; console VMs are pure REPL proxies and never participate) register as Stage 3 switch-signal participants at attach, unregister at teardown. Slot table bumped 8 -> 16, matching messaging.4th's own VM-MAX -- a real, already-agreed ceiling, not an invented number. Added sk_vm_switch_signal_unregister() (compaction-based; Tripod VMs never needed removal, WIREBIND VMs cycle constantly and would otherwise exhaust the bounded table). Increment 3: implements the plan's own ratified option (A) for the async-detach UAF risk -- mark-and-defer via a new pending_reap flag on VMRegistryEntry, deliberately not a new VMState (capsule_vm_kill() already treats VM_STATE_DEAD as idempotent success, which would silently swallow a reap attempt; SWITCHED_OUT still accurately describes a tombstoned VM until the moment it's actually freed). unclean_detach() sets it when capsule_vm_kill() refuses a SWITCHED_OUT target; the Stage 3 checkpoint (vm_core.c) checks it before ever attempting to resume a pending switch target, and calls the new capsule_vm_force_reap() instead -- the one caller allowed to bypass capsule_vm_kill()'s own refusal, because it runs at the exact safe cooperative point the switcher itself controls. A new idle-tick sweep cleans up the WIREBIND live-table entry once the reap has actually happened. Verified clean on all 3 architectures (baseline regression -- no WIREBIND attach happens in a plain boot). The reap mechanism's own correctness under a genuinely parked context is verified separately, next, via a temporary deterministic probe. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K |
||
|
|
2a30212bd3 |
Real per-VM log persistence: source attribution + ACL pin (FABRIC-3.md §XXVII)
Wires the previously-unused vm_log_attributed_vm() into LOG-APPEND's kernel primitive so persisted log records carry a trustworthy source (the real attributed VM's registry name, or "HADES" pseudo-source) instead of a caller-supplied, trivially forgeable string. Drops src-addr/src-u from LOG-APPEND's stack signature accordingly. Pins LOG-APPEND via bare ACL-PIN in Artemis's own init.4th, matching BIRTH/CAPSULE-BIRTH's precedent for a privileged word that can't reach the shared, host-portable ACL.4th. Also fixes two console-banner nitpicks: a mis-rendering em dash (U+2014) in the boot banner, and drops "Emergency" from the CLI banner text. Doc corrections to artemis_sig.h/zuse_eligibility_list.h reconciling the three fixed devblock ranges now in play. LOG-FLUSH (the intended normal entry point) and level-aware log eviction remain open, flagged not fixed. Re-verified clean boot to ok> on all 3 architectures after every change. riscv64 showed one new, unrelated virtio_blk write-timeout anomaly during Artemis's early physics self-test (self-recovered, boot unaffected, sector doesn't map to the log region) -- flagged, not investigated. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UNhH1mhi52i6Qihh7ZV5S |
||
|
|
61755fde78 |
Artemis genesis stamp: fix a BAM-corrupting offset before it ever ran (FABRIC-3.md §XXVI follow-on, Step 3)
Step 3: one-time artemis_sig_t genesis stamp, written once
kernel_main.c's virtio-blk path confirms Artemis's own disk, so the disk
image is later recognizable generically (repl.c's idle-loop USB-MSC scan,
built in the prior commit) regardless of which bus found it.
Correction made before this ever touched the real disk: the signature's
first design (committed in
|
||
|
|
29b6789860 |
Artemis bus-agnostic discovery: signature format + idle-loop generalization (FABRIC-3.md §XXVI follow-on)
Step 1: new artemis_sig_t header format (magic 'ARTM', sibling to homeblocks_sig_t, distinct so a generic scan can tell Artemis's own disk apart from an identity thumbdrive by content alone) -- artemis_sig.h/.c, wired into Makefile.starkernel. Step 2: sk_repl_idle()'s existing per-USB-MSC-slot attach handling (the pattern WIREBIND already uses for identity thumbdrives) now also checks for the ARTM signature whenever a device's home-blocks check comes back BLANK. On a match, once Artemis's own storage-attach round-trip (HERA-BLK-ATTACH-REQ/BLK-ATTACH-ACK) confirms success, capsule_zuse_boot_load_root_pubkey() runs -- the same call kernel_main.c's synchronous QEMU-only virtio-blk path already makes, now reachable without a hardcoded PCI vendor/device scan. That function is already idempotent (no-op once zuse_root_pubkey_known is set), so no boot restructuring was needed despite the initial concern that deferring Artemis discovery to the idle loop would require one. Verified: clean build + QEMU boot to [zuse@Hera] ok> on all three architectures, zero regression to the existing virtio-blk/Zuse-thumbdrive attach path. Steps 3 (genesis-stamping onto disk/artemis.img) and 4 (growable production log-persistence region) not yet started. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo |
||
|
|
a8b16d41da |
Unify console prompt to [user@VM]; fix real personality-block truncation; correct §XXV's wrong lockdown conclusion (FABRIC-3.md §XXVI)
Investigating the std79 lockdown finding from FABRIC-3.md §XXV led to a real discovery: WIREBIND births TWO VMs per identity, a console proxy under the plain username and the actual restricted identity under <username>~user (capsule_wirebind.c). Every test in §XXV targeted the console proxy, which was never locked down at all. Retested against the correct target (rajames~user): the lockdown works exactly as designed. §XXV's "lockdown never engages" conclusion was wrong -- corrected here, not deleted, since the mistake and how it was caught are worth keeping (see the new feedback memory: confirm which specific VM a name resolves to before concluding anything, when a subsystem is known to birth more than one VM per identity). Two real, separate things found along the way are kept regardless of that correction: - capsule_runcap.c: the reserved personality devblock was read in full (mostly zero-padding after a short ~200-byte string) with no terminator, producing "WARN: block 4998 exceeds 1KB, truncating" on every std79-locked identity's birth, universal, since at least 2026-09-10. Fixed by trimming to the first NUL byte actually found -- real, but harmless to execution (real content sat in the truncated block's surviving head); it mattered for capsule_id/ content_hash being computed over padding instead of real content. - console.h/console.c/repl.c: unified the prompt from a separately- computed "[VMName] (user)" into a single "[user@VMName]" line prefix -- exactly the ambiguity that caused the original misdiagnosis (the prompt showed only the WIREBIND username, identical whether USE had targeted the console proxy or the real ~user identity). Implemented as a registered callback (console_set_user_prefix_provider()) rather than console.c calling into WIREBIND/session logic directly, since console.c is a clean HAL module with no prior dependency on capsule-level subsystems. Verified: clean build on all 3 architectures, zero new warnings, identical dict_hash/capsule_hash to every prior boot this session (console/prompt-only change). Full 9-identity messaging campaign re-run end to end: 202s, zero faults, all 8 identities at 99/99 tokens, zero regression. Also surfaced, not yet acted on: the full campaign's own console tags now visibly show which VM each identity's tests actually reached ([zuse@rajames], not [zuse@rajames~user]) -- messaging.4th's VM-NAMES-INIT registers identities by plain username, so std79-doe. fth's turn-attractor has been dispatching to each identity's console proxy, not the actual locked-down identity, since the messaging rewrite. Flagged for a deliberate decision, not investigated further. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo |
||
|
|
71bcb72a59 |
Fix O(N) idle-loop messaging pump; full 3x9x3 turn-attractor campaign clean on all 3 architectures (FABRIC-3.md §XXII)
sk_repl_idle()'s messaging pump (repl.c) walked the entire live-VM registry every idle beat (~1Hz) and dispatched a full VM-EXEC "MSG-TICK" -- a dictionary lookup plus a 32-slot arena scan -- into every live VM, every tick, unconditionally, forever. Fine at Tripod's original 3-VM scale; a full 9-identity turn-attractor campaign exposed it as a genuine wall on riscv64 specifically (its TCG makes each dispatch cost more): the same campaign that completed in 194s/ 339s on amd64/aarch64 never finished on riscv64 at 9 VMs across three attempts, while 8 VMs there was fine in 159s. Ruled out capacity explanations before touching anything: bumping riscv64's QEMU RAM 1024->4096 changed nothing (reverted), and a live STADIUM-RES@/MSG-STATUS probe with all 9 VMs attached showed no depletion. Host memory pressure was also ruled out directly (one background task did get OOM-killed once during the investigation, but the identical stall reproduced again with 9.2GB free). The real mistake was three premature kills under 4 minutes with no way to tell "slow" from "stuck" from outside the guest -- fixed by having run_doe_batch.sh sample the qemu process's own /proc/<pid>/stat utime every 60s; with that signal, riscv64 at 9 VMs was unambiguously alive (climbing utime, no hang), just disproportionately slow going from 8 VMs (159s) to 9 (600s+ and climbing). This was never really a riscv64-only bug: an O(N) per-second walk over the full VM population doesn't scale to the hundreds of VMs this fleet is headed toward, on any architecture -- riscv64 just made it visible first, at N=9, because its per-dispatch cost is highest. Fixed by round-robin batching: the pump now dispatches to at most SK_MSG_PUMP_BATCH (4) live VMs per idle beat via a persistent cursor that resumes where the previous beat left off, instead of all of them every time. Bounds both the scan and dispatch cost to O(K) regardless of total VM count; any single VM's queue now drains roughly every ceil(N/K) beats instead of every beat, still bounded and still matching the pump's own existing best-effort contract. No new C primitives, no messaging/Stadium changes. Verified: clean build on all 3 architectures, then the full 3x9x3 campaign re-run on all 3 (not just riscv64) per the standing rule that a defect repair requires a clean re-run everywhere before anything counts as closed: amd64 198s 0 faults 99/99 tokens x8 656/656 K-conserved aarch64 339s 0 faults 99/99 tokens x8 659/659 K-conserved riscv64 178s 0 faults 99/99 tokens x8 659/659 K-conserved riscv64 went from "never completes" to faster than aarch64, same campaign, same seed, same identity set. No regression on amd64/ aarch64. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo |
||
|
|
7edccd2c35 |
Hera can't message: root-caused and fixed by symmetry (FABRIC-3.md §XX)
Hera never loaded common:messaging.4th, unlike every other VM in the fleet. The standing belief was this was deliberate, to avoid moving her dict_hash off baseline. Checked live instead of assumed: loading messaging.4th into her dictionary silently dropped every colon- definition referencing one of 8 STADIUM-* primitives that register_child_vm_words() gives every other VM but register_mama_forth_words() never gave her -- a missing-primitive gap, not a designed privilege boundary. Confirmed mama_word_birth is genuinely VM-agnostic and SPAWN-EVENT is an unwired placeholder before proposing the fix. Fix: register the same 8 STADIUM-* primitives for Hera, load messaging.4th from init.4th the same way Hermes/Artemis/console/mint already do, and give her own idle-loop context a direct MSG-TICK call (not VM-EXEC, which would hit the same reentrancy class the existing per-other-VM pump loop already guards against) so her own queued messages actually drain. Her dictionary is now a proper superset of every child VM's, plus her remaining extra privileges -- not structurally different from any other VM, just additionally privileged. Verified: dict_hash identical across amd64/aarch64/riscv64 (0xc8f4b09e36f4fc4a), Hermes/Artemis dict_hashes unchanged and still cross-arch identical, all three boot clean to zuse)ok> with zero UNKNOWN WORD faults, mkcapsule --lint clean. Unblocks rewriting the turn-attractor (FABRIC-3.md §XIX) to coordinate via real MSG-SEND/MSG-TICK instead of blocking VM-EXEC. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo |
||
|
|
9142dda2d6 |
Fix use-after-free in sk_repl_idle()'s idle-tick VM resolution (FABRIC-3.md §XIII)
Root-caused a heap-corruption bug that reliably failed WIREBIND identity attach on the third attach/detach cycle in one boot. sk_console_readline() and sk_console_getkey() captured `active_vm` once from their caller and kept passing that same (possibly long-stale) pointer to sk_repl_idle() on every idle tick serviced while blocked waiting for input. If the VM it pointed at was killed (WIREBIND detach) mid-block, the existing bailout only checked a generic "is anyone attached" boolean -- masked as soon as a different identity attached next -- so blk_vm_flush_all() kept writing into a freed VM struct sitting on kmalloc's own free list, corrupting the free list's linked-list metadata itself. Both idle branches now re-resolve the live active VM fresh from g_repl_active_vm on every tick, matching the dispatch-side fix already made for the sibling bug in §XII.3. Verified: rebuilt amd64, reran the exact three-cycle repro that reliably corrupted the heap before the fix -- free-list census stayed stable through the same idle window that previously collapsed to zero. All three architectures (amd64/aarch64/riscv64) boot clean to the zuse)ok> prompt. kmalloc_debug_census()/kmalloc_debug_census_bytes() kept as permanent diagnostic infrastructure; every other temporary probe added during the investigation was reverted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo |
||
|
|
70421bdd43 |
Fix real WIREBIND crash: stale active-VM pointer dispatched after blocking read (FABRIC-3.md §XII.3)
The interpreter_enabled guard added in the previous commit (
|
||
|
|
d6661b5eed |
Scope VM fault halt to the faulting identity's own session (FABRIC-3.md §XI.4)
A standalone WIREBIND identity (no Zuse, USE'd in directly) hitting an ACL-denied word halted the entire machine -- Hera, Hermes, Artemis, all of it -- instead of just that identity's own session. sk_fault_handler() was being called unconditionally on whichever VM's ->error was set, with no distinction between Hera's own root session (where "no fallthrough surface" is the correct, deliberate fail-closed behavior) and a USE'd-in guest identity (which should recover and resume at its own prompt instead of taking the fleet down with it). Both call sites (sk_repl_step, sk_repl_run) now compare the faulting VM against Hera before deciding: Hera's own session still halts by design; any other VM prints a recovery message, clears its fault state, and continues. Also: mint identities 01-06 with the same FORTH-79/83 restricted personality identity 00 already had, verified via the fixed fault scoping above (which this verification pass surfaced). Verified live on amd64 (both the Hera-halts and identity-recovers branches); three-arch clean qemu acceptance passed (riscv64's first attempt hit an unrelated virtio_blk I/O timeout hang, a known QEMU/TCG flake -- a clean retry booted normally). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Ec88YKxxhZGG1RNnune78 |
||
|
|
1a263555e2 |
Fix pathological migration scan that stalled WIREBIND identity attach
Root cause (found by a fresh subagent after an extended live-debugging investigation into "identity 00 attaches slowly/stalls when Zuse never attached first this boot"): blk_migration_idle_check() was generalized earlier today to walk every attached device slot uniformly instead of hardcoding first_disk_slot() (Artemis's own disk). But its per-slot scan can only early-exit once it finds a devblock that is BOTH "hot" (claimed and worn) AND "free" -- and a just-attached, never-claimed USB identity drive can never satisfy the "hot" half by design (claiming only ever happens via blk_firsttouch_claim(), which only ever targets first_disk_slot()). So the scan ran to completion -- the drive's entire ~16,000 devblocks, mostly cache misses over slow emulated USB/BOT -- every single idle tick, forever, blocking sk_repl_idle() (and therefore the console and the storage-attach message round-trip) each time. Fix, in src/block_subsystem.c: a new has_ever_claimed flag on blk_dev_slot_t (set in blk_set_meta(), the single choke point every BLK_FLAG_CLAIMED transition passes through) skips the scan entirely, O(1), for any slot nothing has ever claimed -- the common case for a freshly-attached drive. A new migration_scan_lbn resume cursor bounds *any* slot's per-tick cost to MIGRATION_SCAN_BUDGET (256) devblocks examined, picking up where the previous tick left off instead of restarting from start_lbn every time -- restores this function's own documented "coarse cadence, cheap early-exit" design intent for every device, not just the one it used to hardcode. Also along the way (kept, all real improvements, verified live): - src/starkernel/usb/xhci.c: xhci_wait_bit()/xhci_bot_wait_for_idle() had zero yield hints in their MMIO-polling loops; added arch_relax() to both (matches virtio_blk.c below) -- a tight loop of nothing but MMIO reads can starve TCG's own host-side timer injection under QEMU. - src/starkernel/virtio/virtio_blk.c: vblk_io()'s spin bound was 33M iterations with zero logging on timeout; a single real (still not fully root-caused) timeout cost 31+ minutes of CPU before this was caught. Reduced to 1M and added a log line naming the failing sector, turning a silent, effectively-unbounded stall into a fast, loud failure -- callers already tolerate BLKIO_EIO. - src/starkernel/repl.c: blk_migration_idle_check() deferred for any idle tick where a storage-attach message round-trip is still pending, to keep the two block-subsystem-touching paths from interleaving; the existing MSG-TICK pump now checks the target VM's own dictionary for MSG-TICK (vm_find_word + acl_allow) before VM-EXECing into it, instead of spamming "VM-EXEC: ERROR" every tick for a VM that doesn't have -- or isn't allowed -- the word (e.g. a FORTH-79/83-locked-down identity); fixed a real -Wunused-variable build break in the EMERGENCY_CONSOLE_ ENABLED=1 path (unused today, but needed live to reproduce this bug with no identity attached at all). - src/starkernel/capsule/capsule_mint.c: dropped the dead S" common:messaging.4th" EXEC / MSG-CD-INIT lines from MINT_RESTRICTED_PERSONALITY -- a FORTH-79/83-locked-down identity has no legitimate use for a messaging vocabulary it can never call. Status: the pathological CPU-climbing scan is confirmed fixed (verified live: CPU stays flat across an extended run instead of climbing without bound). The WIREBIND storage-attach message round-trip still does not complete promptly in the "Zuse never attached, other identity attaches first" scenario -- a separate, still-open issue in the message-delivery path itself, not the scan. Tracked as follow-on work. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Ec88YKxxhZGG1RNnune78 |
||
|
|
63b8b3bc29 |
Migrate Hera->Artemis storage-attach to a real message round-trip
Hera still polls xHCI and sig-checks attached drives, but the storage
registration step (blk_subsys_attach_device(), now wrapped as the
BLK-ATTACH primitive) moves to Artemis's own dictionary, reached via
HERA-BLK-ATTACH-REQ/BLK-ATTACH-ACK (VM-EXEC, since Hera can't load her
own messaging.4th -- see the doc comment in repl.c). Identity birth
(Zuse genesis / WIREBIND) is deferred until the ack confirms storage
actually succeeded, instead of running synchronously underneath a
storage call that might fail ("wait for ack, safer for identity data").
Caught and fixed a real bug live during acceptance testing: Artemis's
ACK-APPEND-NUM fed a single-cell value into <# #S #> (which expects a
double-cell pair), causing a stack underflow the first time
HERA-BLK-ATTACH-REQ ran. Fixed with the same `0 SWAP` convention every
other numeric-append helper in this codebase already uses.
Verified booting clean to (zuse) ok> with no VM-EXEC errors on all
three architectures (amd64/aarch64/riscv64).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Ec88YKxxhZGG1RNnune78
|
||
|
|
2c1b3cd695 |
Four bugs found live verifying the 8 identity thumbdrives (FABRIC-3.md §IX)
All found by actually running the identity workflow §VII/§VIII made possible, not by code review: 1. Zuse/WIREBIND cross-contamination on detach: capsule_zuse_boot_logout() and capsule_wirebind_unclean_detach() both had no device parameter, so an unrelated device detaching (while the real owner's own stayed attached) incorrectly tore down the wrong session. Both now compare the departing device against their own tracked one, mirroring capsule_wirebind.c's pre-existing g_wirebind_attached_dev precedent. 2. Dictionary-entry memory leak: vm_create_word()'s sf_malloc()'d DictEntry (plus a second per-entry allocation for transition_metrics) was never freed by vm_cleanup(), in both the hosted and kernel implementations. Caused a real kernel PANIC after 8-9 repeated VM birth/kill cycles in one boot. Fixed by walking vm->latest in both. 3. sf_malloc/sf_free (alloc_kernel.c) was a 4MB bump arena with a deliberate no-op free, sized on "VM born once, never killed" -- fix #2 alone didn't stop the panic because free() itself discarded the pointer regardless. Given a real free list (first-fit reuse). 4. Headless-console gate didn't re-engage after a mid-boot logout: the original fix (sk_console_mark_login(), one-way sticky) only gated the first login of the boot. Replaced with a live check (sk_console_identity_present()) re-evaluated continuously, including inside sk_console_readline()'s own blocking idle loop -- the console is normally sitting blocked there when a hot-unplug logout happens, so checking only at the top of the REPL loop wasn't enough. Also: MINT now verifies its own write (verify_mint(), capsule_mint.c) by reading back through the same check a real attach performs, rather than trusting blkio_write()'s BLK_OK alone -- logged via log_message(), not console_println(), per direct instruction. Verified live, amd64: the full 8-identity repeated attach/detach cycle that previously panicked at the same point every time now completes clean, and a full serial-log sweep found zero bare unauthenticated prompts anywhere in the run. Three-arch clean-qemu acceptance passed. Still open, not fixed here: a 3+-simultaneous-device USB enumeration failure found in a separate live test, not yet root-caused. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018EjXFo7mPXjUMjfJeuUUz4 |
||
|
|
9e81de3f43 |
xHCI/BOT driver: genuine multi-device support (FABRIC-3.md §VII)
Per-slot registry (xhci_msc_slot_t/dev->msc_slots, sized off the controller's own reported max_slots) replaces the single-device scalar fields the driver carried since Milestones 2e-2h. Boot-time port scan no longer stops at the first connected device; a connect/disconnect that arrives while the Command Ring is busy is now queued and drained instead of dropped. blkio_usb.c and repl.c's own single-device state (device descriptor buffers, blkio_dev_t, attach bookkeeping) became per-slot registries the same way. Live multi-device testing (not just compiling) surfaced a second, more severe bug outside the original plan: transfer_purpose and next_action were also single scalars shared across the whole controller. Two devices enumerating concurrently could have one's completion silently overwrite the other's still-outstanding one, permanently stalling it with no error. Fixed by moving both per-slot and, critically, reading the Transfer Event TRB's own real Slot ID field instead of trusting external bookkeeping. Verified live, all three architectures, mandatory clean-qemu acceptance: existing single-device path unchanged, and two devices attached simultaneously (amd64) both progress independently through enumeration without corrupting or stalling each other. Also in this pass (implemented and verified in earlier turns this session, committed together per direct instruction): - Headless-until-login console policy: no prompt/banner until a real identity logs in via an attached thumbdrive (WIREBIND or Zuse, neither special), reusing EMERGENCY_CONSOLE_ENABLED as the debug/recovery escape hatch (now default-off). - KILL/g_repl_active_vm dangling-pointer fix: killing the VM the console is currently USE'd onto now detaches back to Hera first, matching the existing EJECT/UNCLEAN precedent. FABRIC-3.md §VII/§VIII carry full closure notes for all three. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018EjXFo7mPXjUMjfJeuUUz4 |
||
|
|
4d4ab59189 |
Build the FIRSTTOUCH overflow trigger flagged in FABRIC-2.md §I.2
The overflow-triggered migration path (a WIREBIND-attached identity's own drive running low on space) was scoped but never built -- only the trigger-detection call site was missing, per this section's own text. - capsule_wirebind.c now tracks the attached blkio_dev* alongside the already-tracked VM id, set in try_attach() and cleared in both EJECT/UNCLEAN paths. - New capsule_wirebind_overflow_idle_check(), called once per idle tick in repl.c right alongside blk_migration_idle_check() (same cadence): reads the attached drive's free/total via blk_get_device_free_blocks(), and if free space is below a fixed 10% threshold, extends the identity's pool with a one-time blk_firsttouch_claim() of 8 additional devblocks on Artemis's system-resident device. - New blk_owner_has_claim(owner_fp) in block_subsystem.c answers the debounce question blk_firsttouch_claim()'s own doc comment had left open: a disk scan, not a RAM flag, so the already-extended answer survives reboot/reattach, matching BMAPFMT's "ownership travels with the block" model. Premise checked before building (does a WIREBIND-attached drive actually give a real free/total signal, or does it stay PROVISIONAL/raw): traced repl.c's attach sequence and confirmed blk_subsys_attach_device() runs on the same dev pointer right after WIREBIND, and a WIREBIND-eligible drive is always already STFR/v2-formatted, so the signal is real. Premise held, unlike the BAM item's overstated one. Verified with the mandatory 3-arch QEMU acceptance (identical dictionary hashes, no regression) plus a live logic test of blk_owner_has_claim(): a temporary TEST-OWNER-CLAIM word, run once via SK_CMD and reverted, confirmed it correctly detects the claiming owner and rejects an unrelated one. The low-disk-space-triggers-a-claim path itself is not verified end-to-end -- that needs a real minted WIREBIND-user thumbdrive with deliberately tiny capacity, out of scope for this pass; noted as such in the FABRIC-2.md §I.2 closure note rather than overclaimed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018EjXFo7mPXjUMjfJeuUUz4 |
||
|
|
70dc8beba4 |
FABRIC-2.md §I.9 follow-on: blinking | cursor instead of static block
Captain Bob asked for the framebuffer cursor to render as a blinking vertical bar rather than the previous static solid-block glyph. vt100_draw_cursor() (hal/vt100.c) now fills a thin bar (cell_w()/8, min 1px, full cell height) at the cursor's left edge instead of the whole cell -- an I-beam shape. vt100_erase_cursor() is unchanged (clearing the whole cell already safely covers the narrower bar). Blinking is new in repl.c: sk_console_readline()'s idle branch toggles the cursor on/off every SK_CURSOR_BLINK_INTERVAL (50 ticks, 500ms at 100Hz) via alternating console_fb_draw_cursor()/console_fb_erase_cursor() calls, independent of the heartbeat/idle-beat mechanism the §I.9 fix just touched (deliberately not reused, to avoid recoupling to that path). Runs regardless of n, so it blinks whether sitting at a bare prompt or paused mid-edit. Every deterministic draw site (initial prompt, prompt reanchor, backspace, character echo) now goes through a new helper, sk_cursor_show(), which resets the blink cycle to "on" and redraws -- typing always shows a solid cursor, never mid-blink. Verified via the mandatory foreground 3-arch QEMU acceptance boot: amd64 (logs/20260905-021054, extensive live interactive typing including multi-line : / ; word definitions and error cases, prompts stayed correctly attached throughout), aarch64 (logs/20260905-021551), riscv64 (logs/20260905-022324) -- all three reached (zuse) ok> and shut down cleanly via BYE. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var |
||
|
|
8edb95b65d |
FABRIC-2.md §I.9: fix the terminal phantom-linebreak defect
console_ensure_line_start() (hal/console.c) used to emit its newline
immediately via a path that deliberately skipped the tx-byte counter, so
that sk_repl_idle()'s unconditional per-beat call to it (repl.c, ~1s idle
heartbeat) could force a real newline the REPL's own prompt-reanchor logic
never noticed -- the prompt was never reprinted, and the next real
keystroke echoed onto the now-blank line, indistinguishable from Enter
having already been pressed at a bare prompt. Root-caused in the previous
commit (
|
||
|
|
dbaead0af2 |
xhci: route driver chatter through log_message(), silence at default log level
xhci.c and repl.c's USB attach/detach path printed every xHCI command submission, completion, and BOT transfer step unconditionally via console_println()/console_puts() -- floods the serial log on every boot, regardless of whether anyone is debugging the USB stack. Converted every "xhci:"-prefixed line to log_message() with a level chosen by what it reports, not blanket debug: - LOG_ERROR: allocation/mapping failures, timeouts, command failures, CSW signature/tag mismatches, CSW FAILED/PHASE ERROR, "not implemented" refusals, every "deferred ... setup failed" path - LOG_WARN: dropped/skipped conditions (command ring busy, tracked-port range exceeded), unrecognized media (bad version/CRC), TUR retry - LOG_DEBUG: routine progress (command submitted, succeeded, transfer completed, port connected) and expected outcomes (recognized/blank media) Default log level is LOG_INFO, so the LOG_DEBUG chatter that was the actual complaint is now silent by default and re-enabled with --log-level=debug; LOG_ERROR/LOG_WARN stay visible so real faults aren't buried. xhci_log_hex32() now routes through log_message(LOG_DEBUG, ...) instead of console_puts()/console_println() directly -- kept its own zero-padded 8-digit hex formatting rather than switching to log_message()'s %x (which has no width control), since register values lining up in the log is the reason this helper exists. console.h dropped from xhci.c, no longer used directly. Verified functionally unchanged, not just "still boots": all three architectures reach zuse)ok>, log_message()-instrumented lines are gone from the default-level log (grep -c xhci == 0 on all three, versus dozens before), and the USB thumbdrive path still works end to end -- "Zuse: identity confirmed from attached thumbdrive" appears on all three boots exactly as before. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var |
||
|
|
b031b802e3 |
Rename FABRIC series: FABRIC.md->0, FABRIC-2.md->1, FABRIC-3.md->2, FABRIC-4.md unchanged
FABRIC.md -> FABRIC-0.md FABRIC-2.md -> FABRIC-1.md FABRIC-3.md -> FABRIC-2.md (the current/living document) FABRIC-4.md unchanged (new #3 to follow separately) Every cross-reference repo-wide updated to match, including doc-comment citations inside kernel source (.c/.h) files -- done via an ordered placeholder substitution (FABRIC-3.md->placeholder2, FABRIC-2.md-> placeholder1, FABRIC.md->placeholder0, then placeholders resolved to final names) in a single pass per file to avoid double-shifting already-renamed references. One line in capsules/font.4th grew past the 64-char block-format limit as a side effect of the longer filename; shortened it and reverified with mkcapsule --lint (34/34 pass) before rebuilding. Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the foreground) after the fix; logs and DoE CSVs from this session's verification runs included per this repo's own audit-artifact convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var |
||
|
|
4018fe8b04 |
FABRIC-3.md §I.2: FIRSTTOUCH + migration state machine (blk_meta_relocate_devblock)
Closes the block-subsystem punch-list item -- built exactly to §F.11's already-decided algorithm after re-verifying it against current blk_meta_t (a 2026-09-03 re-scoping note had wrongly claimed the chain fields no longer existed; they do, untouched by BMAPFMT). blk_firsttouch_claim(): one linear scan of Artemis's own device (new blk_get_first_disk_range(), correctly bounding the scan instead of the global multi-device LBN space), scattered-chain claim via prev_block/next_block/chain_length, owner_fp stamped on every member devblock, fails outright with no partial claim. blk_meta_relocate_devblock(): the real migration primitive -- bridges the existing FORTH-block-granularity blk_subsys_relocate_block() up to devblock granularity (BLK_PACK_RATIO=3, corrected mid-design), running it 3x and transferring blk_meta_t ownership fields. The "migration state machine" turned out to be just the 2 states BLK_FLAG_MIGRATING already reserved; the real design work was the trigger. Two were scoped in conversation (overflow onto Artemis; heat-based wear leveling); heat/wear-leveling is built and wired into sk_repl_idle() via blk_meta_t.write_count. Overflow is deliberately left open, precisely scoped (needs a slot-lookup-by-device-pointer call site threaded from WIREBIND) rather than guessed at. Also flagged, not fixed: BMAPFMT's owner_fp/CLAIMED and the pre-existing BAM allocator are two parallel, unreconciled accounting systems -- FIRSTTOUCH/relocate only touch the former. Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the foreground); logs and DoE CSVs from this session's verification runs included per this repo's own audit-artifact convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var |
||
|
|
1d468a65b1 |
FABRIC-3.md §I.1: (user) console prompt segment, closing the 4.4s->4.3->1.11 chain
Extends the REPL prompt to "[VM name] (user) ok>" (e.g. "[Hera] (zuse) ok>") per the locked FABRIC.md §4.4s spec, unblocked by this session's own §I.3/§I.8 identity-tracking work. Adds capsule_wirebind_attached_username() alongside the existing tracked VMUuid, and a new sk_print_prompt() helper (repl.c) that all three prompt call sites now go through -- checks Zuse first, then a WIREBIND user, prints nothing when neither is attached. Closes 4.4s, 4.3 (console umbrella), and formally settles 1.11 (dirty-event granularity) as region-based per FABRIC-2.md's own "no independent path" ruling -- a decision closure only, not an implementation, so §17.4 (framebuffer heat/decay physics) stays open, re-scoped precisely: blocked on the dirty-region-tracking mechanism existing, not on 1.11's decision. Documents a reported-but-unreproduced terminal defect (§I.9) as a new punch-list item -- investigated the readline/keyboard-bridge code paths, found nothing conclusive, needs a live repro with a serial log before it's actionable. Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the foreground); logs and DoE CSVs from this session's verification runs included per this repo's own audit-artifact convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var |
||
|
|
60d9c2520e |
FABRIC-3.md §I.3/§I.8: WIREBIND EJECT/detach + EXPIRE re-scoped as logout
Closes §I.3 (Milestone 5 remainder): WIREBIND now tracks which VM is attached via the home-blocks USB path, and a new EJECT word plus the existing hot-unplug signal both flush/reset-console/kill through it (FABRIC-3.md §F.10). Closes §I.8 (EXPIRE/ACL), re-scoped: the original "admit the zuse session as a Stadium patron and reap on TTL" plan was invalidated a second time -- Zuse authenticates directly onto Hera, who is patron zero and permanently pinned, so there is no patron for a reap sweep to ever find. Built instead as a detach-triggered logout (capsule_zuse_boot_logout()), the same trigger EJECT/hot-unplug use for regular WIREBIND users, so neither identity is a special case. Required a companion fix: install_and_activate() used to skip re-running ACL-ZUSE-BOOT whenever the cert was already installed, which made a logout permanent for the rest of the boot; the outer re-attach gate now checks zuse_session (clears on logout) instead of zuse_cert_installed (a deliberate permanent one-way ratchet, left untouched). Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the foreground) after both steps; logs and DoE CSVs from this session's verification runs included per this repo's own audit-artifact convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var |
||
|
|
c14324f498 |
Fix framebuffer console: idle heartbeat corrupts in-progress input line
sk_repl_idle() (called every ~1s from sk_console_readline()'s idle loop) opens with console_ensure_line_start(), which unconditionally forces a newline whenever the console isn't at a line boundary -- including mid-edit, after characters have been typed and echoed but before Enter. This fired on every elapsed SK_IDLE_BEAT_INTERVAL regardless of whether sk_repl_idle() had anything to print, visually snapping the in-progress input line to a fresh empty line -- indistinguishable from Enter having been pressed. Most noticeable on the space key since it's the most common key hit during a pause. Gate the idle beat on n == 0 (no in-progress edit), mirroring the n > 0 guard the prompt reanchor logic just below already uses. Deferring the xhci/block-sync idle service by at most one more interval while a line is being edited is within its own documented "coarse cadence, cheap early-exit" tolerance. Verified: reproduced via QMP send-key against a live amd64 QEMU boot (multi-character line typed with pauses across several idle intervals stayed intact after the fix, where it previously broke on each interval). Re-verified boot to ok> on all three architectures (amd64/aarch64/riscv64) per repo acceptance policy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
58c59e87e5 |
Initial commit
Signed-off-by: Robert Allan James <robert.allan.james@gmail.com> |
||
|
|
5689c397fc |
Bug-fix sweep: repl reentrancy, virtio/blocksys bounds, identity CRCs, LOG_LINE_MAX
Code review fixes, all compile clean (hosted gcc + aarch64/riscv64 kernel flags):
- repl.c (H1): reentrancy guards on the MSG-TICK idle pump. sk_repl_idle()
now defers when Hera is mid-interpret (g_mama_interpreting) or when its
own vm_interpret is on the stack (g_idle_pump_active), so a blocking
KEY/EXPECT/QUERY inside a dispatched line can no longer re-enter the
interpreter and clobber the in-flight input buffer.
- virtio_rng.c: clamp device-returned used_len to VRNG_BUF_SIZE before the
caller's data_buf copy, closing a device-controlled OOB read.
- block_subsystem.c: first-write path now keys off created_time==0 instead
of dead magic==0 so fresh blocks get a real created_time stamp; first_free/
last_allocated fixed to absolute Forth LBNs (set in blk_compute_fresh_geometry
from slot->start_lbn, no longer the wrong physical-BAM-index values from
compute_totals_from_B); physical-bounds guard on blk_meta_zone_read/write
prevents unsigned underflow on a corrupt fence >= device size.
- capsule_zuse_boot.c / capsule_wirebind.c: identity seed validated magic ->
version -> CRC-64 (compute_crc64 over offsetof(crc)) before trusting it,
so a corrupt/format-mismatched record is refused, never loaded.
- log.h / starkernel/log.h: unused LOG_LINE_MAX 256 renamed LOG_MSG_LINE_MAX
to lift the include-order collision with vm.h's LOG_LINE_MAX 64; stale
include-order comments dropped (kernel_main.c, shim.c, capsule_birth.c).
- FABRIC-3.md: three stale-doc carry-forward items closed [x] with
|
||
|
|
09d78c99d0 |
BINDSTEP + fence-persistence fix: identity arc closed end to end
Two items, closed together per direct instruction.
1. Fence-persistence root cause, found and fixed: meta_fence_blocks
(the field gating whether blk_meta_zone_write() can succeed at all)
was carved out of what used to be unused padding in blk_volume_meta_t
-- the code's own comment already documented this. disk/artemis.img
was formatted before that field existed, so its on-disk bytes there
have always read back as 0, and the existing-volume load path
(blk_format_or_load_disk()) never recomputes it -- only a fresh
format does. Every "fence write FAILED" message this entire session,
old block-fence flow and new zuse_genesis_marker_t alike, traces to
this one thing. Patching the field in place without redoing the rest
of the geometry would risk corrupting whatever's already allocated
near the top of the volume, so the only safe fix is a genuine
reformat -- done, with explicit confirmation, since it discards
disk/artemis.img's accumulated persistent test state (regenerated
fresh at next boot regardless, not real data). Verified: fence write
now succeeds with no failure suffix, and the full mint-once ->
reboot -> reattach -> re-authenticate cycle works for the first time
this session ("Zuse: identity confirmed from attached thumbdrive",
ZUSE-SESSION? goes 0 -> -1 without re-minting).
2. BINDSTEP (FABRIC-3.md §F.9): capsule_wirebind_verify_cert() extracted
as a shared function so WIREBIND (the original attach) and BINDSTEP
(every USE of an identity-locked VM) check the exact same thing the
exact same way. mama_word_use() now re-verifies live, not cached,
whenever the target VM has VMIdentity.installed=1 -- reads whatever
drive is CURRENTLY attached, re-verifies its cert, compares owner
pubkey against the target's own installed identity, refuses on any
mismatch or no drive attached. A target with installed=0 (Hera,
Hermes, Artemis, any console VM) stays freely targetable, unchanged.
Two related bugs found and fixed live while testing BINDSTEP, not
assumed away: USE was Mama-only, so a console-paired session (§F.22)
had no way back to Hera at all -- any attempt to call USE from inside
a console VM hit "UNKNOWN WORD: USE", a genuine dead end. Per direct
instruction, USE isn't console-specific -- it should work VM-to-VM
universally, same as VM-EXEC already does -- so it's now registered in
register_child_vm_words() too. That alone wasn't enough: the console
relay (sk_repl_dispatch_line()) would have captured a bare USE call and
sent it to the paired user VM as a message instead of running it.
Fixed with a small suffix-match guard (sk_repl_line_calls_use()) --
real FORTH syntax always puts USE last, so a trailing-token check
reliably recognizes it without needing a full tokenizer, and it always
runs directly, never relayed.
Verified live end to end: USE on an unlocked VM works unconditionally;
USE escaping a console back to Hera now works; USE on an identity-
locked VM succeeds while its own drive is attached and is refused
once detached ("USE: FinT~user refused -- no matching identity
currently attached"). Clean 3-architecture regression, including
confirming disk/artemis.img's reformatted geometry loads correctly as
an already-recognized volume ("Artemis: LithosAnanke disk -- resuming")
on aarch64 and riscv64 too, not just the amd64 boot it was reformatted
under.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
|
||
|
|
6fc0ee33a9 |
WIREBIND: real thumbdrive-attach call site, no manual steps
Assembles pieces already built and individually verified this session -- CERTVERIFY (vm_identity_from_cert(), Phase A/B), RUNCAP, the console-VM + user-VM pair (§F.22) -- into one automatic sequence, replacing the RUNCAP-TEST/PAIR-TEST diagnostic words that exercised each piece by hand. New capsule_wirebind_try_attach() (capsule_wirebind.h/.c), called from sk_repl_idle() alongside capsule_zuse_boot_try_attach() on every HOMEBLOCKS_SIG_OK attach: sig->cert_offset==0 means this is Zuse's own genesis-mode drive (no cert region) -- that's already capsule_zuse_boot_try_attach()'s job, skip. Otherwise, with Zuse already authenticated this boot (nothing to verify a regular cert against otherwise), reads the cert devblock(s) and calls vm_identity_from_cert() against mama_vm's own zuse_cert_pubkey and the drive's own drive_uuid. On success: reads the drive's own user_identity_seed_t for its username, births a console VM + RUNCAP-born user VM pair (idempotent -- no-ops if that username is already live this session), installs the verified VMIdentity onto the user VM, and registers the "<username>~user" pairing sk_repl_dispatch_line() (repl.c, §F.22) looks for. Deliberately does NOT auto-USE the new console -- that stays an explicit, ACL-gated step (BINDSTEP, §F.9), not something a bare attach should trigger silently. Verified end-to-end live in QEMU, including a genuine negative case: attached disk/user1.img (signed by a different, earlier-session Zuse instance) and got a correct "cert verification FAILED -- drive refused" -- proof the check is real, not a rubber stamp. Minted a fresh identity with this boot's own Zuse, reattached, and got "WIREBIND: SamS attached and ready" printed with zero manual commands, followed by a working USE + async WELCOME relay end to end (queued, no UNKNOWN WORD, delivered and executed in the paired user VM on the next idle tick). Clean 3-architecture regression: Hermes/Artemis both birth live, no unexpected ACL denials or UNKNOWN WORD. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD |
||
|
|
b0f12710bb |
Console-VM + user-VM pair: real async message-passing relay
Console sessions now route through the same general VM-to-VM messaging system (Phase C) any VM can already use for its own reasons -- not a synchronous shortcut. Per direct instruction: real async MSG-SEND/ MSG-DELIVER (Option B), not a VM-EXEC-based synchronous relay, because messaging is a general capability, not a console-specific mechanism. New CONSOLE-CMD-EVENT message type (common:messaging.4th). New sk_repl_dispatch_line() (repl.c), called from both sk_repl_step and sk_repl_run in place of a direct vm_interpret(): if the active VM's own name has a live "<name>~user" counterpart registered, the raw input line is wrapped as an S"-embedded CONSOLE-CMD-EVENT MSG-SEND and interpreted on the console VM instead of being run directly -- the console's own next MSG-TICK (Hera's idle pump) delivers it into the paired user VM via VM-EXEC, same mechanism every other message already uses. Falls back to direct interpretation if there's no pairing, or if the line contains a `"` (known v1 limitation, warned about explicitly rather than silently mishandled). New capsule_console_birth() (capsule_console.h/.c): a bare VM whose only content is loading common:messaging.4th -- the console side of a pairing, parallel in shape to RUNCAP's user-VM birth but with fixed embedded content instead of a devblock read (no identity, no thumbdrive involved). New PAIR-TEST diagnostic word (mama_forth_words.c, matches RUNCAP-TEST's own precedent): births both halves of a pairing and registers the "<name>~user" mapping. Not the real pairing call site -- that's the eventual attach/onboarding flow -- this exists to exercise the relay live before that flow exists. Found and fixed a real, serious bug live: console_set_vm_name() stored the caller's raw pointer instead of copying it. mama_word_use() (USE) passes a VMRegistryEntry field living on its own stack frame -- once USE returns, that pointer dangles, corrupting every console tag after the first USE (observed directly as garbled "[[]" / binary-looking prefixes instead of "[CaptBob]"). Fixed at the source: console_set_ vm_name() now copies into internal storage. That surfaced a second, related bug across every console_get_vm_name()-based save/restore call site in mama_forth_words.c (BIRTH, VM-STEP, VM-EXEC, CONNECT-HERMES, CONNECT-ARTEMIS): saving just a pointer into the single internal buffer meant an intervening console_set_vm_name() call silently corrupted the saved value before the restore ever ran. New console_save_vm_name() copies into caller-owned storage; every save/restore site updated. Verified end-to-end, live in QEMU: typed WELCOME at a paired console VM -- it did not execute directly (no UNKNOWN WORD), printed ok immediately (queued, async), and on the next idle tick "[CaptBob~user] Minted identity -- default personality" appeared on its own -- genuine delivery and execution in the paired user VM through the real MSG-SEND/MSG-DELIVER pipeline. Console tags confirmed clean (no garbling) across all three architectures' full regression boot. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD |
||
|
|
cc9521d2cc |
Retire emergency CLI: Zuse goes thumbdrive-resident, ACL.4th activated
Three tightly-coupled changes, verified together per Captain Bob's own "getting rid of the emergency cli" direction: 1. Zuse's identity is thumbdrive-resident, never system-resident. New zuse_genesis_marker_t (magic/version/zuse_pubkey[32]/crc) replaces zuse_cert_devblock_t's slot in the top-of-device fence -- the system now remembers only that a root identity exists and its pubkey, never a seed. zuse_cert_devblock_t is kept in the repo, marked superseded, no longer written by any code path. capsule_mint_identity() grows a genesis mode (issuer_vm=NULL): no cert is built or written (Zuse isn't verified against a separate signer -- she's recognized by pubkey match against the marker) and two new optional out-params (out_pubkey/out_seed) let the caller install the cert immediately after a genesis mint. New capsule_zuse_boot_try_attach() (capsule_zuse_boot.c), called from sk_repl_idle() on every fresh USB attach (the only point in the boot lifecycle a thumbdrive can actually be detected -- attach polling doesn't exist yet at kernel_main.c's old one-shot mint point, which is why that whole block is gone): no marker + blank drive -> genesis-mint; marker present + matching drive -> read its own user_identity_seed_t, install the cert. Either way, re-runs ACL-ZUSE-BOOT (zuse.4th) so zuse_session activates exactly like it always has for a same-boot cert install -- ACL-PIN only blocks redefinition, not re-execution, so no new C-side auth logic needed. 2. ACL.4th activated (capsules/init.4th) -- inactive all session until now. Found and fixed a real bug this immediately surfaced: zuse.4th's ACL-ZUSE-BOOT tried `['] ACL-ZUSE-BOOT ACL-PIN` from inside its own still-compiling definition -- the word isn't findable yet at that point, so the whole definition silently failed to compile every previous boot this session (dormant, since ACL.4th never loaded). Fixed: pin after the definition closes, not from within it -- it only needs to happen once anyway, and pinning doesn't block the re-invocation genesis/attach needs. 3. The unauthenticated emergency-CLI ACL bypass is retired (repl.c): `emergency_console = is_hera ? (zuse_session ? 0 : 1) : 0` deleted from both sk_repl_step and sk_repl_run. Every word run from Hera's own bare prompt now goes through ordinary ACL enforcement; emergency_console is driven only by the genuine C-level fault handler again. Added ZUSE-SESSION? (starforth_words.c), a read-only diagnostic matching ZUSE-PUBKEY@'s own precedent, to verify the whole chain directly rather than by inference. Verified end-to-end live in QEMU: fresh boot, no thumbdrive -> ZUSE-SESSION? reads 0. Attach a genuinely blank drive via QMP -> genesis mint fires automatically (no typing) -> ZUSE-SESSION? reads -1 (true). Hermes/Artemis both birth clean on all three architectures with ACL now actually enforced for the first time all session -- no denials, no UNKNOWN WORD beyond the deliberate POST self-test cases. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD |
||
|
|
f6e2737f1e |
Phase E: MINT -- real keypair, Zuse-signed DER cert, working default identity
capsule_mint_identity() (new capsule_mint.h/.c): mints a fresh identity onto a blank/unminted thumbdrive -- real Ed25519 keypair from virtio_rng, a fresh drive_uuid (independent random draw, not derived from the identity seed, per FABRIC-3.md §F.8 decision 3), a Zuse-signed DER cert in the CERTVERIFY format, and a small working default personality (a real WELCOME word, not a stub -- FABRIC-3.md §F.6/§F.8's own "default personality content" question stays open, but whatever mints today must actually do something when RUNCAP births it). Refuses to overwrite a drive that already reads as a recognized home-blocks drive, mirroring WRITE(10)'s own refuse-on-non-blank posture (decided now, not just "reasonable by analogy" as §F.8 left it). x509_build_user_cert() (x509_ed25519.h/.c): the encode-side counterpart to the existing decode functions (x509_extract_ed25519_pubkey(), x509_verify_signature(), x509_extract_serial()) -- a minimal DER TLV writer producing exactly the fields those functions read. Host-tested round-trip against the real decoder before trusting it in the kernel, including a high-bit-serial case that exercises the DER integer-padding rule; all assertions pass (pubkey/serial round-trip, signature verifies against the real issuer, correctly rejects the wrong key and a corrupted signature). New user_identity_seed_t (user_identity_seed.h): the on-disk record for a minted identity's own keypair, same magic+version+fields+pad-to-4096+ real-CRC convention as zuse_cert_devblock_t and homeblocks_sig_t. Fixed devblock layout: sig(1), cert(2), seed record(3), default personality(4). New MINT word (mama_forth_words.c) and a small accessor (sk_repl_get_attached_blk_dev(), repl.h/.c) exposing the currently attached USB device regardless of home-blocks recognition -- MINT's own target is a blank drive, which by definition never sets Phase D's sk_repl_get_homeblocks_dev(). Verified end-to-end live in QEMU: MINT on a genuinely blank test drive, then (after a detach/reattach so the sig cache picks up the fresh header -- a known workflow gap, not fixed here, flagged for whoever builds the real Console onboarding flow) RUNCAP birthed a VM from that drive's own newly-minted content, and VM-EXECing its WELCOME word printed the default personality banner. The full mint-to-birth Tripod identity flow works end to end for the first time. Clean 3-architecture regression boot confirms no side effects on normal boot. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD |
||
|
|
e1e839258d |
Phase D: RUNCAP -- runtime capsule construction from thumbdrive content
capsule_runcap_birth() (new capsule_runcap.h/.c): builds a heap-only, single-entry CapsuleDirHeader + CapsuleDesc + CapsuleNameEntry + arena from a home-blocks drive's identity_src region (skipping the first devblock, reserved for MINT's user_identity_seed_t record) and hands it to the existing, unmodified capsule_birth_baby() -- no new birth mechanism, matching FABRIC-3.md §F.6's own trace. Found and closed a real gap in that trace along the way: capsule_birth_baby()'s signature check calls capsule_get_signatures(), which unconditionally returns the compile-time-baked global array -- meaningless for a heap-built directory, where index 0 would compare RUNCAP's own content against whatever real capsule happens to occupy that slot in the baked array (guaranteed-wrong, not a security check). Added an explicit skip_pki_sig flag (0 for all 4 existing call sites, 1 for RUNCAP): that content's trust comes from CERTVERIFY, a separate root, not the capsule-PKI chain. Also found live: capsule_birth_baby() never sets the registry entry's own .name (every existing caller does this itself afterward via capsule_vm_registry_set_name() -- RUNCAP now does too), and capsule_exec_payload() requires a "Block NNNN" header per chunk of content or it's silently skipped, never executed -- not a bug, but necessary context for whoever authors MINT's default personality content next. Added a small accessor pair (repl.h/.c) exposing the currently attached home-blocks device/sig -- the same gap F.9's own BINDSTEP scoping had already flagged, needed by both. Verified end-to-end live in QEMU: synthetic identity-source content written directly to a thumbdrive image's raw devblocks (no capsule build, no mkcapsule) was read, compiled, and executed by a genuinely new VM via a diagnostic RUNCAP-TEST word -- confirmed via VM-EXEC invoking a word defined only in that source. Clean 3-architecture regression boot (no RUNCAP drive attached) confirms no side effects. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD |
||
|
|
21bca315ff |
Phase C: distributed messaging capsule + idle-loop pump
Extract the messaging vocabulary (arenas, MSG-*/CH-*/MBR-* words) out of capsules/hermes/init.4th into a new shared capsules/common/messaging.4th that Hermes and Artemis each load at birth, giving every VM its own private MSG-ARENA/CH-ARENA instead of only Hermes having one. Hermes stays the owner of the one real, canonical COMMON-CH; Artemis subscribes into it via VM-EXEC at her own birth, and Hermes proactively subscribes Hera (idx 0) since Hera always exists first. Hera does NOT get her own copy: register_child_vm_words()'s own doc comment explains why the STADIUM-* primitives common:messaging.4th depends on are deliberately never registered in her dictionary (keeps her dict_hash off item 4.1's baseline). Confirmed live by loading it into her dictionary anyway first -- every colon-definition referencing an unregistered Stadium primitive was silently dropped (MSG-HEAT@/!, CH-HEAT@/!, MSG-COOL-ALL, MSG-TICK all missing after boot). Reverted that path; she orchestrates via BIRTH/VM-EXEC/VM-CALL instead. Added capsule_vm_registry_get_by_index() (capsule_birth.c/.h) for registry enumeration by birth-order position, and a pump in repl.c's existing idle hook that walks every live VM once per idle beat and VM-EXECs MSG-TICK into each one except Hera's own entry. Verified clean (no UNKNOWN WORD / VM-EXEC errors after birth) on all three architectures. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD |
||
|
|
c07184e984 |
Add VMIdentity type, relocate homeblocks_sig_t off devblock 0
Phase A of the identity pipeline (FABRIC-3.md §F.2/§F.16, §F.13):
- New include/starkernel/vm_identity.h: VMIdentity{owner_pubkey[32],
installed, acl_caps}, its own header per the project's "give real-shaped
data its own header" convention (VMUuid's own precedent), embedded by
value on struct VM. acl_caps is an independent capability bitmask, not
an ordered privilege tier (decided 2026-08-28) -- Zuse isn't a
structurally special VM, her identity just has every bit set
(VM_IDENTITY_CAP_ALL). No individual capability bits assigned yet,
deliberate slack matching blk_meta_t's own acl_reserved precedent --
real bits get names once the operation they gate (BINDSTEP, MINT, ...)
actually gets built.
- Applied the devblock-1 relocation decided in §F.13: new
HOMEBLOCKS_SIG_START_FBLOCK constant (homeblocks_sig.h), repl.c's
homeblocks_sig_check() call site updated from the literal 0. Also
applied the still-owed blockmap_offset/blockmap_devblocks ->
identity_src_offset/identity_src_devblocks rename decided in §F.6 (no
other code referenced the old names). Updated the header's own stale
GPT-relative doc comments to match -- GPT was dropped permanently, this
location is final, not an interim stand-in.
Verified live on all three architectures: clean compile, clean boot to
ok>, Hermes/Artemis both confirmed live with no KILL (no regression from
last session's session-less fix). One pre-existing, unrelated anomaly
observed consistently on all three arches, not caused by this change (no
code touched here intersects Zuse's own fence-write path): "Zuse: minted
but fence write FAILED (not persistent)" -- flagged for its own
investigation, not chased here.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
|
||
|
|
28c1b12c7f |
Wire homeblocks_sig_check() into USB attach: the warn half (Phase 8)
Wired into sk_repl_idle()'s USB hotplug attach handler, right between blkio_usb_open_msc() succeeding and blk_subsys_attach_device() -- logs a distinct message per outcome (recognized / blank-or-foreign / bad-version / bad-crc / read-error). The "refuse" half is deliberately not implemented -- there is nothing real to gate yet. blkio_usb.c has no SCSI WRITE(10) support at all, so there is no write path today to refuse; attach currently only enables read-only access, which is also the general-purpose USB block I/O path this repo already relies on for unrelated testing, not exclusively a home-blocks identity workflow. Refusing attach on blank media would break that legitimate use without protecting anything real -- same "don't build ahead of a real caller" reasoning EXPIRE's deferral used. Refuse belongs on the write path once WRITE(10) exists. sig_start_fblock is hardcoded to 0 at the call site -- correct for today's unpartitioned raw test/real media (no GPT parser exists yet), flagged in the comment as the one place that changes once a real GPT-partition-relative lookup exists, isolated from homeblocks_sig.c's own location-agnostic check logic. Verified live: hot-attached disk/usb-thumbdrive-test.img (blank media) through a running amd64 instance's QMP socket (blockdev-add + device_add usb-storage) -- captured exactly 4 real TUR+READ10 BOT cycles (matching the header's 4 forth-block span) followed by the correct "not recognized" warning, then normal attach completing successfully afterward (no regression). Conservation intact, no panic. Clean zero-warning compile and clean boot on all three architectures. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
8eaefeb9ee |
sk_repl_idle() auto-flush: implement Section V's "anything dirty? no? done" check
Makes blk_vm_flush_all() (block_words.c) non-static and declares it in block_words.h -- it's already the entire implementation behind SAVE-BUFFERS (block_word_save_buffers() is a one-line wrapper), so sk_repl_idle() can call the exact same flush path outside word dispatch without duplicating any logic. Cheap every idle tick regardless of dirty state: every check inside is a small fixed-size scan, so no separate pre-check was needed on top of it. Caught a real bug via a live persistence test before trusting the feature: the first version gated the flush on sk_repl_get_active_vm() returning non-NULL, but NULL is that accessor's documented default (Tripod's own USE-redirect override, "restore default dispatch") -- without an active USE redirect, the flush silently no-op'd for the entire session. Confirmed live: wrote a byte via BUFFER (no UPDATE/SAVE-BUFFERS), waited past the idle cadence, killed QEMU abruptly, rebooted with the same disk image, read back 0 instead of the written 65. Fixed by threading the VM sk_repl_run()'s own loop already resolves each iteration (g_repl_active_vm ? g_repl_active_vm : vm) down as a parameter through sk_readline() into sk_repl_idle(), rather than trying to re-derive it from an accessor with the wrong default. Re-ran the same test after the fix: read back 65, matching the written byte -- the write survived an abrupt kill with no explicit flush call anywhere in the test, proving the idle-tick auto-flush genuinely ran. All three architectures re-verified clean. FABRIC-2.md Section V item 6 and the corresponding Milestone 3 punch-list item marked done. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
af267a52a6 |
Artemis Milestone 2h: hot-detach -- 2h complete
blk_subsys_detach_device() (block_subsystem.c) walks the device chain, refuses removal of anything but the current tail (a mid-chain removal would corrupt every later slot's start_lbn -- this architecture's own doc already argues USB stays last specifically to avoid that), unlinks, shrinks total_user_lbn, closes and frees the slot. Discards rather than flushes dirty state -- the device is physically gone by the time this runs (PORTSC disconnect only). Trigger wiring mirrors the attach path: bot_msc_attached (set only once attach actually succeeds) gates a new bot_msc_detach_pending flag set at PORTSC disconnect (not Disable Slot completion, which is conditionally skipped and would miss concurrent connect/disconnect pairs), consumed in sk_repl_idle(). Advisor flagged the real hazard ahead of time: block_words.c's VM block window (blk_vm_lbn[]/blk_vm_cbuf[]) can go stale across a detach then a same-LBN re-attach, and suggested a pointer-identity re-check in blk_vm_load() as a minimal fix. That fix was implemented, then directly falsified by its own designed-for-this test: attach a blank device, read a block (populating the cache), detach, re-attach a device with distinct content at the identical LBN, read again -- served stale content from the first device. Root cause, confirmed live: glibc's allocator hands free(slot) straight back to the very next same-size calloc(), so the "fresh" and stale pointers were bitwise identical despite being two different devices. Fixed properly with a monotonic blk_subsys_epoch() counter (bumped on every attach/detach) checked by a new blk_vm_check_epoch() helper at the one choke point (blk_vm_find(), plus blk_vm_flush_all() which reads the same arrays directly) that covers every path touching the window cache -- unfooled by address reuse. Verified live with a new disk/usb-thumbdrive-test2.img fixture (distinct content from the existing blank test image): attach A, read (cache hit populated), detach, re-attach B at the same LBN, read again -- correctly ran a fresh device read and returned B's real content, not A's stale cached zeros. The failing pointer-comparison attempt's own capture log kept as evidence, not deleted. All three architectures re-verified clean. FABRIC-2.md Section X 2h marked complete -- enumeration through hot-detach all live and verified; only WRITE(10) (2g's own still-open item) remains unimplemented in the driver, not blocking anything here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
3b085dd875 |
Artemis Milestone 2h: blkio_usb.c backend -- USB thumb drive is now a real block device
Wires a hot-plugged USB Mass Storage device into the block subsystem's unified LBN chain. blkio_usb.c/blkio_usb.h mirror virtio_blk.c/ virtio_blk.h's established shape exactly (singleton state, blkio_vtable_t, a blkio_usb_open_msc() "find" function playing virtio_blk_find_artemis()'s role): read() translates a Forth block into a SCSI LBA/count pair and calls xhci_bot_read_block() + xhci_bot_wait_for_idle(); write() returns BLKIO_ENOSUP (no SCSI WRITE(10) exists yet, and blk_format_or_load_disk() never writes at attach time, so read-only is sufficient -- confirmed by reading that function first, not assumed). Refuses (-2) if the reported SCSI block size doesn't evenly divide the 1024-byte Forth block size. Connect-time wiring reuses the bot_msc_attach_pending/consume-in- sk_repl_idle() shape the prior increment's temp probe already validated, now made permanent: SET_CONFIGURATION sets the flag, sk_repl_idle() (strictly after its own xhci_poll_events() call returns) calls blkio_usb_open_msc() then blk_subsys_attach_device(). Verified live via hot-attach: full chain from USB connect through 'blkio_usb: MSC device ready' to 'blk: disk 'StarForth Volume' v2 LBN 26074..75184 (49111 user blocks)' -- real attachment, disk image confirmed byte-for-byte untouched after. Chased a real debugging detour along the way: the attach initially appeared silent (no blk: log line) -- traced to LOG_INFO filtering at the default LOG_WARN boot level, not a functional bug (settled via a temporary log-level bump, reverted after capture; also found and reported, but did not fix, a pre-existing unrelated Makefile.starkernel bug where --log-level=info via KERNEL_ARGS breaks printf parsing). All three architectures re-verified clean. FABRIC-2.md Section X 2h updated -- only hot-detach remains for 2h. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
2b16daba16 |
Artemis Milestone 2d: xHCI Event Ring servicing, polled not interrupt-driven
Implements Event Ring TRB parsing and ERDP dequeue-pointer update
(xhci_poll_events(), src/starkernel/usb/xhci.c), called from
sk_repl_idle()'s existing ~1s idle cadence rather than a per-arch
interrupt handler.
A first attempt wired real interrupt delivery (PCI->IOAPIC GSI routing,
a dedicated isr_stub34/vector 0x22, GIC/PLIC routing mirroring
virtio_input.c). Checked live via QMP query-pci before trusting it: the
amd64 PIRQ swizzle formula predicted GSI 16 for the xHCI controller at
PCI slot 4; the real QEMU-assigned IRQ was 10, and embedded ICH9
functions contradicted the same formula too. Reverted all of it back to
the exact committed baseline rather than chasing chipset PIRQ routing
further, and reframed around Section U item 6's own design intent
("interrupt-driven, coarse cadence, cheap early-exit... quick check
blocks... done") via sk_repl_idle() instead -- USB insertion is a
human-timescale event, not a hot path.
Added -device qemu-xhci to all three QEMU launch targets (required for
any of this to be testable). Verified end to end via genuine post-boot
hotplug (QMP device_add/device_del usb-storage): all three architectures
detect a live attach within seconds. A false-alarm heartbeat "freeze"
found mid-verification traced to querying the wrong counter
(vm->heartbeat.tick_count, which only advances during word execution,
not the kernel's real ISR-driven heartbeat_ticks()) -- confirmed via a
temporary diagnostic word, captured and reverted.
Full writeup, including the discarded interrupt-routing attempt and the
false-alarm investigation, in FABRIC-2.md's Milestone 2c/2d entries.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZ8kNoTuP63pbQtro4qvrm
|