Every Claude Code session you close writes a JSONL transcript to ~/.claude/projects/. Hundreds of turns sit there. Tool calls, decisions made and unmade, the ten-turn arc you forgot the same afternoon. Your next session knows nothing about any of it. orchestrator dream changes that. It mines those transcripts, extracts durable learnings with Haiku, and injects the best of them into your context before your first prompt lands.
Four steps and a settings file. Nightly extraction will be running over your own transcripts before your next session starts.
A quick note on what you're installing. orchestrator is part of nanika, an open-source multi-agent runtime I've been building around Claude Code. Most of nanika is a mission engine — you write a task, it decomposes the work into phases and dispatches each phase to its own Claude Code session with its own persona. Dream is the part that runs the other direction. It reads transcripts after the fact and feeds the durable bits forward into the next session. You don't have to use the rest of nanika to use dream. The binary stands alone, and what you wire up here is independent of any mission you ever run.

Step 1. Install the orchestrator binary
The dream pipeline ships inside nanika's orchestrator binary. Get it on $PATH first.
# From the nanika repo root
go install ./skills/orchestrator/cmd/orchestrator
orchestrator --versionThe shell prints a version string, something like orchestrator v0.x.x. No command not found. If go install puts the binary somewhere not on your path, add $(go env GOPATH)/bin to PATH in your shell profile and re-source it.
Success looks like: orchestrator --version returns a single line with a version, not an error.
Skip cost. Every step from here calls orchestrator. Without the binary on $PATH, none of them work, and you'll think the pipeline is broken when nothing has actually run yet.
Step 2. Dry-run before you let Haiku eat your archive
The first time you point dream at ~/.claude/projects/ it could touch every JSONL file you've ever produced. On my machine that's over a thousand transcripts and rising. Each one becomes one or more chunks, each chunk becomes a Haiku call. Run blind and you spend an hour and a few dollars before a single learning lands.
Dry-run first.
# Preview only. No Haiku calls. No DB writes.
orchestrator dream run --dry-run --verboseThe output is a list of files dream would process and a count line at the end. Something like would process 47 transcripts. Read the list. If it includes paths from before you cared about this pipeline, run the bounded version next instead of the full sweep.
# Real extraction. Only the last 24 hours of transcripts.
orchestrator dream run --since 24h--since 24h restricts to transcripts modified in the last day. The first real extraction takes seconds, not minutes. You watch learnings land. You confirm the wiring works. Then you can drop the flag and let it process the rest of the archive on a schedule you set.
Success looks like: the live run ends with a line like extracted N learnings from M transcripts. Any non-zero N means Haiku received chunks, wrote learnings, and the SQLite store accepted them.
Skip cost. Run without --since 24h on a virgin install and you process the full archive in one shot. It works. It also burns the Haiku tokens you didn't budget for, and you spend the next hour wondering which transcript was the expensive one.
Three filters decide what survives. File hash skips transcripts dream has already read. Chunk hash skips conversation windows it has already extracted from. Cosine similarity at 0.85 inside learning.DB.Insert is the last gate — same idea worded differently doesn't enter the database twice. The third gate is the one most people forget to add, and it's the one that keeps the store from drowning in near-duplicates.


Step 3. Inspect the database before you trust it
The pipeline can fail silently. A network blip mid-run, a Haiku rate limit, a permissions glitch on the SQLite file. The CLI exits zero, the next session starts with no learnings, and you never notice. Verify the DB has content before you wire it into your sessions.
# CLI summary
orchestrator dream status
# Direct counts
sqlite3 ~/.alluka/learnings.db "SELECT COUNT(*) FROM learnings"
sqlite3 ~/.alluka/learnings.db "SELECT COUNT(*) FROM learnings WHERE embedding IS NOT NULL"dream status prints processed counts: transcripts and chunks. The two SQL counts tell you how many learnings exist in total and how many have embeddings attached. The embedding gap is normal. Backfill is incremental and runs in the background. What matters is that the total count isn't zero.
Spot-check the top three by quality score.
sqlite3 ~/.alluka/learnings.db \
"SELECT id, content, quality_score FROM learnings ORDER BY quality_score DESC LIMIT 3"You want three rows back. Each content field should read as a sentence a human wrote. If quality_score is 0.0 across all three rows, the scoring step is broken upstream, and the preflight in Step 4 will inject nothing useful even when the row count looks healthy.
Success looks like: dream status shows non-zero processed transcripts, the COUNT(*) on learnings matches or exceeds the "extracted N" line from Step 2, and the spot-check returns three readable rows with non-zero scores.
Skip cost. You trust the silent run. Step 4 wires a preflight that reads from a DB that's empty. Fresh sessions start with no learnings, and you can't tell whether the cause is "nothing useful extracted yet" or "the writer has been failing for a week." Verification is cheap. Mystery is expensive.
Step 4. Wire the SessionStart preflight
The DB has content. Now make Claude Code read from it on every session start. Add a hook to .claude/settings.json (project-level) or ~/.claude/settings.json (global).
{
"hooks": {
"SessionStart": [
{
"command": "orchestrator hooks preflight",
"timeout": 10000
}
]
}
}orchestrator hooks preflight is the brief assembler. It reads the top learnings by quality score, packs them together with scheduler and tracker context into a 6 KB block, and prints to stdout. Claude Code's SessionStart hook captures stdout and injects it as a system reminder before your first turn lands.
Verify the hook fires. Run the command directly first, before opening a new session.
orchestrator hooks preflight | grep "^### Relevant Learnings"That header line must appear. If it doesn't, the learnings section was dropped by the byte budget, or the DB query returned empty, or the section is disabled in your config. Fix it before relying on the auto-injection.
Then open a fresh Claude Code session. The system-reminder block at the top of the context now contains a ### Relevant Learnings section with two or three top entries from your DB, sitting above your first prompt.
Success looks like: the grep returns the header line, and a new session opens with ### Relevant Learnings followed by insight or pattern bullets above your first turn.
Skip cost. Steps 1 through 3 build a database that nothing reads. The dream pipeline becomes a write-only archive. The cycle that's supposed to close stays open, and the fresh session you open tomorrow forgets everything yesterday's session learned.

What dream still cannot see
The four steps above close the loop. Nightly extraction lands learnings, the preflight injects them, the next session starts with context the previous one earned. It's the part that sounds too clean to be real, and a lot of it is.
The pipeline only sees what got written to a JSONL transcript. Four blind spots ride along with that constraint, and they don't go away when the row count climbs.
Worker sessions are excluded by design. Dream skips anything where the working directory sits inside ~/.alluka/worktrees/. That's correct. Worker output already goes through a live capture path during phase execution. But it means a worker's tacit choices, the ones it makes without surfacing them in its output markers, never show up in either pipeline.
Tacit correct decisions leave no signal. When a session goes right on the first try, nothing in the transcript tells Haiku that something went right. Dream extracts from friction. A smooth session looks like an empty transcript. The DB accumulates evidence of things that were hard, not things that were easy.
External context never enters the JSONL. You read a blog post. You decide. You come back to Claude. The reasoning that shaped the decision exists entirely outside the transcript. Dream reads what's there. Everything you thought before typing is invisible to it.
Cross-chunk causal reasoning gets fragmented. A decision in chunk three that responds to a problem from chunk one looks unconnected when each window is processed as a standalone unit. Long-arc reasoning, the kind where you try something, it fails, you adjust over several turns, and the final approach is shaped by the failures, splits into pieces that no single learning can reassemble.
Four structural gaps. The number on dream status keeps going up. How much of what actually mattered to you last month falls inside one of those four categories?
That's the question I don't have a clean answer to. Run the four steps anyway. The cycle that does close is worth more than the parts that don't. And the only way to learn what extraction can't see is to first see what it can.