111 lines
4.1 KiB
Markdown
111 lines
4.1 KiB
Markdown
# Hermes Channel Negotiation Smoke Test — Results
|
||
|
||
**Date:** 2026-06-27
|
||
**Branch:** `lithosananke`
|
||
**Script:** `tools/hermes_channel_smoke.sh`
|
||
**Status:** PASS — 16/16
|
||
|
||
---
|
||
|
||
## Summary
|
||
|
||
Full channel negotiation lifecycle validated:
|
||
CH-REQUEST → CH-ACCEPT → business messaging → CH-CLOSE → HERMES-TICK (hot, no reap) → zero heat → HERMES-TICK (reap) → CH-ACTIVE = COMMON-CH only.
|
||
|
||
---
|
||
|
||
## Output
|
||
|
||
```
|
||
>>> [1] CH-REQUEST Hera->Hermes negotiate
|
||
>>> [2] CH-ACCEPT (Hermes creates ephemeral ch)
|
||
>>> [3] CH-STATE (expect 1=OPEN): 1
|
||
>>> [4] CH-ID (expect non-0): 2
|
||
>>> [5] CH-OWNER (expect 1=Hermes): 1
|
||
>>> [6] Y sends chanid to X on COMMON
|
||
>>> [7] COMMON-CH OPEN (expect 1): 1
|
||
>>> [8] business msg on ephemeral ch
|
||
>>> [9] CH-CLOSE
|
||
>>> [10] CH-STATE (expect 2=CLOSING): 2
|
||
>>> [11] HERMES-TICK (hot channel, no reap yet)
|
||
>>> [12] CH-STATE after tick (expect 2=CLOSING): 2
|
||
>>> [13] Zero heat + HERMES-TICK (reap)
|
||
>>> [14] COMMON-CH OPEN (expect 1): 1
|
||
>>> [15] CH-ACTIVE is COMMON-CH (expect 1): -1
|
||
>>> === Channel negotiation smoke complete ===
|
||
```
|
||
|
||
---
|
||
|
||
## Validation Results
|
||
|
||
| # | Check | Result |
|
||
|---|-------|--------|
|
||
| 1 | test started | PASS |
|
||
| 2 | CH-ACCEPT ok | PASS |
|
||
| 3 | ch state OPEN | PASS |
|
||
| 4 | ch id non-zero | PASS |
|
||
| 5 | ch owner Hermes | PASS |
|
||
| 6 | chanid sent | PASS |
|
||
| 7 | COMMON still OPEN | PASS |
|
||
| 8 | business msg | PASS |
|
||
| 9 | CH-CLOSE called | PASS |
|
||
| 10 | state CLOSING | PASS |
|
||
| 11 | tick no reap | PASS |
|
||
| 12 | still CLOSING after tick | PASS |
|
||
| 13 | zero+tick reap | PASS |
|
||
| 14 | COMMON still OPEN after | PASS |
|
||
| 15 | CH-ACTIVE is COMMON-CH | PASS |
|
||
| 16 | smoke complete | PASS |
|
||
|
||
**Result: 16 passed, 0 failed**
|
||
|
||
---
|
||
|
||
## Architecture Notes
|
||
|
||
**Protocol path confirmed:**
|
||
|
||
| Step | Actor | Action |
|
||
|------|-------|--------|
|
||
| 1 | Hera (0) | CH-REQUEST on COMMON → MSG-SEND with SPAWN-EVENT type |
|
||
| 2 | Hermes (1) | CH-ACCEPT → ephemeral channel allocated, ID=2, OWNER=1, STATE=OPEN |
|
||
| 3–5 | — | Verify ch fields: STATE=1, ID=2 (non-zero), OWNER=1 |
|
||
| 6 | Hermes | Sends ch ID to Hera on COMMON via MSG-SEND |
|
||
| 7 | — | COMMON-CH still OPEN after in-use |
|
||
| 8 | — | Business message on ephemeral channel |
|
||
| 9–10 | Hermes | CH-CLOSE → STATE becomes CH-CLOSING (2) |
|
||
| 11–12 | — | HERMES-TICK with heat > 0: channel not reaped, STATE still 2 |
|
||
| 13 | — | Zero heat manually; HERMES-TICK reaps ephemeral channel |
|
||
| 14–15 | — | COMMON-CH still OPEN; CH-ACTIVE = COMMON-CH only |
|
||
|
||
**CH-REAP-SAFE rebuild-the-list pattern:**
|
||
Resets CH-ACTIVE to 0 at the start of the walk, then re-inserts non-reaped channels. Freed channels are simply skipped. This avoids the prior double-free bug where freed nodes remained linked in CH-ACTIVE.
|
||
|
||
**CH-ACCEPT stack fix:**
|
||
`CH-ACTIVE !` → `DUP CH-ACTIVE !` — the original consumed the only copy of ch, returning an empty stack instead of `( ch )`.
|
||
|
||
**Block size constraint:**
|
||
Test sequence split across blocks 9998 (helpers + VARIABLE MYCHAN + colon words) and 9999/9000 (two-part execution), each well under the 1024-byte limit. Block 9999 alone was 1159 bytes, causing silent truncation before steps [15] and "smoke complete".
|
||
|
||
**Compile-only constraint:**
|
||
`IF THEN ELSE DO LOOP` are compile-only in StarForth — cannot appear in interpreted block context. All control flow in helpers is wrapped in colon word definitions (block 9998). The abort check `ASSERT-CH` wraps the `IF...BYE...THEN` idiom. `TRIP-ZERO-CH` wraps the zero-heat + HERMES-TICK sequence. Both are defined before `CD-INIT` so they are available when called from block 9999/9000.
|
||
|
||
---
|
||
|
||
## Bugs Fixed (prerequisite work)
|
||
|
||
| Bug | Description | Fix |
|
||
|-----|-------------|-----|
|
||
| H4 | `CH-REAP-SAFE` freed channel stayed in CH-ACTIVE → double-free on next tick | Rebuild-the-list: reset CH-ACTIVE=0, re-insert non-reaped channels only |
|
||
| H5 | `CH-ACCEPT` `CH-ACTIVE !` consumed the only copy of ch → empty stack return | `DUP CH-ACTIVE !` keeps ch on data stack |
|
||
|
||
---
|
||
|
||
## What This Does Not Test
|
||
|
||
- Multi-VM channel negotiation across separate process boundaries
|
||
- MBR (member) records within channels
|
||
- CH-MAX exhaustion (free list empty path)
|
||
- Concurrent channel lifecycle (multiple ephemeral channels)
|