Fix SWITCH-MARK-WORK off-by-one: was tripping vm->error on every MSG-SEND (FABRIC-3.md §XXVIII.2)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run

mama_word_switch_mark_work()'s stack-underflow guard checked dsp < 2,
requiring 3+ items, when it only ever needs the 2 IDX>NAME leaves it
(caddr u). Since dsp is index-based (2 items == dsp 1), this rejected
every normal call. MSG-SEND tail-calls SWITCH-MARK-WORK unconditionally,
so this fired on every message sent anywhere in the system -- visible
only where a caller happened to check the target VM's error flag
afterward (mama_word_vm_exec()'s "VM-EXEC: ERROR in Artemis" report).

Verified clean on all 3 architectures: boot reaches Startup: Artemis
live -> zuse@Hera] ok> with no VM-EXEC: ERROR line at all.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
This commit is contained in:
Robert Allan James
2026-09-14 20:17:01 -04:00
co-authored by Claude Sonnet 5
parent 66beae7fd4
commit 05ae7aa886
9 changed files with 45289 additions and 2 deletions
+55
View File
@@ -3894,3 +3894,58 @@ someone's attention in a future pass; `mama_word_vm_exec()`'s error-report path
(both mechanisms can independently move control between the same VMs) is unchanged by this (both mechanisms can independently move control between the same VMs) is unchanged by this
pass. Stage 4 (WIREBIND-scope extension) remains a ratified-decision-only step, untouched. pass. Stage 4 (WIREBIND-scope extension) remains a ratified-decision-only step, untouched.
## XXVIII.3 -- §XXVIII.2's own `SWITCH-MARK-WORK` had an off-by-one, tripping on every
single `MSG-SEND` in the system; root-caused and fixed (2026-09-14)
**The `VM-EXEC: ERROR in Artemis` item §XXVIII.2 flagged as "found live, deliberately not
fixed" traced back to a genuine bug in that same section's own new code, not to something
pre-existing.** `mama_word_switch_mark_work()` (`mama_forth_words.c`, backing `SWITCH-MARK-WORK`)
guarded against stack underflow with `if (vm->dsp < 2) { vm->error = 1; return; }`. `vm->dsp` is
index-based in this codebase (confirmed via the existing ACL-denial reset, `vm->dsp = -1` for an
empty stack, `vm_core.c`) -- so 2 items on the stack is `dsp == 1`, and `SWITCH-MARK-WORK` only
ever needs the 2 items `IDX>NAME` leaves it (`caddr u`). The guard demanded `dsp >= 2` (3+
items), rejecting every normal, correctly-formed call. Since `MSG-SEND`
(`capsules/common/messaging.4th`) tail-calls `SWITCH-MARK-WORK` unconditionally on every send,
**this fired on every single message sent anywhere in the system**, not just the one boot-time
case that happened to be visible in the console log.
**Why it stayed invisible everywhere except one deterministic spot:** `MSG-SEND`'s actual
message-queuing work all completes before the broken tail call, so messages kept being
delivered correctly regardless -- the bug only ever left a stray `vm->error = 1` behind
afterward on whichever VM had just executed the send. The one place this became visible was
`HERA-BLK-ATTACH-REQ` (`capsules/artemis/init.4th`), executed inside Artemis via a direct
`VM-EXEC` from `repl.c`'s USB-attach handler: its own `MSG-SEND` reply to Hera left
`target->error` set, and `mama_word_vm_exec()`'s own error-report path (the thing printing
"VM-EXEC: ERROR in Artemis") is the only caller in this codebase that actually inspects and
reports a target VM's `error` flag after the fact. Everywhere else `MSG-SEND` is called --
`MSG-DELIVER`'s own idle-pump dispatch included -- nothing downstream happened to check
`vm->error` afterward in a way that surfaced console output, so the same bug was firing
silently on every send, all along. Flagged as a real, if quieter, latent risk this correction
does not separately chase: `execute_colon_word()`'s own dispatch loop aborts the *next* word in
a colon-body sequence if `vm->error` is already nonzero when it's reached (`vm_core.c` ~line
817) -- so a stray unset error flag surviving past a message send, into a caller's own
subsequent code in the same execution frame, was a real (if apparently unobserved so far)
correctness risk this bug carried the whole time it was live.
**Diagnosis method, worth recording:** the first diagnostic attempt (print
`target->current_executing_entry` from `mama_word_vm_exec()`'s error branch) came back useless
every time (`(none)`) -- traced to `vm_interpret_word()` itself unconditionally zeroing
`current_executing_entry` immediately after a top-level word's own `func()` returns, error or
not (`vm_core.c` ~line 1070), so it can never reflect the word that actually set the error for
this class of failure (a word found and dispatched normally, whose own C function sets
`vm->error` internally, as opposed to a genuine "word not found"). Replaced with a direct print
of the attempted command text (`cmd_buf`) instead, which immediately showed the real call
(`52108768 HERA-BLK-ATTACH-REQ`) and pointed straight at `MSG-SEND`'s tail call once traced
by hand. Both probes were temporary, reverted immediately after their respective captures, per
this project's usual discipline -- neither is present in the fix commit.
**Fix:** `dsp < 2` -> `dsp < 1`. One line. Left `STADIUM-ADMIT`'s own `dsp < 2` check
(`mama_forth_words.c`, unrelated function, genuinely needs 3 items) untouched -- confirmed by
reading its own pop sequence before editing, not assumed from the shared literal.
**Verification:** all three architectures rebuilt clean and boot-tested by Claude Sonnet 5 at
Bob's request, same one-at-a-time foreground discipline as §XXVIII.2's own verification. All
three now reach `Startup: Artemis live` -> `[zuse@Hera] ok>` with **no** `VM-EXEC: ERROR` line
at all -- confirming both that the fix is correct and that this was the sole cause of that
report (nothing else was masking or contributing to it).
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated # Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-09-14T20:33:48Z --> <!-- Generated by mkcapsule --manifest 2026-09-15T00:05:32Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. --> <!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live --> <!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. --> <!-- in MANIFEST.md alongside this auto-generated index. -->
BIN
View File
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -804,7 +804,7 @@ static void mama_word_switch_mark_work(VM *vm)
uint32_t i; uint32_t i;
VMRegistryEntry entry; VMRegistryEntry entry;
if (vm->dsp < 2) { vm->error = 1; return; } if (vm->dsp < 1) { vm->error = 1; return; }
u = vm_pop(vm); u = vm_pop(vm);
caddr = vm_pop(vm); caddr = vm_pop(vm);