Skip to content

Architecture

Steven Enamakel edited this page Sep 1, 2026 · 5 revisions

Architecture

Three crates, split by one question: does answering require waiting on something?

crates/
├── tinyhivemind-core/   the pure algebra. No async, no IO, no host types.
├── tinyhivemind/        the session runtime. Ports a host implements.
└── tinyhivemind-hive/   bounded group deliberation. Pure, opt-in.

If a question can be answered from its arguments, it lives in the core crate. If it has to await a read, a write, or a model call, it lives behind a port in the runtime crate. When the answer is unclear, the decision goes in core and the waiting goes in the runtime. That split is what makes the interesting logic testable without a fixture.

tinyhivemind-core

The algebra. Desks and membership, the roster, the mention grammar and its resolution, the responder ladder's decisions, and the fold that projects a shared transcript into one viewer's turn history.

module what it holds
chat conversation identity, and the four stored spellings that mean the default desk
desk desk records and the borrowed overlay fold behind DeskSet
dispatch bounded selection of at most one mentioned child turn
mention mention parsing and pure routing choices
referral at most one child turn, which may run on another desk
roster borrowed agent and person identity snapshots
responder deterministic selection of one agent for one message
error the crate-wide Error and Result

This crate sits on the hot path of every agent turn, so it has to compile in a host's default build with no feature flags behind it. It may not depend on an async runtime, a transport, an HTTP client, a web framework, a SQL client, a git implementation, or anyhow. .github/scripts/assert-pure.sh asserts that in CI, and the enumerated list in that script is the source of truth.

The constraint is not only about compile cost. A crate that cannot call out cannot grow a path back into its host, which is the layering violation this repository exists to fix.

tinyhivemind

Everything that has to wait, expressed against ports a host implements.

module what it holds
session the SessionLog port, the paging walk, and attributed projection
threads a bounded, recency-ordered index of a desk's live threads
sharing continuous transcript sharing over a caller-owned watermark
responder the Selector port and model-assisted responder choice
dispatch the mention-dispatch edge and the MentionTurnQueue boundary
referral the cross-channel edge and the ReferralQueue boundary
briefing ephemeral team initialization, and typed per-turn context

It depends on the core crate and re-exports it, so a host takes one dependency instead of two and the types crossing the boundary are the same types rather than structural twins.

Four ports, and nothing else, is the whole surface a host implements: SessionLog, Selector, MentionTurnQueue, and — only if it wants agents to reach across channels — ReferralQueue.

tinyhivemind-hive

The swarm mechanisms. It answers a different question from the other two: not who responds to this message, but how a room of agents reaches a decision at all. Stigmergic traces, decaying salience, quorum sensing, cross-inhibition, and a response-threshold bid for the floor. Further reading traces each one to its source.

module what it holds
trace the marker grammar and its read
salience recency decay, importance, and relevance
quorum standings, cross-inhibition, and the consensus predicate
attention the bid each member makes for the floor, and the argmax
episode the state machine and the visibility filter

It defines no port. An episode is step(state, transcript, roster, desks, policy) -> HiveStep, a fold over arguments the caller already holds, and the host does its waiting through the three ports it already implements. All arithmetic in it is fixed-point integer, so every payload derives Eq and every fold reproduces.

See Hive episodes.

Out of scope

No second journal, no host types, no fan-out, no releases. See ADR 0002 for the fan-out argument and FAQ for the rest.

Clone this wiki locally