Files
Robert Allan JamesandClaude Sonnet 5 c5442c8377 docs: mark TRIPOD/HERMES/ARTEMIS/CONSOLE.md superseded by FABRIC.md/FABRIC-2.md
Captain Bob ruled directly: all four subsystem docs are superseded, not
individually assessed for partial staleness case-by-case. FABRIC.md
(design history) and FABRIC-2.md (current/living) are the sole
design-of-record for Tripod/Hermes/Artemis/Console work now.

Added a superseded-header banner to the top of all four .claude/*.md
files, pointing to FABRIC.md/FABRIC-2.md. Corrected .claude/CLAUDE.md's
own pointer paragraph, which previously claimed these four were
individually "authoritative" for their subsystems - that's now wrong.

Closes FABRIC-2.md's three open documentation questions (CONSOLE.md's
fate, HERMES.md's stale block map, 5.3's larger shrink-the-docs ask) at
once: the header approach makes reconciling a superseded document's
internal accuracy moot, and accomplishes what "shrink to a pointer" was
already trying to do.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 09:26:59 -04:00

12 KiB
Raw Permalink Blame History

HERMES.md — Hermes VM Architecture

StarshipOS / StarForth — Captain Bob (Robert Allan James)

This document constrains Claude Code behavior. Read it completely before touching Hermes.


SUPERSEDED (Captain Bob, 2026-08-15). This document is no longer authoritative. FABRIC.md/FABRIC-2.md (repo root — read FABRIC-2.md first, it is the current/living one) are the sole design-of-record for Hermes work now. Kept here as historical record only — note its message-layout table (8 cells) is confirmed stale against the real shipped capsule (9 cells, capsules/hermes/init.4th), see FABRIC-2.md. Do not read this for current design authority. See .claude/CLAUDE.md's own pointer.


Language Constraint — Non-Negotiable

All Hermes implementation is in StarForth dialect ONLY.

This includes:

  • Data structures
  • Constants
  • Variables
  • Control flow
  • Everything

C99 is forbidden unless explicitly blocked in StarForth AND explicit written permission is given by Captain Bob for that specific construct. "I could not figure out how to do this in StarForth" is not permission. Ask first. Wait for the answer.

If you are about to write a C struct, a #define, or a C variable — stop. Implement it in StarForth or ask.


What Hermes Is

Hermes is the messenger. He moves messages between VMs and manages channels. That is his complete contract.

Hermes does not store persistent data — that is Artemis. Hermes does not make lifecycle decisions — that is Hera. Hermes does not route by load or heat — dispatch is capability-based, always.

Capsules are baked into the OS blob by mkcapsule.c at build time. Hermes has no capsule cache, no fetch logic, no repository concern. That is a future chapter.


The Event Loop

Hermes runs a compudynamic event loop. Every message is a compudynamic object:

  • Born hot (full heat at creation)
  • Cools each heartbeat tick
  • ACK received → clean death, K redistributed to fleet
  • NACK received → requeue with reduced heat, or reaped if heat floor reached
  • TTL expires (heat → 0) → Hermes reaps the message, K rebalanced

There is no special timeout logic. The physics handles it. A message that nobody answers simply cools to death.


Message Structure

Every message carries:

type        — work type tag (determines recipient)
sender      — originating VM identity
recipient   — destination VM identity
payload     — message body
heat        — current thermal value (compudynamic)
sequence    — monotonic sequence number

Point-to-point only. Sender addresses recipient directly. Hermes delivers and tracks thermal state. He does not inspect payload.


ACK/NACK Protocol

  • ACK → message dies cleanly, K redistributed
  • NACK → message requeued with reduced heat
  • No response → message cools naturally to death via TTL

The binary receipt of a response IS the ACK where applicable. No separate acknowledgement frame needed in those cases.


PubSub — Channels

Hermes implements a publish/subscribe model. Topics are called channels.

The Common Channel

There is one permanent channel: COMMON.

  • Always exists, always hot
  • Any VM can publish to it
  • Used for negotiation and channel establishment only
  • Not for business transactions — those happen on ephemeral channels

Ephemeral Channels

Ephemeral channels are compudynamic objects — born hot, reaped when cold or closed.

Channel structure:

channel_id    — owner VM identity + monotonic sequence number
owner         — VM that created the channel (always the responder)
members[]     — participant list
heat          — compudynamic, born hot at creation
state         — NEGOTIATING | OPEN | CLOSING

The Negotiation Protocol

1. X publishes request on COMMON — born hot, TTL ticking
2. Y sees request, responds yes/no on COMMON
   — No response → message cools to death → implicit NACK to X
3. Y accepts → Y creates ephemeral channel
             → Y publishes channel_id to X over COMMON
4. X and Y conduct business on ephemeral channel
5. Business concluded → owner Y reaps channel → K rebalanced

Y owns channel creation and destruction. Always the responder, never the requester. A channel dying of cold (abandoned transaction) is not an error — the physics handles it.

Multi-Party Channels

Channels are bilateral by default. The owner may invite additional VMs:

1. Owner publishes invite to candidate VM on COMMON
2. Candidate accepts or declines on COMMON — same TTL/cool protocol
3. Accept → candidate added to members[]
4. Decline or timeout → candidate never joins, channel continues bilateral

Multi-party is designed for but not implemented yet. Do not implement invite logic until explicitly instructed. The members[] structure must exist from the start to avoid future redesign.

Channel Identity

channel_id = owner VM identity + monotonic sequence number. Unforgeable by non-owners. Hermes mints it. No VM constructs its own channel_id.

Compudynamic Invariant for Channels

Channels participate in K≡1.0. An open channel with active messages holds heat. Channel reap must redistribute K correctly to the fleet. A channel cannot be reaped while messages on it are still in flight — drain first, then reap.


What Is Not Hermes

  • Capsule cache — future chapter, not now
  • Remote fetch — future chapter, not now
  • UDP server / client — future chapter, not now
  • Instance authentication — future chapter, not now
  • Persistent block storage — that is Artemis
  • VM lifecycle decisions — that is Hera
  • Heat-based routing — wrong model, never implement

Compudynamic Invariant

Every object Hermes manages — messages and channels — participates in K≡1.0. Fleet K includes the thermal contribution of in-flight messages and open channels. Hermes reaping a message or channel must rebalance K correctly.

Do not write code that breaks K≡1.0 and add a comment explaining why it's okay.


Current Scope

  1. Compudynamic event loop — message emit, drain, TTL, ACK/NACK, reap
  2. COMMON channel — permanent, always hot, negotiation only
  3. Ephemeral channels — compudynamic lifecycle, owner=responder, bilateral default
  4. Channel negotiation protocol — request on COMMON, Y creates, Y reaps
  5. members[] structure — present from the start, multi-party invite not yet implemented

Capsule management is a future chapter. Remote repository is a future chapter. Multi-party channel invite is a future chapter.


v1 Known Scope Gaps

Named, bounded limitations. Logged here to prevent future confusion and make the upgrade path explicit. None of these are defects in v1.

Synchronous delivery — RESOLVED (G1)

MSG-SEND now queues only; HERMES-TICK drives delivery via MSG-DELIVER-ALL as its first step. Delivered messages are marked MSG-DELIVERED (type=255) so the scanner skips them on the next tick. MSG-REAP clears them when heat reaches zero.

G2/G4: ACK/NACK server implemented; NACK-requeue deferred

MSG-ACK-LAST and MSG-NACK-LAST live in block 4121. MSG-DELIVER stores the current message pointer in MSG-LAST-MSG before calling VM-EXEC; the target VM's handler calls HERMES-ACK / HERMES-NACK (from common:msg.4th) which use VM-EXEC back into Hermes to invoke the server words — no cross-VM stack passing required. MSG-DELIVER-ALL guards the MSG-DELIVERED stamp so an ACK inside the handler does not corrupt the freed slot.

v1 NACK = immediate reap (same as ACK). NACK-requeue (reduced heat, retry) is deferred: it requires preserving the original message type through delivery, which needs either an extra cell (MSG-CELLS=9) or a side table. Neither is warranted until a real NACK-retry use case appears.

MSG-REAP ordering bug — FIXED

MSG-REAP (block 4109) previously called MSG-FREE-NODE before clearing the type field. MSG-FREE-NODE writes old_free_head into cell[0]; the subsequent MSG-TYPE! then overwrote cell[0] with 0, severing the free list.

Fixed: type is cleared first (0 MSG-SCAN @ MSG-TYPE!), then the node is prepended to the free list (MSG-SCAN @ MSG-FREE-NODE). Matches the correct order already used by MSG-ACK-LAST/MSG-NACK-LAST in block 4121.

Payload size coupled to block size

MSG-PADDR/MSG-PLEN carry FORTH string pointers. Payloads from S" literals in block source are bounded by block size (1024 bytes). No logical-block spanning is implemented. No real Hermes script has hit this ceiling yet — revisit when something forces the issue.

G8: HERMES-K implemented; K-FLEET integration deferred

HERMES-K ( -- q48 ) = MSG-TOTAL-HEAT + CH-TOTAL-HEAT is implemented in Hermes's VM (block 4116) and gives the total thermal mass of all in-flight messages and active channels. Wiring it into K-FLEET in Hera requires a cross-VM return value — something the current synchronous VM-EXEC model cannot deliver. Full K≡1.0 accounting (K-FLEET includes Hermes thermal mass) is deferred until the async inter-VM model supports return values (G1 path).

IDX>NAME and CH-ACCEPT owner hardcoded for 3-VM Tripod

IDX>NAME maps 0→Hera, 1→Hermes, 2→Artemis only. CH-ACCEPT writes 1 OVER CH-OWNER! unconditionally. Correct for v1 Tripod; generalization needed if Tripod grows beyond three VMs.

G10: EVENT-WAIT reads raw arena slot 0

EVENT-WAIT returns MSG-ARENA MSG-TYPE@ — the type field of slot 0, whatever it happens to hold. With the async model this is unreliable: slot 0 may be pending, delivered (type=255), or free (type=0) depending on allocation order. EVENT-EMIT and EVENT-DRAIN have been corrected (G10): EMIT delegates to HERA-NOTIFY-*; DRAIN is a no-op (MSG-REAP owns cleanup). EVENT-WAIT is retained as a diagnostic peek but must not be used for correctness decisions.

Terminology: heat-gated reclamation, not mark-and-sweep

Channel and message reaping is heat-gated reclamation: objects are freed only when compudynamic heat decays to zero. This serializes reaping against active traffic on that object only — it is not stop-the-world for the VM as a whole. Do not apply GC vocabulary (mark-and-sweep, generational, root-set tracing) to this mechanism; those terms carry expectations the design never intended.


v1 Block Map — LOCKED

Blocks 41104113 are Artemis. NEVER touch them.

4100  Constants: event codes, channel states, node sizes, arena sizes, MSG-DELIVERED
4101  Arena CREATE: MSG-ARENA CH-ARENA MBR-ARENA; free-list vars; MSG-SEQ CH-ACTIVE
4102  MSG-INIT-FREE + CH-INIT-FREE
4103  MBR-INIT-FREE + MSG-ALLOC + MSG-FREE-NODE
4104  CH-ALLOC + CH-FREE-NODE + MBR-ALLOC + MBR-FREE-NODE
4105  Message field accessors: MSG-TYPE@/! MSG-FROM@/! MSG-TO@/! MSG-PADDR@/! MSG-PLEN@/! MSG-HEAT@/! MSG-SEQ@/!
4122  Message field accessors cont.: MSG-CH@/!
4106  Channel+member accessors: CH-ID@/! CH-OWNER@/! CH-STATE@/! CH-HEAT@/! CH-MBRS@/! CH-NEXT@/! MBR-NEXT@ MBR-VM@
4107  VARIABLE MSG-LAST-MSG + IDX>NAME + MSG-DELIVER
4123  MSG-SEND
4108  VARIABLE MSG-SCAN + MSG-COOL-ONE + MSG-COOL-ALL
4124  MSG-TOTAL-HEAT
4128  MSG-DELIVER-ALL
4109  MSG-REAP
---- 41104113: ARTEMIS — DO NOT TOUCH ----
4114  VARIABLE CH-SCAN + CH-COOL-ALL + CH-TOTAL-HEAT
4115  CH-REAP-SAFE
4116  VARIABLE COMMON-CH + COMMON-INIT + HERMES-TICK
4125  HERMES-K + WELCOME (LOG-INFO at load)
4117  EVENT-EMIT + EVENT-WAIT + EVENT-DRAIN (backward compat)
4118  HERA-NOTIFY-SPAWN + HERA-NOTIFY-KILL
4119  CH-MINT-ID + CH-REQUEST
4126  CH-ACCEPT + CH-CONFIRM + CH-CLOSE
4120  CD-INIT (loads lib.4th + common:msg.4th; LOG-INFO" Hermes: ready")
4121  MSG-ACK-LAST + MSG-NACK-LAST
4127  MSG-USED + CH-USED + HERMES-STATUS

Node layouts (cells)

Message node — 8 cells:

  • 0: type (in-use) / next-free ptr (free)
  • 1: sender VM index
  • 2: recipient VM index
  • 3: payload addr (FORTH string addr)
  • 4: payload len
  • 5: heat (Q48.16)
  • 6: seq
  • 7: channel ptr (0 = no channel)

Channel node — 6 cells:

  • 0: channel-id (in-use) / next-free ptr (free)
  • 1: owner VM index
  • 2: state (0=NEGOTIATING 1=OPEN 2=CLOSING)
  • 3: heat (Q48.16)
  • 4: members-head (→ member list)
  • 5: next-active (active channel list link)

Member node — 2 cells:

  • 0: next-ptr
  • 1: vm-id

Physics

Cooling constant: Q-DECAY (65208) from compudynamics.4th — same as VM-DECAY-ONE. Messages and VMs cool at identical rates. Fleet is thermodynamically consistent.


This document is authoritative. If it conflicts with something in the codebase, the codebase is wrong.