DuoDuo docs

How DuoDuo works.

What DuoDuo is, how it learns, and the ways you reach it. Where there is something to do, these pages hand it to your DuoDuo.

See how durable files, two loops, and intuition connect.

Understand

Architecture

Follow a message from arrival to disk, execution, and recovery.

DuoDuo is a filesystem-first, event-sourced host runtime. The runtime (Claude, Codex, Grok or Pi) does the thinking. The daemon keeps events safe, routes work to the right session, wakes things on time, and gets results delivered.

Two loops · one body of filesOne daemon
Foreground surfaces
stdio
Feishu/Lark
ACP
Ambient
duoduo daemon
Routes work. Wakes the subconscious.
live sessions · subconscious loop · scheduled jobs · channel delivery
Harness runtimes · pick one per actor
Claude · Codex · Grok · Pi
Files you can open
sessions/on disk
jobs/on disk
memory/on disk
events/on disk
Every message is written to disk before it runs, as a record of what happened. On its own cadence, the subconscious reads that record and distills it into intuition, and every runtime starts from it, whichever one did the work.

Invariant 1: WAL before execute#

The Spine is the event bus that every channel, the cadence and every session feed: everything that arrives or happens becomes a canonical event on one durable, replayable log. The Spine WAL, an append-only JSONL log, is the source of truth for what happened. A best-effort live feed speeds up running components but never carries correctness.

A long-lived system will be stopped mid-turn, by a crash, an upgrade or a closed laptop lid. Because the event is written down before anything acts on it, the record of what was asked exists whether or not the answer ever did.

An incoming channel message follows one ordering: spine → mailbox → drain.

  1. Normalize the incoming message into a canonical event.
  2. Persist the raw ingress payload and append the event to the Spine WAL.
  3. Route the event into the owning session's mailbox.
  4. The session runner drains the mailbox and executes the turn.
  5. Outputs are recorded and delivered through the channel outbox.

DuoDuo nudges a teal ticket into an upright ledger gate connected to a raised lever.

The WAL is the cold record of what crossed the machine's boundaries: audit, the daemon's own bookkeeping, duoduo spine and the subconscious read it, and the daemon replays it to rebuild its routing and scheduling state after a restart. The live conversation each runtime resumes is hot session state, kept in that runtime's own session files. A session continues from its own state; the WAL tells you what happened to it and does not replace it.

Read the log#

A busy day of history is tens of megabytes of JSONL, and most of its lines are tool plumbing. duoduo spine is the read surface built for that:

duoduo spine cat --date 2026-09-04 --session <key>
duoduo spine cat --date 2026-09-04 --count-only
duoduo spine show <event-id>

cat prints what people and agents said in full, and folds each tool call and its result into one line you can drill into. The header and the closing END spine footer tell a complete read from a truncated one, and --after <event-id> resumes strictly after a given event, so a long scan continues instead of starting over. show prints one event whole, nothing elided.

Before you script against it: --count-only sizes a window without printing a body, which is how you check before pulling a day into an agent's context. A cat with no narrowing filter refuses to print a body unless you pass --unfiltered. That is context-window protection, not a permission check.

Invariant 2: durable state lives in files#

The filesystem is the durable body
Durable runtime state
runtime_dir/var/
events/Spine WAL · source of truth
sessions/conversation state
jobs/scheduled work
outbox/channel egress queue
usage/measured activity
Kernel · a Git repo
kernel_dir/
config/channel defaults
memory/intuition layer + dossiers
subconscious/partitions + playlist
Workspace
/your/project/

The project files the agent is reading and changing. The session stays attached to this workspace.

The filesystem is the database. The Spine WAL is the record of everything that crossed the machine's boundary: what audit, duoduo spine, and the subconscious read, and what the daemon rebuilds its routing from after a restart. Each runtime keeps the conversation it resumes in its own session files beside it. The kernel's Git history records every change to the intuition layer and configuration. Locks, PIDs, and heartbeat bookkeeping live separately as ephemeral files—safe to clear, never needed to recover. Run duoduo daemon config to see the paths used on this machine.

Different kinds of state change at different speeds, and mixing them is how systems become unrecoverable. The installed system separates files by how they may change:

  • Kernel (kernel_dir) is a Git repository: channel-kind defaults, the intuition layer and its dossiers (under memory/), subconscious partition prompts, and the playlist. Its commit history records every change to them.
  • Durable runtime state (runtime_dir/var) holds ingress records, the Spine WAL, mailboxes, sessions, jobs, the outbox and usage ledgers. The daemon rehydrates from it.
  • Ephemeral state (locks, PIDs, heartbeat bookkeeping, queue offsets) is kept separately and can be cleared at any time; rehydration never depends on it.
  • Workspace is the project a session is reading and changing. It belongs to you, not to the runtime.

Indexes the daemon holds in its process speed up routing, but they are derived views. On restart, the daemon rehydrates from the Spine WAL and durable state, then resumes scheduling.

Invariant 3: one actor owns each stream#

A foreground conversation, a scheduled job and a subconscious partition each have their own session identity and lifecycle. Lease locks prevent two runners from owning the same active stream at once. Per-session mailboxes keep work in order without merging unrelated conversations.

Externally this is still one DuoDuo; internally, explicit actors keep concurrency understandable and recoverable.

Cadence and the subconscious#

Cadence emits a heartbeat independently of channel traffic. On each tick, the meta-session reads the playlist, selects the next enabled partition that is not cooling down or backed off, and runs it with its own prompt and time budget. For a completed run, the runtime records and settles success, timeout, invalid output or error before advancing the playlist and partition state.

Partition configuration lives in Markdown frontmatter. The prompt body defines what the partition does; the schedule fields control whether and when it becomes eligible. The prompt is read from disk on each tick, so a reviewed local prompt edit can affect the next eligible run without replacing the daemon.

Intuition returns to the foreground#

The pipeline, in one line: events —distill→ text gradients —weave→ intuition layer. What the subconscious keeps along the way has several layers:

  • text gradients distilled from external event history, each naming a line of the intuition layer, the events it cites, and which way that line should move
  • entity dossiers and topic dossiers
  • verified correction paths called lessons
  • repeated procedures distilled as grooves
  • a compact broadcast intuition layer loaded into future foreground sessions

Each layer has one writer, so two partitions never contend over the same file. The committer's only operations are git add and git commit. It records what changed and does not judge it, because a quality gate there would guard the Git history while every foreground session already reads the working tree. Foreground sessions load the compact broadcast layer, not the entire transcript archive.

Runtime selection#

Claude, Codex, Grok and Pi are peer execution backends where installed. A channel, job or subconscious partition can select one. /model and /effort adjust the current foreground session without changing the whole host. Every runtime takes images as ordinary turn input.

All four sit behind one DuoDuo interface. A session on any of them sees the same DuoDuo tools, the same kind/instance/job/partition prompt layering (including whether the prompt is appended to or replaces the runtime's own), and the same mid-turn steering. Token and cost accounting is normalized, so the numbers stay comparable.

Availability is checked against what is installed, and it fails closed: asking for a runtime that is not usable gives you a clear error, never a silent fallback to another one.

A runtime name DuoDuo does not know is treated the same way. A typo such as runtime: codx is refused with a sentence that names it and lists the valid ones, never replaced by the next layer down: an unknown ALADUO_DEFAULT_RUNTIME stops the daemon from booting, an unknown value in a channel descriptor refuses that channel's turns, a job with one fails, and a partition with one is skipped. A session also stays bound to the runtime that created its history. Moving it to another runtime needs /clear first, because one harness's history is meaningless to another.

Available does not mean ready. Codex and Grok are found by their installed CLI. Pi is embedded, so it is always available, but a Pi session needs a model named for it; until one is, it refuses each message with the fix in the reply rather than quietly answering as Claude.

Each runtime also brings something the others cannot:

RuntimeHow DuoDuo runs itWhat only it has
ClaudeEmbeds the Claude Agent SDK at a pinned versionModel profiles: per-model context windows, endpoints, and tier aliases
CodexThe Codex CLI you installedIts own loop and tool-calling convention
GrokThe Grok CLI you installedNative X search from Grok Build
PiEmbeds the Pi coding agent at a pinned versionYour own Pi: extensions, skills, prompt templates, AGENTS.md

DuoDuo strips the parts of a runtime that would duplicate what it already owns. A runtime's own scheduler and background-task tools give way to DuoDuo's jobs, so scheduled work lives in one place. Claude Code's own auto memory feature is off in every Claude session DuoDuo starts for the same reason: it would keep a second store of learned notes beside DuoDuo's intuition layer.

See Mental Model for why these are separate runtimes rather than one harness with four logins.

Recovery and inspection#

Use duoduo daemon status for health, cadence, subconscious progress, and the tool calls running right now. Use duoduo daemon config to see the resolved paths and runtime settings on a machine. Use duoduo spine to read the history itself. DuoDuo Manager reads the same daemon APIs for sessions, jobs, partitions, usage and the event stream.

After a package upgrade the daemon must restart so the detached process loads the new code. duoduo upgrade does both, and your DuoDuo runs it when you ask. Kernel prompts are preserved separately because they may have changed on this host.

The daemon's control interface is a unix socket with owner-only permissions; the loopback TCP port serves the dashboard read-only. See Host Operations.