Architecture¶
you (phone) you (terminal / Claude app)
│ Telegram │
▼ ▼
┌──────────── bot (anna-tg) ────────────┐ ┌──────── brain (anna-brain) ─────────┐
│ cheaper model · chat, quick checks, │ relay │ full model · the record, the hosts, │
│ allowlisted actions · deny-by-default │◀──────▶│ real work · rolls over daily │
└───────────────────────────────────────┘ └─────────────────────────────────────┘
│ ▲ │ ▲
▼ │ anna-stream (the day) ▼ │
┌──────────────────────────────────────────────────────────────────────────────────┐
│ stream · clock · checks · senses · notify (bin/, state in paths.state) │
└──────────────────────────────────────────────────────────────────────────────────┘
│ ▲
▼ │
┌──────────────── the record (git) ─────────────────┐ ┌── headless runs (anna-run) ──┐
│ journal · hosts · runbooks · memory · planning · │◀──│ morning check · evening fold │
│ clock · checks · senses · bot/context │ │ clock tasks · deny by default │
└───────────────────────────────────────────────────┘ └──────────────────────────────┘
The pieces¶
| Piece | Files | What it does |
|---|---|---|
| config | bin/anna-config, config/anna.example.toml |
one TOML file; every script reads it; defaults for everything |
| stream | bin/anna-stream (+ hook) |
short-term memory: a JSONL log per day; each session sees what the others did since its last turn |
| clock | bin/anna-clock, systemd/anna-clock.* |
notes, pings and headless tasks with dates, in the record (clock/YYYY-MM.md); a 5-minute tick fires them |
| brain | bin/anna-brain, bin/anna-day, systemd/anna-day-* |
the long-running session in screen; a resume loop for stray exits; close (wrap up overnight) and open (fresh session) |
| hooks | hooks/ |
session start (what happened while away), session drafts (raw, redacted journal material), idle checkpoint |
| headless | bin/anna-run, bin/anna-evening, config/headless-settings.json.in |
claude -p with a deny-by-default allowlist; reports to journal/handoffs/ |
| skills | skills/anna-* |
setup, morning, wrap-up, checkpoint, evening fold |
| bot | bin/anna-tg, bot/ |
a channel session (Telegram by default; another app); prompt template + your bot/context/ |
| relay | bin/anna-relay, docs/relay.md |
bot ↔ brain over two fakechat channels and a file queue |
| notify | bin/anna-notify |
Telegram messages from scripts (alerts, clock pings); logged to the stream |
| checks | bin/anna-checks + record/checks/ |
read-only checks: OK / ⚠ / ⛔ lines |
| senses | bin/anna-body + record/senses/ |
how she feels: JSON sensations, one summary line |
| engines | agents/anna-*.md → ~/.claude/agents/ |
subagents the brain calls: memory, interpretation, epistemic, action-gate, goals, curiosity (sensors: they return a block, the brain acts) |
| gate hook | hooks/anna-gate-hook |
PreToolUse: in the brain, a state change needs a recent go from the action-gate engine |
| engine inputs | bin/anna-rhythm, bin/backlog-age |
precomputed numbers (active windows, gaps, ages) so no model does date arithmetic |
Engines and their order¶
The engines are Claude Code subagents with short prompts, fixed output blocks and no tools that change anything. The brain
decides when to call them (~/CLAUDE.md says when); most turns call none.
you → bot ── chat / allowlisted actions / quick check ──→ answers itself (the bot has no engines)
└─ real work → relay → brain
1. interpretation what of the context belongs in the reply; stale → verify; ask, don't guess
2. memory when it touches the past or a host's history
3. action-gate before each state change ← enforced by anna-gate-hook
4. act
5. epistemic on the result before it goes back
← relay ← bot says it in its own voice
you → brain directly: the same, without the relay
morning run: anna-clock due · backlog-age · anna-rhythm → goals → curiosity → the report (+ "Question for today")
Only the gate is enforced by code. Interpretation and the epistemic check are judgment calls and stay instructions. The
goals engine works without a goal tool (goal_status: none): it still returns Anna's own due follow-ups. The memory engine
uses merlin's search when that MCP server is configured, and reads the record's files otherwise.
A day¶
- Night (
day.close): if the brain has been idle,anna-day closetypes/anna-wrapup overnightinto it, waits for the wrap-up to finish, then exits it. Relay hand-offs wait meanwhile. runs.evening:anna-eveningdrafts any session the hooks missed and runs/anna-eveningheadless: the drafts and yesterday's stream are folded into the journal; record changes are only proposed.runs.morning:anna-run /anna-morning: a read-only check of the handoff, the journal, the clock andanna-checks, plus the goals and curiosity engines (Anna's own to-dos, one question for the day). A ⛔ notifies you.day.open:anna-day openstarts a fresh brain session for the day; queued hand-offs go in.- During the day: you talk to the bot or the brain; the stream keeps them in step; the clock fires what's due; long quiet spells trigger a checkpoint.
Tests¶
tests/run-all runs tests/test_*.sh and bun tests/test_relay.ts (exit 1 if any fails), each suite in a throwaway config, state, record and (where needed) HOME:
nothing touches a live setup. Pieces that need a real Claude session (the rollover's keystrokes, the bot's channels) are
covered by their logic tests plus a manual check on a real machine.
Where it lives in this repo¶
bin/ scripts: anna-config, -stream, -clock, -relay, -brain, -day, -run, -evening, -notify, -tg, -checks, -body, -rhythm, backlog-age
hooks/ Claude Code hooks (session start, drafts, idle checkpoint, the action-gate hook) + the settings snippet install.sh merges
agents/ the engines (subagents): memory, interpretation, epistemic, action-gate, goals, curiosity
skills/ Claude Code skills: anna-setup, anna-morning, anna-wrapup, anna-checkpoint, anna-evening
.claude/skills/ anna-setup only (a symlink), so `claude` in a fresh clone offers /anna-setup before install
bot/ the Telegram bot's prompt template and deny-by-default settings template
systemd/ user units and timers (timers rendered from your config times)
config/ anna.example.toml (every setting, with defaults), settings templates, the brain's ~/CLAUDE.md template
record-template/ an empty record to start from (conventions written down)
examples/ starter checks and senses
docs/ this site (getting-started/, guides/, reference/, concepts/); mkdocs.yml builds it
tests/ one suite per piece, each in a throwaway config/state/record