Tether Interaction
You Are the Tether
Section titled “You Are the Tether”You are the thread back to base reality. Psychonauts work autonomously within the annihilation, but you have final say over questions, drifts, pipeline mutations, and code grounding.
Question Queue
Section titled “Question Queue”When a psychonaut needs human input, it invokes the ask_user tool, which calls QuestionQueue.ask/2. This blocks the psychonaut until the Tether answers or the question times out.
Question Struct
Section titled “Question Struct”%Annihilation.Tether.Question{ id: "q_abc123", agent_id: "agent_xyz", bead_id: 42, burst_id: "burst_20260227T103045_001", text: "Should I use PostgreSQL or SQLite for the user store?", context: "The requirements mention multi-region...", priority: :normal, # :critical | :high | :normal | :low timeout: 120_000, # 2 minutes default status: :pending, # :pending | :answered | :timed_out answer: nil, asked_at: ~U[2026-02-27 10:30:45Z], answered_at: nil}Priority Ordering
Section titled “Priority Ordering”Questions are sorted by priority (critical > high > normal > low), then by submission time (oldest first). The TUI Tether mode shows them in this order.
- Psychonaut calls
ask_usertool ->QuestionQueue.ask/2blocks the agent process - Question appears in TUI Tether mode with priority badge
- Event broadcast:
"tether:reaching"->{:question_asked, question} - Answer path: Tether types response ->
QuestionQueue.answer/3-> agent unblocks with{:ok, answer_text} - Timeout path: Timer fires -> agent receives
{:timed_out, question}-> agent proceeds with an assumption (drift)
Answering
Section titled “Answering”QuestionQueue.answer(question_id, "Use PostgreSQL for multi-region support")# -> :ok (answered in time)# -> {:late, question} (question already timed out -- triggers late beacon handling)# -> {:error, :not_found}Assumptions Ledger
Section titled “Assumptions Ledger”When a psychonaut times out waiting for an answer, it records its assumption in the AssumptionsLedger.
AssumptionMade Struct
Section titled “AssumptionMade Struct”Each drift records:
agent_id,bead_id,burst_id— contextquestion_id— the original timed-out questiontext— what the psychonaut assumedreason— why it chose this assumptionstatus—:drifting|:confirmed|:rejected|:supersededcorrection_bead_id— created when rejected or superseded
Drift Lifecycle
Section titled “Drift Lifecycle”Question times out | v :drifting (assumption recorded) | +----+----+----+ | | | v v v:confirmed :rejected :superseded(Tether (Tether (late beacon agrees) disagrees) arrived)Persistence
Section titled “Persistence”Drifts are persisted as append-only JSONL at .annihilation/assumptions.jsonl. The AssumptionsLedger GenServer maintains an in-memory index loaded from file on startup. Every mutation (create, update) is appended as a new line.
Drift Review Actions
Section titled “Drift Review Actions”The Tether can act on drifts via DriftReview:
Ground (Confirm)
Section titled “Ground (Confirm)”DriftReview.ground(drift_id, note: "Good assumption")Confirms the psychonaut’s assumption was correct. Sets status to :confirmed. Broadcasts {:drift_confirmed, info} on "tether:drifts".
Reject
Section titled “Reject”DriftReview.reject(drift_id, "Should have used PostgreSQL, not SQLite")The assumption was wrong. Creates a correction bead automatically:
- Title: “Correct rejected drift: {question_summary}”
- Labels:
["drift-correction", "tether-rejected"] - Priority: 1 (high)
- Description includes the original assumption, reason, and Tether’s correction
Sets status to :rejected. The correction bead will be picked up in the next burst.
DriftReview.note(drift_id, "Worth discussing in the next standup")Adds a note without changing status. Notes can be added to drifts in any status.
Late Beacon Handling
Section titled “Late Beacon Handling”When the Tether answers a question after it has timed out, the LateBeaconHandler processes it:
QuestionQueue.answer/3returns{:late, question}- Event broadcast:
"tether:reaching"->{:late_answer, question, answer_text} LateBeaconHandlerreceives the event- Looks up the corresponding drift via
AssumptionsLedger.find_by_question_id/2 - Creates a correction bead (similar to rejection, but with “superseded” context)
- Updates drift status to
:superseded - Broadcasts
{:drift_superseded, info}on"tether:drifts"
If the drift does not exist yet (race condition), the late answer is buffered in pending_late_answers and applied when the drift creation event arrives.
Configuration
Section titled “Configuration”config :annihilation, :assumptions, question_timeout: 120_000, # 2 minutes auto_create_correction_beads: true # auto-create beads on late answersPipeline Drift
Section titled “Pipeline Drift”Pipeline mutations (via set_pipeline and create_pipeline tools) go through the Pipeline.GroundingQueue:
- Psychonaut proposes a pipeline change
- Mutation enters the grounding queue with a timeout (default 2 minutes)
- Tether can:
{:approve, _}— apply as-is{:reject, reason}— do not apply{:modify, modified_value}— apply with modifications
- If timeout expires, the mutation drifts — applied as an assumption and recorded in the
AssumptionsLedger
Pipeline drifts can be reviewed via PipelineDriftReview using the same ground/reject pattern.