Files
LithosAnanake/logs/hermes-tripod-integration-design.md

7.3 KiB

Hermes Tripod Integration Test — Executive Summary

Date: 2026-06-27
Author: Claude Code / Captain Bob (Robert Allan James)
Branch: lithosananke
Status: Design — not yet implemented


What This Addresses

The Hermes smoke test confirms the messaging substrate in isolation. What it does not exercise is the inter-VM dispatch path: MSG-DELIVER → VM-EXEC → recipient handler. In the hosted build VM-EXEC is a no-op stub (2DROP 2DROP). This design replaces that stub for the duration of the test with a working dispatcher, then drives all six directional message paths through the Tripod and confirms each one with a stdout line.

No logs are kept. No persistent state changes. The test script patches init.4th, runs, and restores — same pattern as tools/hermes_smoke.sh.


Architectural Basis

From TRIPOD.md — dispatch is capability-based, never heat-based:

Work type Correct recipient
VM lifecycle Hera
Event emit/drain Hermes
Block I/O Artemis

VM-EXEC signature (from MSG-DELIVER, block 4107):

( paddr plen name-addr name-len -- )

IDX>NAME maps VM index → string:

  • 0"Hera" (len 4)
  • 1"Hermes" (len 6)
  • 2"Artemis" (len 7)

All three names have unique lengths. The hosted test dispatcher uses name length as its discriminator — no COMPARE word is needed.


The Six Message Paths

# Sender Recipient Type Rationale (capability-based)
1 Hera Hermes SPAWN-EVENT Hera notifies Hermes: child was spawned
2 Hera Artemis 10 (arbitrary) Hera requests persistence of boot record
3 Hermes Hera SPAWN-EVENT Hermes confirms delivery back to Hera
4 Hermes Artemis 11 (arbitrary) Hermes stores message log to Artemis
5 Artemis Hera 12 (arbitrary) Artemis ACKs Hera boot record stored
6 Artemis Hermes 13 (arbitrary) Artemis asks Hermes to route a result

After all six sends, HERMES-TICK runs once. All six messages cool one step (heat: Q.1 → Q-DECAY). Then 0 FILL heat on each and a second HERMES-TICK reaps them. Final check: MSG-ARENA MSG-TYPE@ must be 0 (arena clean).


Exemplar: VM-EXEC Dispatcher Shim

This block replaces the no-op VM-EXEC for the duration of the test. Name-length dispatch: 4=Hera, 6=Hermes, 7=Artemis.

( test VM-EXEC dispatcher — hosted only )
: VM-EXEC ( paddr plen name-addr name-len -- )
  >R >R 2DROP R> R>        ( drop payload, keep name )
  DUP 4 = IF DROP ." [Hera recv]"    CR EXIT THEN
  DUP 6 = IF DROP ." [Hermes recv]"  CR EXIT THEN
           DROP ." [Artemis recv]"   CR ;

Stack trace for the 4 = branch:

  • Entry: ( name-addr name-len )
  • DUP 4 =( name-addr name-len flag )
  • IF DROP( name-addr ) — then ." ..." and EXIT, name-addr is dead on stack

Wait — name-addr leaks. The handler must also drop it:

: VM-EXEC ( paddr plen name-addr name-len -- )
  >R >R 2DROP R> R>
  DUP 4 = IF 2DROP ." [Hera recv]"    CR EXIT THEN
  DUP 6 = IF 2DROP ." [Hermes recv]"  CR EXIT THEN
             2DROP ." [Artemis recv]"  CR ;

2DROP consumes ( name-addr name-len ) after DUP copies len for the test. Actually with DUP 4 = IF:

( name-addr name-len )
DUP →  ( name-addr name-len name-len )
4 =  → ( name-addr name-len flag )
IF   → ( name-addr name-len )   ← both still on stack inside IF
2DROP  ( -- )
." [Hera recv]" CR EXIT

That is correct. Remainder branch falls through with ( name-addr name-len ) still on stack after the failed IF, so 2DROP at the end of each branch is correct.


Exemplar: Six-Path Test Sequence

( Hermes Tripod Integration Test )
CD-INIT
." >>> Tripod integration test" CR

( Path 1: Hera → Hermes )
." >>> [1] Hera->Hermes SPAWN-EVENT" CR
SPAWN-EVENT 0 1 S" boot" MSG-SEND

( Path 2: Hera → Artemis )
." >>> [2] Hera->Artemis store request" CR
10 0 2 S" boot-rec" MSG-SEND

( Path 3: Hermes → Hera )
." >>> [3] Hermes->Hera delivery confirm" CR
SPAWN-EVENT 1 0 S" ok" MSG-SEND

( Path 4: Hermes → Artemis )
." >>> [4] Hermes->Artemis log store" CR
11 1 2 S" msglog" MSG-SEND

( Path 5: Artemis → Hera )
." >>> [5] Artemis->Hera ACK" CR
12 2 0 S" stored" MSG-SEND

( Path 6: Artemis → Hermes )
." >>> [6] Artemis->Hermes route result" CR
13 2 1 S" result" MSG-SEND

." >>> HERMES-TICK (cool all 6)" CR
HERMES-TICK
." >>> HERMES-TICK ok" CR

( Zero heat on all 6 and reap )
MSG-ARENA MSG-SCAN !
6 0 DO
  0 MSG-SCAN @ MSG-HEAT!
  MSG-SCAN @ MSG-CELLS CELLS + MSG-SCAN !
LOOP
MSG-REAP
." >>> MSG-ARENA clean (expect 0): " MSG-ARENA MSG-TYPE@ . CR
." >>> === Tripod integration complete ===" CR
BYE

MSG-SEND calls MSG-DELIVER which calls VM-EXEC. With the test shim in place, each send prints [Hera recv], [Hermes recv], or [Artemis recv] to stdout immediately at send time — no async; hosted FORTH is synchronous.


Expected Output

>>> Tripod integration test
>>> [1] Hera->Hermes SPAWN-EVENT
[Hermes recv]
>>> [2] Hera->Artemis store request
[Artemis recv]
>>> [3] Hermes->Hera delivery confirm
[Hera recv]
>>> [4] Hermes->Artemis log store
[Artemis recv]
>>> [5] Artemis->Hera ACK
[Hera recv]
>>> [6] Artemis->Hermes route result
[Hermes recv]
>>> HERMES-TICK (cool all 6)
>>> HERMES-TICK ok
>>> MSG-ARENA clean (expect 0): 0
>>> === Tripod integration complete ===

The test script greps for >>> lines to confirm the sequence completed. The [VM recv] lines confirm dispatch fired on the correct path.


Pass / Fail Criteria

Check Pass condition
6 [VM recv] lines appear in the right order Hermes, Artemis, Hera, Artemis, Hera, Hermes
HERMES-TICK completes without error Line >>> HERMES-TICK ok present
Arena clean after reap >>> MSG-ARENA clean (expect 0): 0
Script exit code 0 grep matched at least one >>> line

Block Size Note

The test shim + 6-path sequence shown above totals approximately 580 bytes combined. Split into two smoke blocks as with the existing test (Block 9996 = shim + CD-INIT, Block 9997 = 6 sends + tick + reap + BYE). Each well under 1024 bytes.


What This Does NOT Test

  • True async inter-process delivery (hosted FORTH is single-threaded; VM-EXEC fires synchronously inside MSG-DELIVER)
  • K≡1.0 fleet accounting across VM boundaries (that requires the kernel Tripod with real separate VM instances and a fleet K observer)
  • Channel negotiation (CH-REQUEST → CH-ACCEPT → ephemeral channel lifecycle)
  • Hera lifecycle decisions driven by fleet K telemetry

Those belong to the kernel integration test (three-arch QEMU boot). This test validates the Hermes dispatch substrate — that messages reach the named VM and the thermal machinery cleans up correctly.


Implementation Vehicle

tools/hermes_tripod_smoke.sh — same structure as tools/hermes_smoke.sh:

  1. Back up capsules/core/init.4th
  2. Concatenate: core stubs + VM-EXEC shim block + Hermes capsule + 6-path test blocks
  3. Run ./build/amd64/standard/starforth --log-none 2>&1 | grep '^>>>'
  4. Restore capsules/core/init.4th on EXIT/INT/TERM

Say the word and this gets built.