Turn pipeline
Every AsteronIris surface that executes a companion turn converges on the same turn contract. That sentence is easy to say and costly to maintain; it is also the mechanical backbone of the continuity claim. Discord text is the product-proven channel. Other channels and operator surfaces reuse the contract where they create or replay turns, but they are not all equally mature product surfaces.
The shared path
Section titled “The shared path”[Accepted turn: Discord / CLI / gateway / operator surface] │ ▼ Pickup / ingress policy │ ▼ Turn enrichment affect detection → memory recall → persona context │ ▼ Response assembly + tool loop │ ▼ Pre-send verification │ ▼ Reply delivery │ ▼ Post-turn update relationship memory · persona drift · continuity cuesThe transport-facing path lives in src/runtime/services/companion_turn.rs. Pre/post-turn enrichment is owned by src/core/agent/turn_enrichment.rs. The tool loop runs every tool call through a fixed middleware chain:
SecurityMiddleware → TaintMiddleware → AuditMiddleware → EntityRateLimitMiddlewareWhy a single pipeline
Section titled “Why a single pipeline”Three reasons the project pays the maintenance cost of unification:
- Continuity cannot silently diverge by surface. If Discord-side enrichment and CLI-side enrichment drifted apart, the companion would become a different entity depending on where you talked to it. That is the exact failure mode the runtime is built to prevent.
- Verification is a choke point, not a decoration. Pre-send verification has to be unskippable. A second pipeline is a second chance to forget to run it.
- Behavior is reproducible. The same accepted turn walks the same code regardless of where it originated. That makes evals, replay, and incident triage possible; otherwise every surface is its own debugging surface.
Ingress policy lives at the edge
Section titled “Ingress policy lives at the edge”Not every inbound event becomes a turn. The pickup / ingress policy — run before enrichment — decides:
- Is this message directed at the companion, or ambient chatter?
- Has the companion already said enough in this channel recently?
- Is the room public, private, or a thread, and does that change the distance the companion should keep?
That decision is intentionally before memory recall and persona context. A companion that runs enrichment on every inbound event burns budget and also reads as eager and boundary-less. The pickup gate is a character constraint as much as a performance one.
The tool loop is a subroutine, not the product
Section titled “The tool loop is a subroutine, not the product”When a tool call is needed mid-turn, the tool loop spins inside response assembly. It is governed, audited, rate-limited, and taint-tracked. None of that leaks into the “shape” of the turn from the user’s perspective; a turn with tool calls and a turn without both exit through the same pre-send verifier and the same post-turn update.
This is deliberate. A companion that visibly switches modes — “now I am an agent, now I am chatting” — has two personalities. AsteronIris has one.