BETA In open beta. Install live. Lock $5/mo for your first 12 months. See pricing →
← Blog

I slept through my beta launch and delivered on time

At 23:47 the night before I planned to flip the public repo, I had four Claude Code sessions running in four different tmux panes, each in a different project on my machine. Three were workers. One was an orchestrator. None of them were me.

I went to bed.

When I woke up, ten commits had landed on main. All green. None of them mine. Forty-eight commits in over fifty hours. I never wrote down what each session was doing.

This is the story of how that worked, and the one part where it nearly didn't.

The moment that earned the post

Mid-sprint, on the bench pane, after 1902 turns of conversation grinding through benchmark cells, I tested the recovery loop deliberately. I ran /hydrate-distill. The local daemon summarised the entire conversation into one staged injection. I ran /clear. The conversation wiped. I ran /hydrate-last. Thirty seconds later, the new session emitted this:

The bench was mid-run when last seen. Cell 1 (Hydrate, N=10)
was scored 11/20 and Cell 2 (claude-mem) was underway. Two logs
to check:
- Hydrate arm: /tmp/bench-hy-run.log on mini
- claude-mem arm: /tmp/bench-cm-run.log on mini

In-flight sub-session (do not re-dispatch):
sse-project-detail-pane → pane dev, dispatched 07:20 UTC, 1h30m ETA.

Next up after bench completes:
1. wave2-pack-onboarding-measure
2. wave3-cloudflare-email-routing
3. Various wave3 post-launch items

The status line read ↑ 14 tok saved. The full recovery cost fourteen tokens against the local daemon. Re-pasting the equivalent conversation would have cost around two hundred thousand.

That is the ratio the rest of the pattern depends on.

How it works

The orchestrator ran in its own tmux pane on Opus, because it makes the judgement calls. Four worker panes sat alongside it:

Most multi-agent demos run one orchestrator and N workers in the same project. This was three workers in three different project repos on disk, with no shared filesystem mounts and no message broker between them. The bus was the local hook every Hydrate user already runs.

The fifth actor was hydrate-server, my own memory daemon, running locally on the same workstation. Every pane reached it through the two hooks every Hydrate user already has installed: UserPromptSubmit (read state before every prompt) and Stop (capture session decisions on completion). It owned a single SQLite file at ~/.hydrate/data.db.

The orchestrator's only mechanism for controlling the workers was tmux send-keys. It typed prompts directly into each worker's input field. No socket, no API, no message broker. The two-call dispatch pattern took three minutes to debug on the first attempt (text first, sleep half a second, then Enter as a separate keystroke; sending both in one call lets the TUI swallow the Enter as input-field state) and worked for every dispatch after that.

Sharing facts across three project repos

This is the part that is novel, and the part that made the whole sprint work.

Hydrate's normal job is to share memory inside one project. A fact written in a session lands in the local SQLite store. The UserPromptSubmit hook surfaces relevant facts in the next prompt automatically. For the launch, I wrote orchestration facts at the global scope instead, with no project tag, under an [ORCH:*] naming convention:

Globally-scoped pinned facts surface in every session's prompt on the same machine, no matter which project the session is working in. That single line of server logic, the same one shipping to every Hydrate user, is what made the cross-project coordination free. The dev pane could read everything the web pane had landed. The web pane could read everything the bench pane was measuring. None of them paid extra tokens to learn it. The orchestrator never asked a worker "what are you doing right now." It wrote the dispatch as a fact, and the worker's next prompt already contained it.

The state writes looked like this:

hydrate orchestrator set wave3-vs-claude-mem in-progress
hydrate orchestrator set wave3-vs-claude-mem-notes "cell 2/10"
hydrate orchestrator dispatch --to web \
  --brief docs/plans/.../HANDOFF-sse-facts-pane.md \
  --eta 2h \
  --acceptance "per-row SSE frames; user edit not clobbered" \
  sse-facts-pane

The subcommand (cmd/hydrate/orchestrator.go, around three hundred and fifty lines plus tests) is a thin typed wrapper over the existing canon-fact API. The Stop hook does the other half of the job: when a worker session ends, its transcript is processed and any durable decisions are extracted as facts automatically. Even if a worker forgot to call hydrate orchestrator set, its decisions still landed in the store and surfaced in the next orchestrator prompt. I did not have to chase anyone for reports.

What the orchestrator dispatched

The fifty hours split into four kinds of work:

What broke, twice

The first failure was on the bench pane. It had been grinding through benchmark cells for hours. Context climbed past seventy percent. Then eighty. The status line lit up with distill now! — a nudge from the fatigue-signals pane I had shipped two hours earlier on the same plan. I watched it cross eighty-three percent. Then auto-compaction fired. The session reset.

I had built PreCompact and SessionStart hooks days before for exactly this case, but I had not actually tested them on a real fatigued session. They had not been exercised in anger. Now they were.

The replacement session loaded the snapshot. The bench worker picked up the cell-by-cell grading loop. The benchmark continued without intervention. The compact-survival features predicted their own validation event. I had built the recovery loop hours earlier with no plan to exercise it. The sprint exercised it for me, on the same feature it had just shipped.

The second failure was funnier and more my fault. The orchestrator was sending a status update to the dev pane that started with /overview shipped at <sha>.... Claude Code's TUI parsed the leading /overview as a slash command. The worker responded "Unknown command: /overview", then /exit ran in the input history, and the session closed.

Filed as a pattern-doc gotcha: never lead a tmux send-keys payload with /<word>. Wrap in "Dispatch:" or "Status:" before any text that might start with a slash.

In both cases the recovery surface was the same. Open a fresh session in the dead pane, run /hydrate-last, get the structured briefing shown earlier, resume.

The waves

The orchestrator split the plan into four batches:

Wave Tasks Calendar time What happened
1 4 One afternoon First proof of concept. Validated the dispatch pattern.
2 10 ~24 hours Recall-bias bug surfaced and fixed. Distill / clear / last cycle tested live.
Overnight 10 (autonomous) 8 hours unattended Dev queue 100% consumed. Web 100% consumed. Bench mid-run. I was asleep.
SSE-everywhere 12 (parallelised) 78 minutes Ten dashboard panes converted from pull to push. Plan estimated ten hours serial.

Forty-eight commits between the orchestrator subcommand landing (26f1f9d) and full SSE-everywhere completion (3baaf6e). Fifty hours of calendar time. Most of it ran while I was asleep or in other meetings.

What it cost

The orchestrator session ran continuously on Opus for the duration. Worker panes ran on Sonnet for implementation and switched to Opus only for the judgement-heavy passes (writing, design calls). Early read on the token accounting: orchestrator-to-worker ratio settled at around ten percent. Coordination overhead was an order of magnitude smaller than the work the panes did. The dominant cost was always implementation, not coordination.

The reason that ratio held: the orchestrator's input was the pre-prompt hook, which is free at the point of read, and its output was short dispatch prompts plus a running ops log. It never paid to re-derive state from scratch.

What would have happened without this

A week of evenings. Lost momentum. The kind of launch slip that a founder remembers years later as the moment they wished they'd had a team.

Instead, I had a hook the product already shipped, three worker sessions that did not need a standup, and a beta that shipped on time. The state lived in SQLite where I could not lose it. The hook surfaced it on every prompt the worker made. Coordination cost nothing extra.

Hydrate's homepage says memory is a layer, not a feature. This sprint was the test.