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 ac4d431 --
without it, WIREBIND cannot attach any identity on a fresh boot at
all, which blocked this task's own verification until found and fixed
separately.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-22 13:34:20 -04:00
co-authored by Claude Sonnet 5
parent ac4d431aee
commit 3975dc61cd
38 changed files with 158140 additions and 24 deletions
+80 -12
View File
@@ -1117,18 +1117,86 @@ ruling]** cannot be written precisely until Captain Bob settles the named sub-it
invisibility rather than producing new acceptance evidence. All superseded by
`105501`/`105758`/`110304`, cited above, once the disk-reset-before-each-arch procedure and
the final single-line evidence design were both settled.
- [~] **3.9** — **Stage D: `CONSOLE-CMD-EVENT` (type 7)**, then **3.10 `ELEVATE-REQUEST`
(type 8)** — one type per task, each with the 3.8 check. Any type found live beyond these
three is added here, not bundled.
2026-09-22 · **PAUSED, not started-and-blocked by its own scope** — the send-side cutover
(`SK_HERMES_MSG_TYPE_CONSOLE_CMD`, `kernel_hermes.h`; `sk_repl_dispatch_line()`'s new
kernel-Hermes call, `repl.c`) is written but uncommitted. Orienting for verification (Bob
ruled: full QMP/serial-socket-driven real console session, not a self-test) surfaced the
task 3.8 payload-aliasing defect recorded in the findings log above — real fix changes
`SkHermesMessage`'s shape and is bigger than this task. Paused pending Bob's call on
sequencing. `g_kh_console_cmd_buf` (this task's own new static buffer) inherits the exact
same defect shape if committed as-is — do not commit until the task 3.8 fix lands, or this
task ships the same bug a second time.
- [x] **3.9** — **Stage D: `CONSOLE-CMD-EVENT` (type 7)** (§XXXIV.3). One layer owns it; FORTH
Hermes still routes every other type. *Check:* a real interactive console-session relay
works end to end on all three ISAs, via `USE` into a live WIREBIND identity followed by a
plain (unquoted) console line; the same task 3.8 evidence discipline (ledger +
`stadium_conserved()`, `fleet_conserved` not accepted as evidence).
2026-09-22 · **Resumed and closed** — Captain Bob ruled 2026-09-22 to land the send-side
cutover first (already written, per the pause note below) and fix the task 3.8
payload-aliasing defect separately; that sequencing decision stands, the aliasing fix is
still its own open item (findings log above). Final acceptance:
`logs/20260922-132142/amd64/`, `logs/20260922-132512/aarch64/`,
`logs/20260922-132848/riscv64/` — `disk/artemis.img` and
`disk/thumbdrives/{zuse,bob}-thumb-ident.img` reset (`git checkout --`) before each of the
three, disk-reset-before-each-arch procedure unchanged from task 3.8. All three reach
`[zuse@Hera] ok>`, zero `UNKNOWN WORD`, `mkcapsule --lint capsules/` clean (38 files, 0
violations). **`dict_hash` identical across all three architectures for every VM**,
matching task 3.8's own baseline exactly: `0xb8052b99f1d75f59` (Hera) /
`0xecad3d867c9bfec2` (Hermes) / `0xdf59e557986c9009` (Hestia) / `0xa8999a854545f274`
(Artemis).
**What actually cut over**, matching task 3.8's own precedent: `sk_repl_dispatch_line()`'s
"`CONSOLE-CMD-EVENT 0 3 S\" ...\" 0 MSG-SEND`" FORTH-string-interpret is gone; a plain
(unquoted) console line typed while paired to a live WIREBIND identity now calls
`sk_hermes_send_one()` (`repl.c`) directly, with `SK_HERMES_MSG_TYPE_CONSOLE_CMD`
(`kernel_hermes.h`) deliberately reusing `CONSOLE-CMD-EVENT`'s own value (7), same
partition-rule reasoning task 3.8 already established for `BLK-ATTACH-EVENT`. Delivery is
task 3.4's `sk_hermes_drain_checkpoint()`, calling `vm_interpret()` on the target identity
VM with the payload text — the console line itself, unwrapped, since kernel-Hermes's
payload is raw bytes, not a FORTH string literal.
**Real finding, found live verifying this exact task, not folded into the send-side
cutover's own write-up:** kernel-Hermes's drain only ever runs as a side effect of
`vm_interpret()` being called ON the target VM (`vm_core.c`'s own outermost-interpret
checkpoint hook, task 3.4) — it has nothing to do with the old FORTH `MSG-TICK` word. Task
3.8's target was Hera herself, who is *always* being interpreted (the interactive REPL
loop), so her own queue drained as a side effect of ordinary console activity. Task 3.9's
target is a WIREBIND identity's own `~user` VM — a passive receiver nothing else drives.
Confirmed live: `sk_hermes_send_one()` returned `0` (queued correctly) but the payload sat
undelivered indefinitely — no crash, no error, matching `FABRIC-3.5.md §XXXV.0`'s own named
failure signature exactly. Root cause: `sk_repl_idle()`'s existing round-robin pump
(`SK_MSG_PUMP_BATCH`/`g_msg_pump_cursor`, built for the *old* FORTH `MSG-TICK` system) only
gives a live VM an interpret tick if `vm_find_word(vm, "MSG-TICK", 8)` finds an
ACL-allowed entry — a VM minted with `MINT_PERSONALITY_STD79_LOCKDOWN` (FORTH-79/83
standard words only, no `common:messaging.4th`) never has that word, so the pump silently
skipped it forever, and kernel-Hermes's drain checkpoint never got a chance to fire.
**Fixed in the same pump loop** (`repl.c`, `sk_repl_idle()`): an unconditional, direct call
to `sk_hermes_drain_checkpoint((VM*)ent.vm_ptr)` for every live non-Hera VM visited each
beat, independent of the MSG-TICK/VM-EXEC dispatch below it — no FORTH word required,
since kernel-Hermes's own drain is a plain C function designed to be called standalone
(`kernel_main.c`'s own boot self-test already calls it exactly this way). Verified live:
relay now delivers, typically one idle beat after the send (bounded, not instant — the
pump runs once per idle cycle, matching the existing MSG-TICK dispatch's own latency
characteristic, not a regression this task introduced).
**Second real finding, also worth recording though it did not block this task:** the
`FABRIC-3.md §XXX` (2026-09-15) documented `USE`/BINDSTEP crash ("interactive `USE
<identity>` on a freshly-attached identity halts the kernel outright") **did not
reproduce** in this task's own live testing, on any of the three architectures, across
several repeated `USE rajames` calls on a freshly-WIREBIND-attached identity. Not chased
further — either something in the intervening 3.6-series work fixed it as a side effect,
or the specific reproduction conditions differ from this task's own test shape. Recorded
as a finding, not claimed as a fix (no root-cause investigation was done here).
**Also found and fixed along the way, its own separate commit
(`ac4d431`, already landed before this task's own final acceptance run):**
`zuse_root_pubkey_known` never got set after a same-session fresh genesis mint, silently
blocking WIREBIND for every identity attach on any boot that reset `artemis.img`
fresh — which is exactly what this project's own acceptance convention does before every
run. See that commit's own message for the full account; not restated here.
**A prompt-format change was requested mid-task (Captain Bob), attempted, found to
regress live (`[zuse@Artemis]` instead of the intended reformat), and reverted back to the
original, unmodified `[zuse@Hera]`-style format before this task's final acceptance run** —
confirmed by every log cited above. The reformat needs real new state (separating "which
identity" from "which machine," last discussed as needing changes across 8+ save/restore
call sites in `mama_forth_words.c`) and is deliberately deferred to its own task rather
than interleaved with this one's live debugging. Not tracked as a numbered task yet —
pick a number when it's actually scheped.
- [ ] **3.10** — **Stage D: `ELEVATE-REQUEST` (type 8)**, the same one-type-per-task check as
3.8/3.9. Split out as its own explicit item (previously bundled into 3.9's own text without
a checkbox) per this document's own carry-forward-discipline rule (§XXXI.2).
- [ ] **3.11** — **Phase 3 gate.** No FORTH-owned live message types remain; Stage B evidence
holds across a full boot on all three ISAs. **Do not start Phase 4 until this passes.**
+9 -3
View File
@@ -689,15 +689,21 @@ int sk_hermes_reassemble(SkHermesMessage **chunks, int n_chunks,
* new one invented alongside it. */
#define SK_HERMES_MSG_TYPE_BLK_ATTACH 9
/* SK_HERMES_MSG_TYPE_CONSOLE_CMD (FABRIC-3.6.md task 3.9, Stage D) --
* same reasoning as SK_HERMES_MSG_TYPE_BLK_ATTACH above: deliberately
* reuses messaging.4th's own CONSOLE-CMD-EVENT value (7), since this is
* that type's real cutover, not a fresh kernel-Hermes-space number. */
#define SK_HERMES_MSG_TYPE_CONSOLE_CMD 7
/* The five negotiation types above (20-24) were chosen clear of
* messaging.4th's own live/reserved type space (PAUSE-EVENT=2,
* RESUME-EVENT=3, KILL-EVENT=4, CONSOLE-CMD-EVENT=7, ELEVATE-REQUEST=8,
* BLK-ATTACH-EVENT=9, MSG-NACKED=253, MSG-DELIVERED=255) precisely
* because none of them had a real cutover yet -- kernel-Hermes stayed
* its own inert, parallel type space for those. SK_HERMES_MSG_TYPE_
* BLK_ATTACH above is the deliberate exception: task 3.8 IS that type's
* real cutover, so it reuses BLK-ATTACH-EVENT's own value (9) rather
* than picking a fresh one. */
* BLK_ATTACH and _CONSOLE_CMD above are the deliberate exceptions: tasks
* 3.8/3.9 ARE those types' real cutovers, so they reuse the FORTH-side
* value rather than picking a fresh one. */
/* sk_hermes_channel_request - requester asks target for a new private
* channel. A point-to-point CH_REQUEST (sk_hermes_send_one(), tagged
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
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
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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+78 -9
View File
@@ -816,6 +816,30 @@ static void sk_repl_idle(VM *active_vm)
if (capsule_vm_registry_get_by_index(i, &ent) == 0 &&
ent.state == VM_STATE_LIVE &&
ent.vm_ptr != (void *)mama) {
/* FABRIC-3.6.md task 3.9, found live 2026-09-22: kernel-
* Hermes's own drain (sk_hermes_drain_checkpoint(), task
* 3.4) only ever runs as a side effect of vm_interpret()
* being called ON the target VM (vm_core.c's own outermost-
* interpret checkpoint hook) -- it has nothing to do with
* the old FORTH MSG-TICK word this loop was built around.
* A WIREBIND identity minted with the STD79-lockdown
* personality (MINT_PERSONALITY_STD79_LOCKDOWN) never loads
* common:messaging.4th, so the MSG-TICK gate just below
* always skips it -- meaning it never got an interpret
* tick at all, meaning a CONSOLE-CMD-EVENT queued for it
* sat in its pending queue forever, silently. Confirmed
* live: sk_hermes_send_one() returned 0 (queued OK) but no
* drain ever ran. Called unconditionally here, independent
* of the MSG-TICK check below -- kernel-Hermes doesn't need
* a FORTH word to exist, it just needs its target VM to get
* an outermost interpret call periodically. Direct C call,
* not routed through VM-EXEC/vm_interpret: drain_checkpoint
* already saves/restores the target's own interpret state
* internally (kernel_main.c's own self-test calls it the
* same standalone way), so no vm_interpret() wrapper is
* needed to reach it from here. */
(void)sk_hermes_drain_checkpoint((VM *)ent.vm_ptr);
/* FABRIC-3.md, 2026-09-09: a per-VM flag (e.g. "this one is
* std79-locked") doesn't generalize -- any VM whose own
* dictionary lacks MSG-TICK, for whatever reason (never
@@ -1343,6 +1367,27 @@ static int sk_repl_line_calls_use(const char *input)
return (len == 3) || (input[len - 4] == ' ' || input[len - 4] == '\t');
}
/* FABRIC-3.6.md task 3.9 -- static, not stack-local, for the same reason
* g_kh_blk_attach_buf (task 3.8) is: sk_hermes_send_one() stores
* payload_addr out-of-line and the caller must keep it alive until
* drained. Sized to the largest single payload kernel-Hermes will ever
* accept (SK_HERMES_CHUNK_MAX_PAYLOAD, task 3.5's one-block bound) --
* console lines up to INPUT_BUFFER_SIZE-1 (1024) content bytes are
* silently capped to SK_HERMES_CHUNK_MAX_PAYLOAD-1 (1023) to leave room
* for the NUL terminator sk_hermes_drain_checkpoint() requires; chunking
* a console line across multiple messages is out of this task's scope
* (task 3.5 built the primitives, not a sender for this use) and the cap
* is not expected to be reached by ordinary interactive typing. Only one
* console-proxy session exists today (console.c's own singleton, per
* memory project_multiseat_console_idea) so the single shared buffer
* carries the same payload-aliasing defect g_kh_blk_attach_buf has
* (FABRIC-3.6.md task 3.8's write-up wrongly called this shape benign;
* corrected in the findings log, entry "found while starting task 3.9",
* once task 3.8's own defect was found). Fix deferred: Captain Bob
* ruled 2026-09-22 to land task 3.9's send-side cutover first and fix
* the aliasing separately. */
static char g_kh_console_cmd_buf[SK_HERMES_CHUNK_MAX_PAYLOAD];
static void sk_repl_dispatch_line(VM *vm, const char *input)
{
/* H1 reentrancy guard: while this dispatched line executes on Hera
@@ -1376,16 +1421,40 @@ static void sk_repl_dispatch_line(VM *vm, const char *input)
console_println("console: line contains '\"' -- can't relay "
"as a message safely yet, interpreting directly");
} else {
char cmd[INPUT_BUFFER_SIZE + 64];
/* to-index 3: the fixed convention this console's own
* VM-NAME-REG entry for its paired user VM uses (set
* once at pairing time -- see the pairing word). */
int n = snprintf(cmd, sizeof(cmd),
"CONSOLE-CMD-EVENT 0 3 S\" %s\" 0 MSG-SEND", input);
if (n > 0 && (size_t)n < sizeof(cmd)) {
vm_interpret(vm, cmd);
goto out;
/* FABRIC-3.6.md task 3.9 (Stage D, SXXXIV.2/.3):
* CONSOLE-CMD-EVENT's real cutover -- the FORTH
* "CONSOLE-CMD-EVENT 0 3 S\" ...\" 0 MSG-SEND" string
* interpret is gone; this now calls kernel-Hermes
* directly. `paired.vm_id` (just resolved above) is
* the real target VMUuid -- no name-index translation
* needed, unlike the FORTH convention's local "index
* 3" hack. `from` is the console-proxy VM's own real
* identity (`vm->stadium_vm_id`), not the FORTH
* convention's hardcoded "0" (which read as Hera in
* every console-proxy's shared VM-NAMES-INIT table,
* regardless of which console actually sent it) --
* more correct provenance, a deliberate refinement
* task 3.9 makes possible, not a silent behavior
* change: the receiving side never read `from` for
* anything but bookkeeping. The `"` guard above is
* kept even though kernel-Hermes's payload is a raw
* pointer+length, not a FORTH string literal, and no
* longer needs it -- removing a documented v1
* limitation is its own decision, not a side effect
* of this one (per advisor() review). */
size_t ilen = strlen(input);
if (ilen >= SK_HERMES_CHUNK_MAX_PAYLOAD)
ilen = SK_HERMES_CHUNK_MAX_PAYLOAD - 1; /* leave room for the NUL --
* see g_kh_console_cmd_buf's
* own doc comment */
memcpy(g_kh_console_cmd_buf, input, ilen);
g_kh_console_cmd_buf[ilen] = '\0';
if (sk_hermes_send_one(vm->stadium_vm_id, paired.vm_id,
SK_HERMES_MSG_TYPE_CONSOLE_CMD, 0,
g_kh_console_cmd_buf, (uint32_t)(ilen + 1)) != 0) {
log_message(LOG_ERROR, "console: kernel-Hermes refused CONSOLE-CMD-EVENT send");
}
goto out;
}
}
}
+26
View File
@@ -45,6 +45,7 @@
#ifdef __STARKERNEL__
#include <string.h>
#include "freestanding/stdio.h" /* task 3.9 evidence print -- snprintf() */
#include "starkernel/vm/kernel_hermes.h"
#include "starkernel/vm/stadium.h"
#include "starkernel/q48_16.h"
@@ -586,6 +587,31 @@ int sk_hermes_drain_checkpoint(VM *vm) {
saved_error = vm->error;
saved_abort = vm->abort_requested;
/* FABRIC-3.6.md task 3.9 evidence: CONSOLE-CMD-EVENT has no fixed
* FORTH handler word to hang a print on (task 3.8's BLK-ATTACH-ACK
* did) -- its payload is an arbitrary console line, interpreted
* as-is below. So the evidence line lives here instead, gated on
* msg->type, printed before vm_interpret() runs while this
* message's heat is still held (release()/pop() are further down
* in this same function) -- the same mid-hold-instant evidence task
* 3.8 used, same honest limit: evidence for mid-hold, not
* post-release. console_println, not log_message() (confirmed
* invisible in this build's serial capture, task 3.8's own
* finding); fires once per relayed console line, not per word. */
if (msg->type == SK_HERMES_MSG_TYPE_CONSOLE_CMD) {
uint64_t held, pulled, returned, consumed;
char line[160];
int conserved = stadium_conserved(self);
sk_hermes_ledger(&held, &pulled, &returned, &consumed);
snprintf(line, sizeof(line),
"Kernel-Hermes CONSOLE-CMD-EVENT (real, Stage D): ledger held=%llu "
"pulled=%llu returned=%llu consumed=%llu stadium_conserved(self)=%s",
(unsigned long long)held, (unsigned long long)pulled,
(unsigned long long)returned, (unsigned long long)consumed,
conserved ? "true" : "FALSE");
console_println(line);
}
vm->mode = MODE_INTERPRET; /* a payload is never compiled into whatever
* the enclosing context was mid-defining */
vm_interpret(vm, (const char *)msg->payload_addr);