How it works
The loop
Section titled “The loop”coding-agent sessions → vir → your notes (markdown) → CLAUDE.md / AGENTS.md → better sessions → …Sessions become notes. vir sync-claude feeds the best notes back into your project’s CLAUDE.md (and AGENTS.md, where one exists), with a diff and your confirmation. The next session starts knowing what the last one learned. A daemon keeps the loop turning.
The pipeline
Section titled “The pipeline”Each vir run does five things in order:
- Scan. Walk
~/.claude/projects/**/*.jsonl(and~/.codex/sessions/, when Codex is on) and hash every file. A session is only reprocessed if its content changed, so reruns are idempotent and free. - Filter. Drop what isn’t yours before any paid call (details below).
- Scrub. Strip API keys, bearer tokens, absolute paths, and emails from what’s left.
- Classify. A cheap Haiku call reads the prose of the session and returns
{category, topic, project, confidence, themes}. Anything at or below0.6confidence is dropped here. - Distill. The full transcript — tool calls included, large outputs trimmed — goes to the distill model, which writes the note body: a summary that first says what the session was and then states the most important thing it established, the lessons as concrete claims, and a line of context.
Every step records its outcome in ~/.vir/vir.db, so nothing is silently skipped.
What gets filtered, and why
Section titled “What gets filtered, and why”On the author’s machine, 2,012 transcripts produced 460 notes. That ratio is the point: most of what Claude Code writes to disk isn’t a session you drove.
| Skipped as | What it is | Reversible? |
|---|---|---|
workflow-transcript |
Phases of a multi-agent workflow | Yes — workflowTranscripts: "include" |
sidechain-transcript |
Subagent runs nested under a session; Codex subagent and review threads | Yes — same knob |
agent-transcript |
Headless SDK agents (reviewers, verifiers); headless codex exec runs |
Yes — agentTranscripts: "include" |
project-excluded |
A project you excluded in vir projects |
Yes — vir projects include <name> |
project-pending |
A project you haven’t decided on | Yes — decide |
| (no reason) | Low-signal by heuristic, or classify confidence ≤ 0.6 | No |
Detection is structural — directory shape and the first line’s entrypoint field for Claude Code, the session_meta line’s source for Codex — so it costs nothing and runs before the classifier. Flipping a knob re-enters the transcripts on the next run without --full.
Anatomy of a note
Section titled “Anatomy of a note”---topic: "kie-200-error-body"aliases: - "kie-200-error-body"category: gotchaproject: "growthq"session_id: 4f2a9c31-7d0e-4b8a-9c55-1e2f3a4b5c6ddate: 2026-06-01T09:14:22.000Zconfidence: 0.86themes: - "kie-error-handling" - "retry-safety"branches: - "fix/blank-posts"---Project: [[growthq]]Category: [[gotcha]]
## Summary
This session debugged image generation failing silently in growthq's postpipeline. The Kie.ai endpoint answers HTTP 200 even when generation fails: theerror arrives in the body as `{code: 402, msg: …}`, so `response.ok` nevercatches it …
## What Was Learned
- **Check the body's `code`, not the HTTP status** — the client now throws on an in-body 4xx before trusting `response.ok`. Chosen over retrying on empty content, which hid insufficient-credit failures …
## Context
The generation queue had been producing blank posts for two days …
## Related
- [[retry-with-backoff-0c91be7a|retry-with-backoff]]topicis the title, chosen by the classifier after the single most durable lesson in the session — that’s what makes retrieval rank a pointed note above a diary.- The body opens for two readers. The first summary sentence says what the session was, for you skimming it a month later; the second states the single most important thing it established, with the file, number or constraint that carries it, for the session that retrieves it. That order came out of a blind test on real transcripts: a human preferred the orienting opener 14 to 1, a model reader preferred specifics-first 14 to 0, and the prompt that asks for both was the only one neither rejected. One grader and fifteen transcripts — a measurement, not a proof. Decisions in the lessons say what was chosen and what it was chosen over.
categoryis one of four:pattern,gotcha,decision,tool. It’s also the folder the note lives in.confidenceis the classifier’s score. The Obsidian plugin renders low-confidence notes dimmer.session_id+dateare provenance: every claim traces back to the exact session that produced it.## Relatedis built from embedding neighbors, never from the model’s guesses. On a real vault, 1 in 2,261 model-suggested links resolved; neighbor links resolve by construction.aliaseslets a bare[[kebab-topic]]reference resolve in Obsidian even though the filename carries a hash suffix.
Quality controls
Section titled “Quality controls”Auto-distilled notes can be wrong, and a wrong note that reaches CLAUDE.md makes sessions worse, not better. The controls, in the order they apply:
- Transcript and project filtering before any API call.
- The
0.6confidence floor between classify and distill. vir review— walk new notes, approve, edit, or reject. Verified notes get a ranking boost in retrieval; rejected ones move to.rejected/, recoverable.vir lintflags orphans, stale notes, and contradictions;vir dedupemerges near-duplicates.vir sync-claudenever writes without showing the diff first.- Everything is a markdown file. Read it, edit it, delete it.