01 · The runtime spine
Bridge, worker, runner
Three moving parts turn a message into execution. The bridge listens, the worker runs the queue, and the runner does the thinking. Each part keeps to its own job, and the seams between them are what make the system durable.
Every step between the human and the runner is a durable seam rather than a function call.
1 · Ingestion
The bridge only listens
The bridge connects to Telegram (via Telethon) and email (IMAP/SMTP), receives each incoming message, and hands it off. That is all it does. It claims the message, decides whether it resumes an existing session or starts a fresh one, and enqueues an AgentSession for the worker.
The bridge knows nothing about the SDLC pipeline. Keeping ingestion deliberately dumb is what lets execution live somewhere else: a session can run on a different machine than the one that received the message.
2 · Execution
The worker is the one engine
Once a session is queued, the standalone worker is the only thing that executes it. Its entry point configures logging, loads project config, and runs an async loop that pulls sessions off the queue, supervises heartbeats, and delivers output.
On startup it recovers orphaned or crashed work: it rebuilds indexes, cleans corrupted records, closes out sessions whose recorded process is gone, and re-queues the ones still owed a turn. Because the worker is a separate service, a session survives a bridge restart.
3 · The record
AgentSession, the durable envelope
Everything the worker runs is an AgentSession: the saved record of one unit of work. It holds the session's identifiers, chat history, per-stage pipeline state, and lifecycle status across 14 states. The bridge, worker, hooks, and dashboard all read and write this one record.
Because the work is a record and not a live process, it can be paused, resumed, steered, and moved between machines.
The execution record
The record also carries the identity of whatever process is running the current turn. Every spawn stamps four things onto the session: the process id, the start time the operating system reports for it, the directory it runs in, and which harness is driving it (the headless Claude Code harness today). The stamp is written before the turn begins, so a worker that dies mid-turn leaves behind a record that another process can act on.
The start time is what makes the process id worth trusting. Operating systems hand out process ids from a pool and take them back, so the same number belongs to different programs over a machine's lifetime. Reading the live start time and comparing it against the stamped one settles the question that matters before anything signals a process: is this still the one we started? A match means yes. Anything else reads as somebody else's process, and the system leaves it alone.
Stamps append to a spawn history that is only ever added to, so a session that died, resumed, and died again keeps a readable timeline. The current stamp stays in place once a turn ends, too. Keeping it is what makes staleness detectable: a stamp pointing at a finished process still says which process it was, and that is exactly what the comparison needs.
The stamp reports what it can observe, and the system treats it that way. A window remains between reading a start time and acting on the answer, and the race-free primitive that would close it exists on Linux rather than on the macOS this system runs on. So the stamp informs decisions rather than authorizing them. Recovery keys on the session's own status: the status is what says the work should still be running, and the stamp tells the recovery sweep whether a process is there to stop.
4 · Thinking
The session runner, where it thinks
The runner drives a per-turn loop. Each turn spawns a fresh claude -p subprocess through the SDK client, which composes the persona system prompt and manages the headless Claude Code harness. At each turn boundary the loop drains steering messages and routes the output onward.
One turn. The process is disposable; the record and the hook file are what persist.
Three roles, one runner
The role_driver primes one of three personas for each turn.
| Role | Owns | Character |
|---|---|---|
| PM | Orchestration. Reads the conversation, decides what the work is, and routes it. Steers the SDLC pipeline stage by stage. | Decides, delegates, reports back. |
| Dev | Execution. A resumable subagent the PM spawns once and continues across turns. Writes the code, runs the tests, opens the PR. | Full permissions, engineer persona. |
| Teammate | Conversation. Answers questions and handles non-code requests. Source-code writes redirect to an Eng session. | Open Bash, audit-logged, restricted writes. |
One identity
Whichever role a turn takes, the system speaks as one person: Valor Engels. The identity is assembled from composable persona segments (who Valor is, how it works, which tools it reaches for), rendered against structured identity data (name, email, timezone, organization) and injected into every turn's system prompt.
5 · Foundations
Configuration and personas
The runner needs to know which model to use and which persona to wear. A Pydantic settings hierarchy defines every runtime configuration group: API keys, Telegram, Redis, models, features, paths. config/models.py resolves which generation model to use based on host RAM and cloud tags. The persona documents are the operating manuals injected into each turn.
These files are foundational. config/models.py has the highest fan-in in the whole graph: nearly everything depends on it.
6 · The return path
Output routing and steering
After a turn produces output, three pieces close the loop back to the human. output_router is pure decision logic: given the stop reason and nudge counts, it decides whether to deliver, nudge, or suppress a message. The steering module is a Redis-backed inbox for injecting mid-session messages into a running session, drained at turn boundaries. The message drafter composes and validates the actual user-facing reply, extracting open questions.
A session only pauses for a genuine open question. A status update with no question gets an automatic "continue", and the work keeps moving.
7 · Self-healing
The system maintains itself
Valor maintains itself through scheduled reflections and monitoring, all running out of process, outside the request path.
The scheduler
Ticks maintenance and audit jobs on a cadence: code and docs audits, housekeeping, memory cleanup, session cleanup, briefings. The Reflections page covers this system in full.
The loop detector
Clusters recently failed sessions by error fingerprint. When it detects a failure loop, it files one deduplicated GitHub issue instead of a pile of noise.
The watchdog
A standalone health checker that detects zombie processes and crash patterns, and triggers restarts on its own authority.
A failure is either repaired in place or escalated as a single GitHub issue, without a human in the loop.
8 · Observability
The dashboard
A FastAPI dashboard makes every moving part observable: live views of sessions, health, reflections, and pipeline stage. The application factory in ui/app.py also exposes /dashboard.json, the full system state as a single JSON document.