236 lines
7.3 KiB
Markdown
236 lines
7.3 KiB
Markdown
# 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.
|
|
|
|
```forth
|
|
( 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:
|
|
|
|
```forth
|
|
: 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
|
|
|
|
```forth
|
|
( 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.
|