Code-Context 3-Act Architecture — T440 Design
Status: design (T440). Locks the architecture for the CODE-CONTEXT epic (T441-T454). No product code changed. Implementation tasks must treat the contracts here as fixed.
Source cross-checked: src/core/repoMap.ts, src/core/repoBriefing.ts, src/core/lspClient.ts, src/core/lspRegistry.ts, src/core/cachePolicy.ts, src/core/sharedPrefix.ts, src/store/MemoryStore.ts, src/store/memoryRetrieval.ts, src/store/RunStore.ts, src/store/GlobalRegistry.ts, src/judges/DiffJudge.ts, src/launchBg.ts, shared-context-server/docs/design/code-context-indexing.md.
1. The core insight
Every LLM call re-reads the codebase from scratch. The instinct is to build a code graph — a precomputed index that hands the worker orientation for free. The industry has retreated from that approach: Claude Code dropped RAG entirely (Boris Cherny: "agentic search generally works better"), Windsurf and Cline both cite staleness and false-positive rates as the reason. The academic evidence is consistent: Lost in the Middle (Liu 2023) shows that stuffing more retrieved files degrades precision; Agentless (Xia 2024) achieves competitive SWE-bench performance with BM25 + LLM filter + tsc feedback, zero graph.
The right target is not "richest understanding of the codebase." It is: minimize(steps + tokens) from task to a correct, verified edit. Optimizing for understanding builds a graph. Optimizing for least-steps-to-verify builds something else entirely — a loop fed by cheap signals.
2. The boundary: live truth vs. committed-state memory
This is the single most important architectural line in the design, and it must never be blurred.
Live truth = local LSP + tsc. Any edit the worker has made to its worktree is visible immediately to the local language server. src/core/lspClient.ts + src/core/lspRegistry.ts navigate the live tree. src/judges/DiffJudge.ts runs tsc --noEmit against the live tree. These are the sources of TRUTH for the current run. Nothing network-bound ever overrides them.
Committed-state memory = optional cloud (ContextQ). What the worker has learned about committed code — signatures, call edges, prior-run summaries, cross-run lessons — lives in the cloud brain. It reflects the canonical branch at the last indexed commit. Its role is orientation and warmth, not correctness checking. A stale cloud map must never override a live LSP result.
The rule that enforces the boundary: STEER (the inner tool-calling loop) is always local. The cloud is only consulted at session boundaries — boot (SCOPE) and offload-after-judge (CARRY). Network latency never sits on the hot path.
3. The three-act split
Three timeframes, each with a single responsibility.
Act 1 — SCOPE (before dispatch)
Responsibility: hand the worker a candidate set of 3-5 files so it skips the cold-start grep crawl.
Signals used, in cost order:
- Task-text extractor — regex identifiers / dotted paths / file-ish tokens from the task, grep them against the file tree. Catches 40-60% of tasks that name their file. ~20 lines, <50 ms, zero API calls.
- Git co-change cache —
git log --name-onlymined into a co-occurrence map (cache key =git rev-list --count HEAD). Given the extractor's hits, surfaces their top co-change partners — the "second file" the worker would otherwise discover only after the firsttscfailure (Zimmermann 2005: co-change history predicts coupling better than structural analysis). - Recency / churn —
git log --since+git diff HEAD~5 --name-onlyas tie-breaker for vague tasks. - Cached repo skeleton —
src/core/repoMap.ts, but HEAD-keyed and shaped as a ~150-token pointer skeleton (entry points, adapters, key types, invariants) rather than a full walk. The worker pulls detail only for the module it touches. Pull-on-demand, not load-everything. - Invariant injection — a tiny always-on block of hard rules derived from CLAUDE.md invariants:
makeOpenAIClientonly, never snapshot TUI tests. Prevents the most expensive mistake class. - Optional LLM re-ranker (haiku-tier) over ~15 combined candidates to top 5. Agentless confirms this beats embeddings for localization at far lower infra cost.
When the optional ContextQBrain adapter is wired (see §5), Act 1 is augmented: agent_boot (T313) serves a persistent importance-ranked repo-map instead of a per-run rebuild, and invariants / prior lessons come from the cloud facts slot — cross-machine, cross-session.
Cyberine primitives reused: src/core/repoMap.ts (skeleton source), src/core/repoBriefing.ts (branch/status/recency context), src/core/sharedPrefix.ts (injection into the shared prompt prefix that gets cached by src/core/cachePolicy.ts).
Act 2 — STEER (during the run)
Responsibility: converge the worker to a verified edit in the fewest iterations. All operations here are local and live.
Mechanisms:
- Verification-feedback steering — parse
tsc --noEmitand test-runner output for{file, line, error}tuples; feed the named files back as "read these next." Each 300 ms tsc run replaces 3-5 exploratory reads. This generalizes T436's LSP-feedback design (scoped toLoopAgentAdapter) to the full STEER concept — the same idea applied at the task-prompt level for workers that cannot receive injected feedback mid-run. - LSP as JIT navigator —
textDocument/definition,textDocument/references,workspace/symbolon demand viasrc/core/lspClient.ts. Graph-like navigation with perfect freshness; no graph database needed. The warm registry (src/core/lspRegistry.ts) keeps one server process per(cmd, projectRoot)pair across rounds. - Bounded BFS — expand 1-2 hops from the task-named symbol; expand further only when a verification failure forces it. SWE-bench median changed-files = 1-2; 3-hop traversal is noise.
- Prompt-cache re-reads — files read in round 1 carry
cache_controlso round-2 re-reads cost ~10%. Cyberine's existingsrc/core/cachePolicy.tshandles the prefix; this extends the discipline to per-file read blocks.
The cloud brain plays NO role in STEER. ContextQ reflects committed state; it cannot see the worker's in-flight worktree edits. All freshness-critical navigation stays local.
Cyberine primitives reused: src/core/lspClient.ts + src/core/lspRegistry.ts (JIT navigation), src/core/cachePolicy.ts (prompt-cache accounting), src/judges/DiffJudge.ts (the verification oracle — git apply + tsc --noEmit).
Act 3 — CARRY (after the judge)
Responsibility: make the next run start warm — per-machine by default, cross-machine/cross-agent/cross-session when the cloud brain is wired.
Mechanism:
- Post-judge context capsule — an offloaded background summarizer reads the winning diff + task and writes
.local/runs/<runId>/context.mdin the /amemoria handoff shape: files touched / files deliberately NOT read (risk surface) / invariants relied on / likely next steps. The orchestrator's premium context pays ~0 — dispatch-and-return is exactly howsrc/launchBg.tsworks. - Extend Hermes — the name for the semantic memory layer (
src/store/MemoryStore.ts+src/store/memoryRetrieval.ts) — from outcome-only to include the file touch-set and capsule, so the next run's preamble ranks "files recently in play" alongside outcome text. - Regenerative rebuild — a
cyberine index-rebuildcommand (the/amemoria nf-dreamanalog) re-generates the cached skeleton from current structure, re-validates invariants against the live tree (does the named symbol still exist? does anyone now violate a CLAUDE.md invariant?), archives entries for deleted files. Idempotent, snapshot-before-write. - Idempotency marker per
runId + diffSha— a retried capsule summarizer never writes a conflicting entry (mirrors /amemoria's capture-marker pattern, stored insrc/store/RunStore.ts).
When ContextQBrain is wired, the local .local/runs/ capsule is ALSO pushed to POST /api/contexts/bulk-upsert (see §6 for the exact schema). The cloud then serves this capsule on future agent_boot calls — cross-machine, cross-agent, cross-project. The flywheel: more Cyberine runs → richer cloud brain → warmer boots → fewer steps to verify → more runs.
Cyberine primitives reused: src/launchBg.ts (offloaded background dispatch), src/store/RunStore.ts + src/store/GlobalRegistry.ts (run persistence and idempotency), src/store/MemoryStore.ts + src/store/memoryRetrieval.ts (Hermes semantic layer extension).
4. The ContextBrain port
Cyberine is already ports-and-adapters. The cloud brain drops in as another adapter behind a new port. The port has three methods — one per act.
// src/core/contextBrain.ts (new file, T441)
interface ContextBrain {
// SCOPE: boot a run with orientation + lessons from prior work
boot(goal: string, repo: string): Promise<{
repoMap: string; // token-budgeted skeleton (~150 tokens)
invariants: string[]; // hard rules to inject into every worker's prompt
lessons: string[]; // prior-run lessons relevant to this goal
}>;
// STEER: committed-state recall on a verify failure (never live edits)
recall(query: { error?: string; symbol?: string }): Promise<{
pastFix?: string; // prior fix for this error pattern, if known
refs?: string[]; // known referrers of the symbol
callPath?: string[]; // BFS call chain from the symbol
} | null>;
// CARRY: sink the post-judge capsule so future boots start warmer
carry(capsule: ContextCapsule): Promise<void>;
}Two adapters ship with the epic:
LocalBrain (default, offline) — boot reads from the HEAD-keyed repoMap cache + CLAUDE.md invariants. Recall queries Hermes (src/store/memoryRetrieval.ts) for prior fixes. Carry writes the capsule to .local/runs/<runId>/context.md and updates the Hermes touch-set index. This is the behavior of every Cyberine run today once the wiring exists — no network required.
ContextQBrain (optional, cloud) — boot calls POST /api/agent/boot (T313). Recall calls /api/search with a kNN lesson query. Carry calls POST /api/contexts/bulk-upsert with a capsule shaped per §6. All three are best-effort: if the cloud is unreachable, the adapter falls back to LocalBrain results and the run continues. Cloud calls happen ONLY at session boundaries (boot at run start, carry at run end, recall on a verify-failure) — never inside the inner tool-calling loop.
The port replaces nothing today. SCOPE, STEER, and CARRY are implemented piecemeal by the T441-T454 build tasks. The port gives those tasks a stable interface contract so they can ship independently and be wired together without a flag-day cut-over.
5. What is deliberately NOT being built
Stated plainly, because saying no is half the design.
No local live code graph. A graph built over the live worktree is stale the instant the worker edits a file. The LSP (src/core/lspClient.ts) already provides on-demand graph navigation — textDocument/references, workspace/symbol, call-chain BFS — with perfect freshness. Building a graph on top of tsserver duplicates what tsserver already does incrementally, at higher cost and lower accuracy.
No semantic embedding index. BM25 + a haiku-tier re-ranker matches embedding retrieval for file localization (Agentless finding, confirmed by Windsurf's own analysis). An embedding index adds a model dependency, a vector store, and per-worktree index maintenance. The false-positive rate under Cline's testing was the explicit reason they rejected it. Only revisit if profiling shows real spend on semantic queries that symbol-name lookup cannot answer.
No prebuilt call graph. textDocument/references on demand at query time already covers call-chain navigation. A prebuilt graph is stale and expensive to maintain across worktrees.
No ContextQ wiring this epic. ContextQBrain is the optional adapter; the cloud integration is not built in T441-T454. The constraint is: shape every artifact (capsule schema, port methods) so the cloud wire-up costs nothing when it happens. The port isolation guarantees this — flipping from LocalBrain to ContextQBrain is a single adapter swap, not a rewrite.
6. CARRY capsule schema (push-compatible with ContextQ bulk-upsert)
Every capsule produced by the CARRY act must be field-compatible with ContextQ's POST /api/contexts/bulk-upsert payload so the cloud wire-up requires only an adapter swap, not a schema migration. The schema comes from shared-context-server/docs/design/code-context-indexing.md §8 + §15.
Key constraints from that doc:
externalId=code:<repo>:<path>where<repo>is the normalized git-remote slug (github.com/<owner>/<repo>) — stable across machines, so two clones of the same repo converge on ONE set of contexts.type="reference"withtags: ["code"]andmetadata.kind = "code-file"— no new ContextType, no migration.- Idempotency is on
(tenant_id, external_id)withmetadata.content_hashas the change signal. Re-running upserts in place. workspaceis a top-level field on the request, not per-item.bodystructure: path + lang +## Signatures+## Summary(L3, agent-assisted) +## Related(L3).
Concrete JSON example — the capsule Cyberine writes after a verified edit to src/core/repoMap.ts:
{
"workspace": "ai-agent-solution",
"contexts": [
{
"externalId": "code:github.com/llawliet11/ai-agent-solution:src/core/repoMap.ts",
"title": "src/core/repoMap.ts",
"description": "Token-budgeted repo skeleton: ripgrep symbol extraction + cross-ref PageRank ranking.",
"body": "src/core/repoMap.ts\nlang: typescript\n\n## Signatures\nexport async function buildRepoMap(opts: RepoMapOpts): Promise<string>\nfunction rankFiles(files: FileEntry[], refs: RefMap): FileEntry[]\n\n## Summary\nBuilds a token-budgeted repo skeleton for worker orientation. Runs ripgrep for symbol extraction, computes a cross-reference PageRank score per file, trims to the token budget. Entry point for Act 1 SCOPE. Must be called through the HEAD-keyed cache, not rebuilt on every run.\n\n## Related\nsrc/core/repoBriefing.ts, src/core/cachePolicy.ts, src/core/sharedPrefix.ts",
"type": "reference",
"tags": ["code"],
"metadata": {
"kind": "code-file",
"content_hash": "<sha256-of-file-bytes>",
"lang": "typescript",
"preset": "standard",
"symbols": ["buildRepoMap", "rankFiles"]
}
}
]
}The metadata.symbols array is the L1 fact set ContextQ uses in its verify-with-teeth step (§10 of the ContextQ design): every symbol the LLM names in a summary must exist in this set or the entry is rejected. Cyberine must populate it from tree-sitter extraction (or grep fallback) to survive that gate.
Note the <repo> slug in externalId must be derived from the normalized origin remote, not from the folder name — a clone rename must not fork the index. When no remote exists, fall back to the sanitized toplevel basename and warn.
7. ROI-ranked build sequence for T441-T454
The implementation tasks should land in this order to maximize steps-saved-per-engineering-hour:
- ContextBrain port + LocalBrain stub — the foundation all other tasks build on. Unblocks parallel build.
- Verification-feedback steering (Act 2) — highest leverage; the error is the index. T436 is the first slice for
LoopAgentAdapter; the full STEER concept generalizes it to the task-prompt level. - Task-text extractor (Act 1) — ~20 lines, kills cold-start file search on 40-60% of tasks.
- Git co-change cache (Act 1) — eliminates the "discover the second file via a failed tsc" cycle.
- HEAD-keyed repoMap cache — makes the unavoidable skeleton near-free to serve; prompt-cache the re-reads via
src/core/cachePolicy.ts. - Post-judge capsule + Hermes touch-set (Act 3) — run-to-run warmth via
src/launchBg.ts+src/store/MemoryStore.ts. - Regenerative rebuild (Act 3) — keeps the skeleton and invariants honest as the repo drifts.
None of these items are a graph, a vector store, or a network dependency. Each is independently shippable and independently verifiable.
See 3-Act Code-Context Eval (T453) for the harness that measures whether this design actually saves steps.
8. Cross-check statement
Every architectural claim was verified directly against source, not from memory. Confirmed: src/core/repoMap.ts exists (repo-map builder). src/core/lspClient.ts + src/core/lspRegistry.ts (warm LSP registry). src/core/cachePolicy.ts (prompt-cache accounting). src/judges/DiffJudge.ts (verification oracle). src/launchBg.ts (background dispatch). src/store/MemoryStore.ts + src/store/memoryRetrieval.ts (Hermes semantic layer: buildMemoryPreamble, appendEmbedding, outcome storage). src/store/RunStore.ts + src/store/GlobalRegistry.ts (run persistence + cross-project index). The ContextQ bulk-upsert payload shape was read from shared-context-server/docs/design/code-context-indexing.md §8 + §15 (read-only; no edits made to the shared-context-server repo). "Hermes" is the informal name for the semantic memory layer injected via src/core/sharedPrefix.ts as === HERMES SEMANTIC CONTEXT === — no standalone Hermes class; it is the combination of MemoryStore + memoryRetrieval + sharedPrefix injection.
Loop Synchronous Dispatch + Verify Seam — Design Note
Status: shipped — T459 (beam-search forks) and T463 (non-code verify gate) are wired into the loop's syncDispatchTask path, see src/engine/autonomyLoop.ts:728-739,823,866.
Long-term memory for Cyberine — survey + design
Agent memory is organized into five cognitive categories — episodic (experiences situated in time), semantic (abstract facts), procedural (skills, policies), working (in-context manipulation),...