# Comparative Architecture — Hermes · OpenCode · OpenHands · mini-SWE → KHAELOR > Phase 0 synthesis. Sources: `docs/research/HERMES_ANALYSIS.md`, `OPENCODE_ANALYSIS.md`, `OPENHANDS_ANALYSIS.md`, `MINI_SWE_ANALYSIS.md` — all based on traced code paths, not READMEs. Every claim below cites the evidence recorded in those documents. Decisions are elaborated as ADRs in `KHAELOR_ARCHITECTURE_DECISIONS.md`. --- ## 1. Decision Matrix | Capability | Hermes | OpenCode | OpenHands | mini-SWE | KHAELOR Decision | |---|---|---|---|---|---| | **Agent loop** | 7,740-line `while` loop; typed error hints; verification-on-stop | State-derived `runLoop` over persisted messages; stream reducer split out | `Agent.step()` + `run()` state machine; `FinishTool`; buried under integrations | 190-line loop; exceptions-carry-messages; `role=="exit"` | Tiny state-derived loop (mini shape, OpenCode crash-safety); services around it (ADR-2) | | **TUI** | Python REPL + React/Ink TUI + JSON-RPC bridge; settled-block streaming | SolidJS + own `@opentui` engine; 16 ms delta coalescing; 100-msg cap | None (web app; Rich debug visualizer) | Rich prints; spinner | Phase 1 prototype spike; techniques (settled blocks, coalescing, bounded live region) adopted regardless (ADR-14) | | **Tools** | ~93 tools + `tool_search` bridge | 2–4 params, prose-rich descriptions; 9-replacer edit; spill-to-file | Typed Action/Observation; str_replace with line-number errors | One `bash` tool | Exactly 7 primitives; ≤5 params; replacer cascade + OpenHands failure UX (ADR-8) | | **Sessions** | SQLite, 10.9K-line armor, mid-turn flush | SQLite + drizzle; events → atomic projections | File-per-event JSON; event *tree*; resume contract | JSON dump per step; write-only | Event-sourced JSONL, one file/session; resume = replay; linear log, `parent_id`-ready (ADR-3) | | **Context** | `ContextEngine` ABC; 3-tier cached system prompt; token-triggered compressor | Token-budget overflow; compaction as persisted loop task; cheap prune | Condenser; compaction-as-event; safe cut indices; event-count trigger (weak) | None; template head/tail truncation | Hermes interface + OpenHands compaction-as-event + real-token triggers + structured YAML checkpoint (ADR-6, ADR-7) | | **Memory** | MEMORY.md/USER.md + provider plugins + background review agent | None (frecency for UI ranking only) | Microagents/skills systems (out of core) | None | **Not V1**; event-log seams left open (ADR-16) | | **Permissions** | Layered gate; unbypassable hardline floor; LLM guardian | Last-match-wins rules; tree-sitter + arity "always allow `git push *`"; in-memory `always` (flaw) | Risk/policy split; pending = unmatched action events | Mode + regex whitelist | Capability rules, last-match-wins; persisted `always`; conservative bash parsing V1 (ADR-9) | | **Workspace** | Env backends for terminal only; file tools hit FS directly | None explicit; directory-scoped instances | `BaseWorkspace`; sandbox = relocate the agent | 3-method `Environment` protocol | 4-method `Workspace`; `LocalWorkspace` only; tools never touch Node globals (ADR-13) | | **Events** | ~40-event gateway protocol; 33 ms coalescing | Durable typed events + atomic projectors; `GlobalBus` | Event log as source of truth; `visualize` welded on (wart) | None (message list is the log) | In-process typed bus; durable → JSONL, ephemeral deltas → TUI only (ADR-4) | | **Subagents** | `delegate_task`, in-process threads, no parent context | `task` → child session; deny-rule inheritance; depth 1 | Delegate tooling in SDK | None — and >74% SWE-bench anyway | **Not V1** (ADR-16) | | **Processes** | `terminal` + `process` registry: best-in-class | Gap: PTY exists, no model-facing tool | Soft-timeout terminal + `is_input` convention | Fresh subshell, 30 s kill | Model-facing `process.start/list/read/write/stop` — the differentiator (ADR-8) | | **Model abstraction** | OpenAI-dict lingua franca + adapter zoo + credential pools | `ai` SDK + per-provider transforms | 2,300-line litellm wrapper; typed error taxonomy | 164-line litellm wrapper | One `ModelClient` over Anthropic SDK; typed errors; no adapter framework (ADR-10) | | **Persistence** | SQLite WAL + fallbacks + self-repair | SQLite WAL; JSON-blob payloads; ULID ids; incremental counters | FileStore, file-per-event, lock file, autosave | `finally`-block JSON dump | Append-only JSONL, atomic line appends, flush at every loop boundary (ADR-3) | | **Git awareness** | Verify pipeline detects changed files | Shadow-git snapshots; revert/unrevert; per-step patch parts | `sdk/git` diff/changes helpers feed UI | None | Baseline capture + attribution; shadow-git deferred post-V1 (ADR-15) | | **Repository search** | grep tools; output budgets | Bundled ripgrep service; 100-result cap; frecency file-finding | ripgrep-first with logged fallback | Model runs `grep` itself | ripgrep-first, bounded structured results; trust the model to navigate; no index in V1 (ADR-8, §11) | --- ## 2. Capability Analyses ### 2.1 Agent loop **Problem.** Turn a user request into a sequence of model calls and tool executions that terminates correctly, survives crashes, and stays comprehensible. **How each reference solves it.** - *Hermes*: `run_conversation()` in `agent/conversation_loop.py` — one ~6,000-line loop body. Iteration budget with a "grace call", typed `ClassifiedError` recovery hints, dozens of inline `# ──` recovery regions, and **verification-on-stop** (`agent/verification_stop.py`): a model claiming completion after editing code gets a synthetic evidence-bearing nudge to run verify commands (max 2 attempts, withheld answer preserved). - *OpenCode*: `SessionPrompt.runLoop` re-reads persisted messages **each iteration** and derives what to do from them; compaction and subtasks are persisted parts popped as tasks; exit conditions derive from message state, so a crashed process resumes mid-conversation. The stream reducer (`SessionProcessor`, a pure event switch) is separated from the loop. Doom-loop detection: 3 byte-identical consecutive tool calls → ask the user. - *OpenHands*: `Agent.step()` is one LLM round dispatched through `classify_response()`; the outer `LocalConversation.run()` is a status state machine (`IDLE/RUNNING/PAUSED/WAITING_FOR_CONFIRMATION/FINISHED/STUCK`). Completion is an explicit `FinishTool` call. The minimal loop is buried under MCP reconciliation, critics, hooks, and vision fallbacks (~28-parameter `__init__`). - *mini-SWE*: `DefaultAgent` (190 lines): `run()` → `step()` → `execute_actions(self.query())`. All non-linear control flow is exceptions carrying the messages to append; termination is data-driven (`role == "exit"`). Scores >74% on SWE-bench Verified. **Trade-offs.** mini proves the kernel can be tiny but omits everything a product needs (streaming, resume, compaction). Hermes proves rich recovery works but shows what happens when it lives *inside* the loop. OpenCode's state-derivation buys crash-safety and steerability at the cost of per-iteration state reads. OpenHands' explicit state machine is legible but its kernel accreted integrations — the exact failure Absolute Rule #3 guards against. **KHAELOR.** mini's four-verb shape + OpenCode's state-derivation + Hermes' verification-on-stop, with error *handling* in a policy service outside the loop (Hermes proves the taxonomy; KHAELOR fixes the placement). See ADR-2, ADR-12. ### 2.2 TUI **Problem.** Render streaming model output, tool activity, diffs, and input editing in a terminal, flicker-free, at full token-stream speed. **How each reference solves it.** - *Hermes*: paid the Python-first tax — an 18.7K-line prompt_toolkit REPL *plus* a React/vendored-Ink TUI *plus* a Python↔Node JSON-RPC bridge. Its `ui-tui/` rendering discipline is excellent: settled-block incremental markdown (`StreamScanState` freezes completed blocks, re-parses only the live tail), hard live-render caps (16K chars / 240 lines — unbounded trails OOM-killed Node, issue #34095), virtualized scrollback, width-responsive status segments with pre-reserved spinner width, 33 ms delta coalescing. - *OpenCode*: built its own engine — SolidJS fine-grained reactivity over `@opentui`'s retained-mode renderable tree (Yoga flexbox → optimized cell buffer, 60 fps target). Delta-only streaming: `message.part.delta` → `produce()` append → single `` node repaint; SSE events coalesced 16 ms into `batch()`. No virtualization — a hard 100-message cap with part GC (users silently lose scrollback). Composer is extmark-based structured parts; native `` renderable; tree-sitter WASM highlighting. - *OpenHands*: **no terminal product.** A React/Electron web app; events carry a Rich `visualize` property (presentation welded into domain objects — an anti-pattern). - *mini-SWE*: Rich prints and a spinner; honest about being a research harness. Its interaction grammar (Ctrl-C → steering comment, "agent wants to finish" → prompt for next task) is worth preserving. **Trade-offs.** Framework choice is the highest-variance decision: Ink is Node-native but coarse-grained; opentui is the best-in-class renderer but Bun-adjacent; custom ANSI is fully controlled but expensive. The *techniques*, however, converge across Hermes and OpenCode independently (settled blocks, coalescing, bounded live regions) — they are framework-independent facts about terminals. **KHAELOR.** The one decision requiring a Phase 1 prototype spike, with measured criteria; techniques adopted regardless of framework. No 100-message UX cliff. See ADR-14. ### 2.3 Tools **Problem.** Give the model action primitives that are powerful, observable, safe, and cheap in context. **How each reference solves it.** - *Hermes*: ~93 registered tools (browser, video, TTS, kanban…), requiring a `tool_search` deferred-tool bridge to cope with its own count. But its tool-output economics are exemplary: per-result (100K chars) + per-turn (200K) budgets, head/tail truncation with explicit markers, spill-to-file with the path returned, ANSI stripping, secret redaction. - *OpenCode*: minimal schemas — `edit` has 4 params, `bash` 3; long guidance lives in description text files. The edit tool runs a **nine-strategy replacer cascade** (exact → line-trimmed → block-anchor Levenshtein → whitespace-normalized → indentation-flexible → escape-normalized → trimmed-boundary → context-aware → multi-occurrence) with uniqueness and disproportionate-match guards, CRLF/BOM preservation, and model-facing repair-prose errors. `Truncate.output` spills oversized output to files the model can Read/Grep. - *OpenHands*: typed Pydantic Action/Observation pairs per tool; `ToolAnnotations` behavior hints (`readOnlyHint` short-circuits risk checks). The str_replace editor's failure UX is the reference standard: multiple-occurrence errors cite line numbers, "Maybe you meant {cwd/path}?", post-edit snippet so the model self-verifies without a re-read. - *mini-SWE*: **one tool** (`BASH_TOOL`, one required param) scores >74% — proof that big agents' tool *counts* are largely accidental. But bash-only editing (`sed`/heredocs) is unobservable and unsafe outside disposable containers. **Trade-offs.** More tools = more schema tokens per call and more permission surface; fewer tools = less observability (no diffs, no capability classification). The evidence triangulates on ~7 powerful primitives with tiny schemas. **KHAELOR.** Exactly `read/write/edit/grep/glob/bash/process`. OpenCode's cascade + OpenHands' failure messages + Hermes' output budgeting. See ADR-8. ### 2.4 Sessions **Problem.** Sessions must persist across crashes and restarts, resume with full fidelity, and stay separate from agent intelligence. **How each reference solves it.** - *Hermes*: SQLite with hard-won operational armor (`hermes_state.py`, 10,888 lines): WAL with broken-build detection and DELETE-mode fallback, macOS fsync barriers, schema self-repair, incremental mid-turn flushes so a crash loses almost nothing. - *OpenCode*: SQLite + drizzle; **persistence is a projection of durable events** — `updateMessage`/`updatePart` never touch the DB, they publish events; projectors run in the same transaction. ULID-style monotonic IDs make `ORDER BY id` = insertion order. - *OpenHands*: **one JSON file per event** (`events/event-{idx}-{id}.json`), append-only, lock-file cross-process safety, lazy load, O(1) length. Events carry `parent_id` — the conversation is a *tree* (branch/rewind for free), paid for with sentinel/legacy-fallback debt (issues #4057, #3053). `AgentBase.verify()` gives a precise resume contract: same agent class, tools add-only. - *mini-SWE*: full-state JSON dump in a `finally` block every step — crash-safe *recording*, but write-only: no resume path exists. **Trade-offs.** SQLite gives transactions and queries but adds a native dependency and (Hermes shows) an armor tax. File-per-event is dependency-free but inode-heavy. Trees enable rewind but cost invariant complexity. **KHAELOR.** Append-only JSONL, one file per session — no DB dependency, no inode storm, atomic line appends; resume = replay; linear V1 log with schema room for `parent_id`. mini's `finally`-discipline applied at every loop boundary. See ADR-3. ### 2.5 Context **Problem.** Long sessions exceed the context window; the model must receive the right information, and compaction must not corrupt API invariants or destroy evidence. **How each reference solves it.** - *Hermes*: the best-factored component in its codebase — `ContextEngine` ABC separating `select_context` / `compress` / `on_turn_complete` / `prune_tool_results_only`, fed by **real API usage tokens** (`update_from_response`). Default compressor: prune tool results (cheap, no LLM) → protect head → protect recent tail by token budget → summarize the middle on an auxiliary model → iteratively update the summary, with anti-thrash guards and per-turn attempt caps. - *OpenCode*: `isOverflow()` compares real usage tokens against `usable()` (input limit minus a reserved 20K buffer); when a step overflows, a `compaction` task **part is persisted** and the loop processes it next iteration via a hidden compaction agent. Separate cheaper `prune()` blanks old tool outputs (protect newest 40K tokens) marked `time.compacted`. - *OpenHands*: the cleanest structural insight — **a `Condensation` is an event in the log**; the `View` projection re-applies it deterministically on every rebuild, cutting only at `manipulation_indices` where tool_use/tool_result pairing survives; condensation doubles as the *recovery path* for context-window errors. Weakness: the default trigger is **event count** (240), not tokens. - *mini-SWE*: none — `ContextWindowExceededError` aborts the run; the only management is template-level head/tail truncation of tool output (which is, notably, the cheap first 80%). **Trade-offs.** Compaction-in-memory (Hermes mutates the stored list, its one sanctioned cache-break) vs compaction-as-event (OpenHands: replay-deterministic, inspectable, un-condensable). Event-count triggers are simple but blind; token triggers require honest accounting. **KHAELOR.** Hermes' interface verbs + OpenHands' compaction-as-event mechanics + token triggers from real usage + KHAELOR's structured YAML checkpoint as the summary payload. Prune first, summarize second. See ADR-6, ADR-7. ### 2.6 Memory **Problem.** Knowledge that should survive across sessions. **How each reference solves it.** *Hermes* is the only serious implementation: MEMORY.md/USER.md frozen-snapshot injection (cache-conscious), a pluggable provider ABC (one external provider max), and a background review agent (forked, tool-whitelisted, cache-warm, cancelled by any new live turn) — ~10K+ lines including skills machinery. *OpenCode*: none (frecency ranks UI autocomplete only). *OpenHands*: microagents/skills exist as extension systems interleaved with the core. *mini-SWE*: none — and still scores >74%, evidence this layer is unnecessary for coding performance today. **Trade-offs.** Memory pays off for a *personal* agent across months; it costs cache-stability discipline (Hermes freezes snapshots, accepting that mid-session writes are invisible until next session) and a large safety/curation apparatus. **KHAELOR.** Not V1 (CLAUDE.md §23). The event log and kernel service boundaries leave the seam open; adopt Hermes' lessons (aux model, frozen snapshot, cancellable background work) when the time comes. See ADR-16. ### 2.7 Permissions **Problem.** Let the agent act autonomously while keeping destructive or unusual actions visible and consented to — without nagging. **How each reference solves it.** - *Hermes*: a nine-layer command gate (`check_all_command_guards`): an **unbypassable hardline floor** matched against de-obfuscated command variants (even in yolo mode), deny globs, an external analyzer binary, ~47 regex patterns, an LLM guardian, then the human — with "silence is not consent" timeout semantics. The permanent allowlist refuses commands containing shell operators. - *OpenCode*: the elegance benchmark — rules are `{permission, pattern, action}` triples, **evaluation is a 4-line `findLast`** with wildcard matching; capability-ish keys (`write` folds into `edit`); deny rules also *derive tool visibility* (plan mode = permission policy, not a different agent). Bash commands are tree-sitter-parsed and an **arity dictionary** generates precise "always allow `git push *`" suggestions; filesystem verbs trigger `external_directory` checks. Flaw: v1 `always` approvals are in-memory per session — users re-approve after restarts. Rejection-with-feedback becomes model steering (`CorrectedError`). - *OpenHands*: clean **risk/policy separation** — analyzer produces `SecurityRisk` (UNKNOWN incomparable; analysis errors default HIGH), policy decides confirm-or-not; pending approval = unmatched ActionEvents persisted in the log, so approvals survive restarts. Default analyzer is the model grading its own homework (schema-injected `security_risk` self-assessment). - *mini-SWE*: mode (`human/confirm/yolo`) + regex whitelist, ~10 lines — right-sized evaluator, wrong model for a product (no persistence, no scoping). **Trade-offs.** Regex armories are brittle primary defenses (Hermes); deterministic capability rules are predictable but need good generalization UX (OpenCode's arity) to avoid prompt fatigue; LLM self-assessment is cheap but unsound as the gate. **KHAELOR.** Capability-based rules, last-match-wins evaluator, persisted `always` grants (fixing OpenCode's flaw), conservative shell-word parsing for V1 suggestions, elegant inline panel. See ADR-9. ### 2.8 Workspace **Problem.** One seam between the agent and "the world" so remote/sandboxed execution stays possible without proxying every syscall through premature abstraction. **How each reference solves it.** *Hermes*: a clean `Environment` backend layer for terminal execution only (local/Docker/SSH/Modal…) — but file tools touch the local FS directly; the world is not one seam. *OpenCode*: no explicit workspace object; instances are directory-scoped server-side. *OpenHands*: `BaseWorkspace` (working_dir + execute/upload/download) — but tools *also* bypass it, opening files directly; sandboxing works by **relocating the whole agent** into the container (agent-server inside Docker) rather than proxying ops. *mini-SWE*: a 3-method `Environment` protocol; local↔Docker↔Singularity swaps cost ~100–150 lines each with zero kernel changes. **Trade-offs.** Per-op proxying is clean but slow and invasive; relocate-the-agent is fast but demands a server stack. The OpenHands evidence says: keep the interface *thin* so the future remote strategy can be "run KHAELOR's core remotely," not "proxy every syscall." **KHAELOR.** The 4-method `Workspace` from CLAUDE.md §9; `LocalWorkspace` only; tools depend on `Workspace`, never on Node globals directly. See ADR-13. ### 2.9 Events **Problem.** One truthful stream that the UI, persistence, and resume/replay all consume — so streaming, history, and state never diverge. **How each reference solves it.** *Hermes*: a ~40-event typed gateway protocol (message/thinking/tool/subagent/approval events) over JSON-RPC with 33 ms delta coalescing — but it exists to bridge two languages. *OpenCode*: the spine — `EventV2` typed pub/sub where definitions can be **durable** (`{durable: {aggregate, version}}`); publishing a durable event transactionally appends it to the log *and* runs projectors; UI, DB, and remote clients consume the same stream. *OpenHands*: the event log is the single source of truth; everything the LLM sees and the UI shows is a projection — but presentation (`visualize` → Rich Text with emoji) is welded onto domain events. *mini-SWE*: none; the message list is the log — viable only with one linear consumer and no streaming. **Trade-offs.** Durable/ephemeral distinction matters: persisting every text delta would bloat the log; dropping deltas entirely breaks streaming UX. Both OpenCode (16 ms) and Hermes (33 ms) independently converged on coalescing deltas into batched renders. **KHAELOR.** In-process typed bus with the CLAUDE.md §7 vocabulary; durable events → JSONL log; ephemeral deltas → TUI only, coalesced ~16 ms; rendering strictly outside event types. See ADR-4, ADR-5. ### 2.10 Subagents **Problem.** Delegate scoped work with isolation, without recursive explosions. **How each reference solves it.** *Hermes*: one `delegate_task` tool, in-process daemon-thread children inheriting parent toolsets (never model choice), **zero parent context** (bare goal+context string), depth 1 by default, push-not-poll result delivery. *OpenCode*: `task` spawns a child *session* (fresh context by design); only parent **deny** rules propagate; `task`/`todowrite` force-denied for children; `subagent_depth` default 1. *OpenHands*: delegate tooling exists in the SDK amid six extension systems. *mini-SWE*: none — plus a strong model plans in-context to >74%; strong evidence orchestration layers are accidental complexity at current model capability. **Trade-offs.** Fresh-context children are cheap and safe but poor for "continue this refactor" delegation (Hermes' own weakness). Any subagent system multiplies permission, budget, and UI surface. **KHAELOR.** Not V1. Events already carry a session id and the tool registry is data-driven, so child sessions bolt on later without kernel rewrites. See ADR-16. ### 2.11 Processes **Problem.** Dev servers, watchers, and REPLs must run *while the agent keeps working* — blocking bash makes them impossible. **How each reference solves it.** *Hermes*: the best implementation surveyed — `terminal` (foreground, 180 s default / 600 s hard cap that *redirects* long commands to background) + `process` (`list/poll/log/wait/kill/write/submit/close`), a singleton registry with 200K-char rolling buffers, 64-process LRU cap, crash checkpoints with PID-reuse guards, and completion-notification events with rate limiting. *OpenCode*: **the gap** — PTY and background-job infra exist for clients, but the model cannot start/inspect/stop long-running processes; dev-server workflows degrade to blocking bash. *OpenHands*: one persistent terminal with a **soft-timeout convention** (`exit_code = -1` "still running", `is_input` keystrokes) the model must learn from prose. *mini-SWE*: fresh subshell per command, 30 s process-group SIGKILL; long-running processes are impossible. **Trade-offs.** A single soft-timeout terminal is fewer tools but an implicit contract; an explicit process manager is more schema but honest and observable. **KHAELOR.** Strict `bash` vs `process` split; model-facing `process.start/list/read/write/stop` — Hermes validates the design almost line for line, and neither TS reference gets it right. This is a genuine differentiator. See ADR-8. ### 2.12 Model abstraction **Problem.** Talk to the model reliably: streaming, retries, typed errors, honest usage accounting, cancellation. **How each reference solves it.** *Hermes*: OpenAI-dict lingua franca + adapters for every provider + credential pools + failover chains — provider sprawl is its single largest complexity driver, leaking sanitizers throughout the loop. *OpenCode*: Vercel `ai` SDK + per-provider transforms + model-family prompt files — generality tax spread through the model layer. *OpenHands*: a 2,300-line litellm wrapper (four near-duplicate call paths) — but with the key idea intact: provider chaos mapped to a **typed exception taxonomy** (`LLMContextWindowExceedError`, …) that the loop branches on, and cost/cache tokens taken exclusively from real API metadata. Streaming is bolted on and silently degrades to off. *mini-SWE*: 164-line litellm wrapper; auto cache-control for Anthropic-looking models; **hard failure when cost cannot be computed**. **Trade-offs.** Every reference pays a universal-adapter tax KHAELOR's Anthropic-only mandate amputates. The transferable parts are exactly three: typed errors, real usage accounting, cache-control planning. **KHAELOR.** One `ModelClient` over the official Anthropic SDK; typed retryable/fatal/context-overflow taxonomy; AbortSignal cancellation; streaming first-class. See ADR-5, ADR-10. ### 2.13 Persistence **Problem.** Never lose work — including mid-turn — without a heavy storage stack. **How each reference solves it.** *Hermes*: incremental mid-turn SQLite flushes after every tool result; WAL fallbacks; schema self-repair; disk-full classification surfaced to the user. *OpenCode*: SQLite/WAL with JSON-blob payload columns, incremental token/cost counters maintained by delta at write time, keyset pagination. *OpenHands*: FileStore file-per-event + `base_state.json` with autosave-on-mutation; idempotent init (skip if a SystemPromptEvent already exists). *mini-SWE*: serialize everything in a `finally` block every iteration — durability in one line. **Trade-offs.** DBs buy queries and transactions at the cost of native deps and armor code; plain files buy simplicity at the cost of hand-rolled atomicity and derived-state rebuilds. **KHAELOR.** JSONL append with atomic line writes, flushed at every loop boundary (mini's discipline); derived metadata always rebuildable from the log. See ADR-3. ### 2.14 Git awareness **Problem.** Protect user work: know what the agent changed vs what the user had, and never destroy either. **How each reference solves it.** *Hermes*: the verify pipeline detects files changed per turn (feeding verification-on-stop) but has no baseline-protection story. *OpenCode*: the standout — **shadow-git snapshots**: a separate git dir operated against the real work tree (`objects/info/alternates` pointing at the real object DB + copied index = near-free seeding even on Chromium-sized repos), `write-tree` hashes stored in step parts, powering revert/unrevert and the diff viewer, invisible to `git status`. *OpenHands*: `sdk/git` diff/changes helpers feed the UI diff panel. *mini-SWE*: nothing. **Trade-offs.** Shadow git is powerful but nontrivial machinery (orphan repos, index copying, size filters); baseline recording (branch, dirty files, diff hash) delivers the §16 protection guarantees with a fraction of the code. **KHAELOR.** Baseline capture at session start and before first edit; explicit attribution; never auto-commit. Shadow git is the strongest post-V1 candidate on the list. See ADR-15. ### 2.15 Repository search **Problem.** Understand a repository without stuffing it into context or maintaining a stale index. **How each reference solves it.** *Hermes*: grep-style tools under strict output budgets; progressive disclosure (index-in-prompt, bodies on demand) proven on its skills corpus. *OpenCode*: a bundled ripgrep service behind `grep`/`glob`, hard 100-result caps with explicit truncation notices, and server-side fuzzy file finding with frecency ranking for `@` mentions. *OpenHands*: ripgrep-first with an explicit logged fallback to system grep; bounded structured observations. *mini-SWE*: no tooling at all — the model runs `ls`/`grep`/`find` itself and navigates repositories *well*, at the cost of repeated token-burning discovery. **Trade-offs.** Indexes (AST, embeddings) go stale and cost maintenance; honest search primitives never lie. mini demonstrates the model needs less retrieval help than agent frameworks assume. **KHAELOR.** ripgrep-first `grep`/`glob` with bounded structured results; filesystem map + git state via the Context Engine; frecency-ranked file mentions in the TUI. No AST/embeddings/symbol index in V1 (CLAUDE.md §11). --- *Author: Simon-Pierre Boucher · contact@spboucher.ai*