{"slug": "openlore-deterministic-local-first-memory-and-guardrails-for-ai-coding-agents", "title": "OpenLore: Deterministic, local-first memory and guardrails for AI coding agents", "summary": "OpenLore, a deterministic, local-first memory and guardrails system for AI coding agents, launched with a one-command install that indexes a codebase into a navigable knowledge graph in seconds, requiring no API key or LLM in the hot path. The tool auto-detects agents like Claude Code and Cursor, providing static-analysis-grounded answers and pre-change certification to prevent stale assumptions and unsafe modifications.", "body_md": "**Deterministic, local-first memory and guardrails for AI coding agents — with no LLM in the hot path.**\n\nOne call tells your agent the code a task touches; one gate tells it what's unsafe to change.\n\nGrounded in static analysis. No API key. Same answer every time.\n\n*A real, unedited recording — the published openlore on a fresh clone of ripgrep. install wires your agent and indexes the repo live — 235 files, 2,978 functions, 4,329 call edges in 14 seconds, no API key → orient returns the code a task touches → review catches a signature change that left 39 callers stale → prove projects the payoff. Re-record it yourself: docs/openlore-demo.tape.*\n\n[Install](#install-in-one-command) · [What you get](#what-you-get) · [Benchmarks](#value-scorecard--does-it-pay-for-itself) · [Governance](#governance--guardrails-on-what-your-agent-changes) · [How it works](#how-it-works) · [vs. Alternatives](#openlore-vs-alternatives) · [Docs](#documentation) · [Community](#star-history--community)\n\n**Full table of contents**\n\n**Start here:** [Install in one command](#install-in-one-command) · [What you get](#what-you-get) · [Is OpenLore for you?](#is-openlore-for-you) · [See it in action](#see-it-in-action) · [5-Minute Quickstart](#5-minute-quickstart) · [What it costs to adopt](#what-it-costs-to-adopt)\n\n**Evaluate it:** [Value Scorecard (wins and losses)](#value-scorecard--does-it-pay-for-itself) ·\n\n[OpenLore vs. Alternatives](#openlore-vs-alternatives)·\n\n[Known Limitations](#known-limitations)·\n\n[We dogfood our own governance](#we-dogfood-our-own-governance)\n\n**Understand it:** [How It Works](#how-it-works) · [Governance](#governance--guardrails-on-what-your-agent-changes) · [Core Features](#core-features) · [Languages & IaC](#languages--infrastructure-as-code) · [Federation, Interop & PR review](#federation-interop--pr-review)\n\n**Use it:** [Agent Cheat Sheet](#agent-cheat-sheet) · [Claude Code Skill](#use-openlore-as-a-claude-code-skill) · [Requirements](#requirements) · [Documentation](#documentation) · [Development](#development) · [Community](#star-history--community)\n\nAI coding agents are powerful but **amnesiac and ungoverned**. Every task starts by re-reading the same files to rediscover structure; every long session quietly drifts toward confident-but-stale assumptions; and nothing tells the agent when a change is about to break a contract, cross an architectural boundary, or open a path into sensitive code.\n\nOpenLore fixes both halves. It runs a **one-time static analysis** of your codebase and keeps a navigable knowledge graph — call structure, types, tests, decisions, IaC, and spec drift — incrementally fresh as you edit. Agents query it through **MCP tools** (or the CLI) to start every task already oriented, and to certify a change *before* it lands. It is **deterministic and local-first** — no LLM in the hot path — so the same question returns the same grounded answer, and an agent is *told when a fact has gone stale* instead of served a confident guess.\n\n```\nnpm install -g openlore && openlore install\n```\n\nThat one command **auto-detects your agent** (Claude Code, Cursor, Cline, Continue, AGENTS.md), **wires it to call orient() automatically**,\n\n**registers the MCP server**, and\n\n**builds the index**— no API key, no config, no questions asked. Then ask your agent:\n\n`orient(\"add a payment method\")`\n\n…and it begins the task already knowing the relevant functions, their callers, the matching specs, the tests, and the risk of changing each one — in a single call. Full setup, variants, and verification: [5-Minute Quickstart](#5-minute-quickstart).\n\nZero config, everything discoverable.Core value needs no keys. To see everyopt-incapability — embeddings, covering surfaces, the commit gate, the spec store, and more — whether each is active, and the one command to turn it on, run.`openlore features`\n\nOpenLore does two things for an agent, both deterministic and local — it **remembers** your architecture so every task starts oriented, and it **governs** what the agent changes before the change lands.\n\n**🧠 Memory — start every task already oriented**\n\n**Persistent architectural memory**—`orient()`\n\nonce; agents stop re-deriving the system from dozens of file reads, across sessions. Anchored notes and decisions**survive refactors**: a renamed or moved symbol carries its memory forward at the next`analyze`\n\n(with`carriedAcross`\n\nprovenance) instead of orphaning it.**One-call orientation**—`orient(task)`\n\nreturns the relevant functions, their callers, matching spec sections, and insertion-point candidates in a single call.**~430µs p50** on a 15k-node graph.**One unified graph**— application code,** Infrastructure-as-Code**, and** architectural decisions**all project onto the same node/edge primitives, so a single traversal answers questions that span all three.** Test-impact selection & dead-code**— \"I changed`parseConfig()`\n\n— which tests should I run?\" by backward call-graph reachability; cross-language mark-and-sweep finds what's dead, confidence-tagged, never deletion authority.**Context-freshness tracking (Epistemic Lease)**— an agent is*told*when a cached fact has gone stale (context aged, repo moved) instead of acting on a confident guess.\n\n**🛡️ Governance — guardrails on what the agent changes**\n\n**Change-impact certificate**—`change_impact_certificate`\n\nflags when a diff**newly opens a path into a sensitive boundary you declared**(reachable after the change but not before) — differential, deterministic, no LLM.** Breaking-change verdict**—`certify_public_surface`\n\nclassifies every changed export`breaking / non-breaking / potentially-breaking`\n\nover a diff and names the in-repo consumers each break hits; conservative by construction, never silently \"safe\".**Architecture invariants, pre-edit**—`check_architecture`\n\nanswers \"may a file under A import B?\" against your declared layer/forbidden rules*before*the import is written — cross-language.**Grounded claims, not guesses**—`verify_claim`\n\nreturns a deterministic`confirmed / refuted / unverifiable`\n\nverdict with a citation receipt before an agent asserts \"X is dead\" or \"Y is safe to change\".**One commit gate**—`openlore enforce`\n\nresolves every governance finding through your`enforcement.policy`\n\nand blocks only on what you class`blocking`\n\n(advisory by default, no API key). Decisions are recorded, gated, and synced into living specs; spec/code**drift detected in milliseconds**.\n\n**📊 Honest by construction** — **−26% agent round-trips** on deep traces in large repos, with the losses published next to the wins; every public claim traces to a command you can run. Pure static analysis: no API key, no network, same answer every time.\n\nThe fastest way to evaluate a tool is to find out quickly that it isn't for you. So:\n\n✅ Strong fit |\nA codebase big enough that you can't hold it in your head — and neither can the model. Private or niche code the model never memorized. Long agent sessions where stale assumptions compound. Polyglot repos, or code plus the IaC that deploys it. Anywhere \"the agent changed something it shouldn't have\" is a real cost, not a hypothetical. |\n🤔 Try it, but measure |\nMid-size repos and mixed workloads. The orientation win scales with size and depth, so run (seconds, no API key) before you commit to it.`openlore prove --estimate` |\n❌ Probably not yet |\nA small repo the model already knows, answering shallow questions like \"who calls `parseArgs` \" — your agent's built-in search is cheaper, and we\nperform the refactor, OpenLore is the wrong layer — it locates and certifies, it never edits your code. |\n\n**One idea, if you only read one line of this README:** an agent's expensive failure mode isn't ignorance — it's *confidence*. A model that doesn't know a function exists will go look. A model that \"knows\" a stale fact will confidently build on it, and you pay for that at review time. OpenLore is built so the agent can be told **\"that fact is stale\"** and **\"this change opens a path you said was sensitive\"** — deterministically, from the graph, with no second model in the loop guessing about the first.\n\n**The same task, twice.** Ask an agent to add a flag to a command it has never seen:\n\n| Without OpenLore | With OpenLore | |\n|---|---|---|\nOpening move |\ngrep a guessed name → open a file → wrong layer → open three more | `orient(\"add a --since flag to the blast-radius command\")` |\nWhat it learns |\nfile contents, one at a time, in whatever order it guessed | the functions, their callers, the matching spec sections, and the ranked insertion points — in one call |\nWhat it misses |\nthe five callers living in files it never opened | every caller the graph can see — statically resolvable ones, at least; dynamic dispatch is still nobody's friend |\nBefore it commits |\n\"looks right to me\" | `blast_radius` → tests to run; `certify_public_surface` → the consumers this signature change breaks, by name |\n\nThe measured effect of that shape change on deep, multi-hop tasks: **25 → 16 round-trips** on excalidraw, **−26%** aggregate. It is not magic — it is the difference between *rediscovering* structure per task and *querying* it. Full numbers, including where this **doesn't** pay off, in the [Value Scorecard](#value-scorecard--does-it-pay-for-itself).\n\n**orient(\"add a --since flag to the blast-radius command\")** — one query replaces most exploratory file reads\n\nReal output — `openlore orient --json \"add a --since flag to the blast-radius command\"`\n\n, run on **this** repo (abridged: fields elided, caller lists flattened to names):\n\n```\n{\n  \"relevantFiles\": [\"src/cli/commands/blast-radius.ts\", \"src/core/services/mcp-handlers/blast-radius.ts\"],\n  \"relevantFunctions\": [\n    { \"name\": \"computeBlastRadius\", \"filePath\": \"src/core/services/mcp-handlers/blast-radius.ts\",\n      \"signature\": \"async function computeBlastRadius(input: BlastRadiusInput): Promise<BlastRadiusBriefing>\",\n      \"fanIn\": 5, \"isHub\": true, \"language\": \"TypeScript\" }\n  ],\n  \"callPaths\": [\n    { \"function\": \"computeBlastRadius\",\n      \"callers\": [\"handleBlastRadius\", \"computeImpactCertificate\", \"runBlastRadiusCli\",\n                  \"composeReview\", \"collectGovernanceFindings\"] }\n  ],\n  \"landmarks\": [\n    { \"name\": \"runBlastRadiusCli\", \"hops\": 1,\n      \"signals\": [{ \"label\": \"orchestrator\", \"evidence\": { \"fanOut\": 11 } },\n                  { \"label\": \"volatile\", \"evidence\": { \"level\": \"medium\", \"commits\": 6, \"coChangedWith\": 5 } }] }\n  ],\n  \"insertionPoints\": [\n    { \"rank\": 2, \"name\": \"computeBlastRadius\", \"role\": \"hub\", \"strategy\": \"cross_cutting_hook\",\n      \"reason\": \"computeBlastRadius is called by 5 functions -- adding logic here affects the entire callsite surface.\" }\n  ],\n  \"suggestedTools\": [\"record_decision\", \"analyze_impact\", \"get_subgraph\", \"check_spec_drift\"]\n}\n```\n\nThe agent knows exactly where to look, what it touches, and what's risky to touch — before reading a single file. Every field is computed from the graph; nothing is inferred by a model.\n\n**Gate a risky change before it lands** — deterministic, in CI or a pre-commit hook\n\n```\nopenlore impact-certificate --base main      # does my diff open a new path into a declared sensitive boundary?\nopenlore certify-public-surface --base main  # did I break a consumer's public API contract?\nopenlore blast-radius                        # callers/layers touched, tests to run, specs & decisions that drift\nopenlore enforce --hook                       # one gate; blocks only on findings you've classed `blocking`\n```\n\nNo LLM, no API key — the same grounded answer every run. Advisory by default; you opt into blocking per finding.\n\nOpenLore only earns its place if an agent **with** it reaches a correct answer for less total cost than the same agent **without** it. We measure that inequality and publish it — wins **and** losses. Numbers are from the Spec 14 agent benchmark (`claude -p`\n\n, sonnet, N=4 medians, pinned SHAs, `--strict-mcp-config`\n\nisolating each arm), measured **2026-06-01**.\n\n| Scenario (task × repo) | Cost Δ | Round-trips Δ | Correctness | Verdict |\n|---|---|---|---|---|\nLarge/unfamiliar repo · deep \"how does X flow through Y\" (its target) |\n−7% to −21% |\n−26% |\n100% = 100% | ✅ helps — and the win grows with repo size |\n| Small/familiar repo · shallow \"who calls X\" | task-dependent (Round 1: +43%) |\n+38% |\n100% = 100% | ❌ often adds overhead — measure with `openlore prove` |\n\nRe-confirmed live 2026-06-03 (N=2):the deep-task winreproduces— okhttp−13%. The small/familiar case istask-dependent, not a flat loss: same repo class, opposite outcomes (chalk−32%win vs express+59%loss). Don't guess from our repos — runon yours.`openlore prove`\n\nDeep-trace detail — the win scales with codebase size (cost Δ; round-trips WITHOUT → WITH):\n\n| Repo (size) | Cost Δ | Round-trips |\n|---|---|---|\n| excalidraw (~640 files) | −21% |\n25 → 16 |\n| tokio (~790 files) | −21% |\n17 → 13 |\n| okhttp | −13% |\n13 → 11 |\n| django (~3k files) | −7% |\n21 → 15 |\n| gin (110 files, smallest) | +4% (≈even) |\n10 → 9 |\n\n**Where it helps — and where it doesn't:**\n\n**Helps:** large, unfamiliar, or private codebases the model hasn't memorized; deep multi-hop questions; long sessions where re-reading an ever-growing context compounds. The hardest-to-game signal is**round-trips: −26%, fewer on every deep task.****Doesn't (yet):** small, famous repos already in the model's weights answered by a shallow query — there's no orientation tax to remove, so the tool surface is pure overhead.\n\nHonesty contract.We never publish a savings number the benchmark didn't produce; we always show the loss cases next to the wins; the scorecard is date-stamped and re-measured after each optimization phase. Every public token claim traces to a command you can run in this repo — if it doesn't reproduce, treat it as marketing and call it out. Full methodology and per-task numbers:[docs/AGENT-BENCHMARKS.md]. Plumbing latency (orient ~430µs p50) is separate and real:[scripts/BENCHMARKS.md].\n\nProve it on your repo — no API key needed.`openlore prove --estimate`\n\nprojects the orientation tax from your own call graph in seconds (zero API key, zero network); plain`openlore prove`\n\nruns the full measured WITH/WITHOUT pass (needs`claude`\n\n+ a key). Add`--json`\n\n(CI-consumable),`--markdown`\n\n(a paste-ready scorecard block + a shields.io badge for your README), or`--save`\n\n(a dated record under`.openlore/prove/`\n\n). An estimate is labeled`estimate`\n\neverywhere and never presented as a measurement. Details:[docs/AGENT-BENCHMARKS.md].\n\n```\nnpm install -g openlore\ncd /path/to/your-project\n\nopenlore install          # detect your agent, wire it up, AND build the index\n```\n\nThat single command:\n\n**Auto-detects** which agent surfaces are present (Claude Code, Cursor, Cline, Continue, AGENTS.md) and wires each one to call`orient()`\n\n— no manual`CLAUDE.md`\n\nediting.**Registers the MCP server** so it starts automatically when your agent launches (you don't run`openlore mcp`\n\nyourself).**Builds the index**(`init`\n\n+`analyze`\n\n→ a keyword/BM25 graph, no network needed) so`orient()`\n\nreturns real results in your very first session.**Wires task-scoped orientation**(Claude Code): a`UserPromptSubmit`\n\nhook orients each new prompt and injects a bounded, ignorable orientation block*before*the first turn — so the common task begins already oriented without a manual`orient()`\n\ncall. A deterministic relevance gate keeps it out of the small/familiar case; disable with`contextInjection.mode: \"off\"`\n\n.\n\n```\nopenlore install --no-analyze   # wire surfaces only; build the index later\nopenlore install --dry-run      # preview every change without writing\nopenlore doctor                 # verify config, index, MCP wiring, and embedding setup\n```\n\n**Zero-interaction by design.** Nothing prompts you and nothing touches your repo on `npm install`\n\n. `openlore install`\n\nnever asks a question. And if an agent wires the MCP server *without* a prior install, the server **self-bootstraps** — it builds the index once in the background on first run (opt out with `OPENLORE_NO_AUTO_ANALYZE=1`\n\n).\n\n**Stays current automatically.** A once-a-day, non-blocking \"update available\" line; upgrade with ** openlore update** (detects npm vs. Homebrew vs. npx). Silence with\n\n`OPENLORE_NO_UPDATE_NOTIFIER=1`\n\n. The MCP server keeps the index fresh as you edit (file watcher on by default; build dirs like `target/`\n\n, `node_modules/`\n\n, `dist/`\n\nare pruned automatically). See [docs/install.md](/clay-good/OpenLore/blob/main/docs/install.md).\n\n**Full pipeline** (specs + decisions — optional and additive):\n\n```\nopenlore generate         # generate living specs (requires API key)\nopenlore drift            # detect spec/code drift (no API key)\nopenlore decisions        # manage architectural decisions\n```\n\n## Install from source\n\n```\ngit clone https://github.com/clay-good/openlore\ncd openlore\nnpm install && npm run build && npm link\n```\n\n## Nix / NixOS\n\n```\nnix run github:clay-good/openlore -- analyze\nnix shell github:clay-good/openlore\n```\n\nSystem flake:\n\n```\nenvironment.systemPackages = [ openlore.packages.x86_64-linux.default ];\n```\n\nEvery tool asks for something. Here is exactly what OpenLore asks for, measured on a **fresh clone of ripgrep** with the published\n\n`openlore@2.1.6`\n\n— reproduce it in about a minute:\n\n```\ngit clone --depth 1 https://github.com/BurntSushi/ripgrep && cd ripgrep\nnpx openlore init && time npx openlore analyze && du -sh .openlore\n```\n\n| What it costs | On ripgrep (232 source files indexed) |\n|---|---|\nOne-time index build |\n13.6 s, entirely local — no API key, no network call |\nDisk |\n27 MB under `.openlore/` (gitignorable; the graph is a pure function of your source, so it's always rebuildable) |\nPer-query latency |\n~430 µs p50 in-process via the MCP server; a cold one-shot CLI call is ~2 s, nearly all of it Node startup and opening the index |\nKeeping it fresh |\nautomatic — the file watcher re-indexes the changed file's dependency closure on save |\nYour source code |\nnever leaves the machine. No account, no telemetry (opt-in only), no hosted index |\nLock-in |\nnone — it's one gitignored directory. Delete `.openlore/` and nothing about your repo has changed. |\n\nLarge monorepos take minutes rather than seconds; that limit is stated plainly in [Known Limitations](#known-limitations).\n\nMigrating from\n\n`spec-gen`\n\n? The package is now[and the command is]`openlore`\n\n`openlore`\n\n— see[docs/RENAME-TO-OPENLORE.md]for the short checklist.\n\nMemory makes an agent fast. Governance makes it *safe*. As agents get more autonomous, the bottleneck moves from \"can it write the code\" to \"can I trust what it just changed.\" OpenLore answers that **deterministically** — every check below is static analysis, no LLM, **advisory by default with opt-in blocking** — and all of it rides the one graph, so it spans application code, IaC, and your recorded decisions at once.\n\n| Guardrail | What it certifies | Run it |\n|---|---|---|\n`change_impact_certificate` |\nWhether a diff newly opens a path into a sensitive boundary you declared — reachable after the change but not before (a differential, not a snapshot) — plus blast radius, drifted specs, and the tests to run. |\n`openlore impact-certificate --base main` |\n`certify_public_surface` |\nA breaking-change verdict over a diff: each changed export `breaking / non-breaking / potentially-breaking` , each break paired with the in-repo consumers it hits. Conservative — what it can't prove safe is never called safe. |\n`openlore certify-public-surface --base main` |\n`check_architecture` |\n\"May a file under A import B?\" against your declared layer / forbidden / allowed-only rules — a pre-write verdict, cross-language, instead of a post-hoc CI failure. |\ndeclare rules in `.openlore/architecture.json` |\n`verify_claim` |\nA deterministic `confirmed / refuted / unverifiable` verdict with a citation receipt before an agent asserts \"X is dead\", \"Y is safe to change\", or \"decision `abc12345` still governs this\". |\nMCP tool (`verify` preset) |\n`openlore enforce` |\nOne commit gate over every governance finding. Map each finding `code` → `blocking / advisory / off` in `enforcement.policy` ; the gate blocks only on what you class blocking. |\n`openlore enforce --hook` |\nEpistemic Lease |\nTells the agent when its context has gone stale (aged, or the repo moved) so a long session can't drift onto confident-but-wrong assumptions. Facts, never commands. |\nautomatic on every MCP response |\n\nA reviewer doesn't even need an agent: ** openlore review --base main** composes the structural delta and the blast radius into one Markdown briefing, and the bundled GitHub Action posts it as a single sticky PR comment (advisory by default). See\n\n[PR review](#pr-review-no-agent-required).\n\nThe default MCP surface is the ** substrate** preset — 13 tools: the navigation graph-traversal core plus the three highest-value governance reads (\n\n`recall`\n\n, `verify_claim`\n\n, `blast_radius`\n\n), so an out-of-box agent gets the read face on top of navigation (the write face — `remember`\n\n, `record_decision`\n\n— stays opt-in via `--preset memory`\n\n/`minimal`\n\n/`full`\n\n). It cleared the DefaultSurfaceRevealsAllFaces benchmark — no task-completion or tool-selection regression vs. the lean core across two models and both repo tiers. The lean navigate-only **preset (10 tools) stays a one-flag escape (**\n\n`navigation`\n\n`--preset navigation`\n\n), and the full surface of **73 tools** is opt-in via\n\n`--preset full`\n\n. Every tool declares one of six **capability families**—\n\n`navigate`\n\n· `change`\n\n· `remember`\n\n· `verify`\n\n· `coordinate`\n\n· `federate`\n\n— surfaced in its MCP `annotations.family`\n\n, so a wide surface stays discoverable by family rather than as a flat list. Reach for the right tool by situation:| Situation | Tool |\n|---|---|\n| Starting any task | `orient(task)` — functions, callers, specs, insertion points in one call |\n| Shallow \"who calls X / where is Y?\" | `orient(task, lean:true)` — navigation core only, ~40% smaller |\n| \"Which file/function handles X?\" | `search_code` |\n| \"What's the blast radius if I change this?\" | `analyze_impact` — risk score + up/downstream chain + governing decisions |\n| \"How does request X reach function Y?\" | `trace_execution_path` |\n| \"I changed X — which tests should I run?\" | `select_tests` — backward reachability to the reaching tests + paths |\n| \"What's dead / what dies if I delete X?\" | `find_dead_code` — cross-language reachability, confidence-tagged |\n| \"What's the blast radius of my whole diff before I commit?\" | `blast_radius` — callers/layers, tests to run, anchored memories & specs that drift |\n| \"Does my diff open a new path into a sensitive boundary?\" | `change_impact_certificate` — differential reachability into declared surfaces |\n| \"Did my change break a consumer's public API contract?\" | `certify_public_surface` — breaking-change verdict, consumers named |\n| \"May I add this import here?\" | `check_architecture` — pre-edit verdict against declared rules |\n| About to assert a fact / cite a decision | `verify_claim` — deterministic verdict + citation receipt |\n| Recording an architectural choice | `record_decision` before writing the code |\n| \"What changed structurally / whose callers are stale?\" | `structural_diff` — graph diff, stale callers, rename flags |\n| \"What changes together with this / what's volatile?\" | `get_change_coupling` — co-change + churn from git |\n| Reading / checking a spec | `get_spec` · `search_specs` · `check_spec_drift` |\n| \"A lot changed since I last looked — what matters?\" | `briefing_since` — changed symbols ranked by structural significance |\n| \"Does a near-duplicate of this already exist to reuse?\" | `find_clones` — edit-time, scoped clone query |\n\nEverything else (read a file, grep, list files) uses your native tools. Full reference — all 73 tools and parameters: [docs/mcp-tools.md](/clay-good/OpenLore/blob/main/docs/mcp-tools.md).\n\nOpenLore ships a canonical [Claude Code Skill](https://docs.claude.com/en/docs/claude-code/skills) at [ skills/openlore-orient/](/clay-good/OpenLore/blob/main/skills/openlore-orient). Install it once and Claude Code calls\n\n`orient()`\n\nat the start of every task — no `CLAUDE.md`\n\nediting.\n\n```\nnpm run skill:install-local           # → ~/.claude/skills/openlore-orient/\ncp -R skills/openlore-orient /path/to/your-project/.claude/skills/   # or per-project\n```\n\nThe 8 multi-agent **workflow** skills (brainstorm, plan-refactor, write-tests, implement-story, debug, …) install via `openlore setup`\n\ninto `.claude/`\n\n, `.opencode/`\n\n, or `.vibe/`\n\n. See [ skills/openlore-orient/README.md](/clay-good/OpenLore/blob/main/skills/openlore-orient/README.md).\n\nA good tool should tell you when *not* to use it. So here is the honest map of the space.\n\nEveryone in this category is answering the same first question: **\"how does the agent see the codebase without reading it file by file?\"** LSP toolkits answer it with symbols. Graph MCP servers answer it with a parsed graph. Search platforms answer it with an index. They are all real answers, and several of them are good.\n\nOpenLore answers it too — and then keeps going into the **second question almost nobody is answering: what happens when the agent starts writing?** A retrieval layer makes an agent *informed*. It does not make the agent *safe*. Nothing in a symbol index tells you that this diff just opened a path into your auth boundary, that this signature change breaks four consumers by name, that the decision governing this module was superseded last month, or that the fact your agent has been confidently using for the last 40 tool calls went stale 12 commits ago. That half of the problem — **governance, on the same graph, with no LLM in the loop** — is the part OpenLore was built for.\n\nAgent built-ins(Cursor, Claude Code) |\nLSP toolkits(e.g. Serena) |\nGraph MCP servers(e.g. CodeGraph) |\nSearch platforms(e.g. Sourcegraph) |\nOpenLore |\n|\n|---|---|---|---|---|---|\n| Structural context instead of file reads | ❌ grep + file reads | ✓ symbols via LSP | ✓ parsed graph | ✓ index | ✓ call graph + clusters + IaC + decisions on one graph |\n| Local, no API key, deterministic | Partial | ✓ | ✓ | ❌ hosted/indexed service | ✓ no LLM in the hot path |\n| Cross-session memory anchored to code | ❌ | Partial (notes) | ✓ stored notes | ❌ | ✓ anchored to a symbol — carried across renames/moves, self-invalidating |\nTold when a cached fact goes stale |\n❌ | ❌ | ❌ | ❌ | ✓ Epistemic Lease |\n| Blast radius + which tests to run | ❌ | ❌ references only | Partial | Partial | ✓ backward reachability, with reaching paths |\nBreaking-change verdict over a diff |\n❌ | ❌ | Partial (impact heuristics) | ❌ | ✓ per export, consumers named, conservative by construction |\n| \"Did this diff open a new path into a sensitive boundary?\" | ❌ | ❌ | ❌ | ❌ | ✓ differential reachability, pre-commit |\nArchitecture invariants before the import is written |\n❌ | ❌ | ❌ | ❌ | ✓ cross-language |\n| Spec/code drift + ADRs gated at commit | ❌ | ❌ | ❌ | ❌ | ✓ milliseconds, no API key |\n| One policy-driven commit gate | ❌ | ❌ | ❌ | ❌ | ✓ `enforcement.policy` , advisory by default |\n| Claims backed by a citation receipt | ❌ | ❌ | ❌ | ❌ | ✓ `confirmed / refuted / unverifiable` |\nCost/round-trip effect published with the losses |\n❌ | ❌ | ❌ | ❌ | ✓ −7%→−21% cost, −26% round-trips on deep tasks † |\n\n**Where the others are genuinely the better pick** — we would rather you use the right tool than ours:\n\n**Doing surgical, symbol-level**(rename across files, move a symbol, replace a body) — that is an*edits***LSP toolkit's** home turf. OpenLore is deliberately**read-only**: it locates and certifies, it does not refactor for you. The two compose well.** Search across hundreds of repos, org-wide, in a browser, with an audit trail**— that is a** code search platform**. OpenLore is local-first and repo-scoped by default (federation is opt-in and read-only).** You just want fast graph retrieval and nothing else**— a** graph MCP server**is a smaller surface to adopt, and several are excellent at it. OpenLore's extra weight is governance; if you do not want a commit gate, drift detection, or change certificates, you are paying for capability you will not use.**A small, familiar repo and shallow questions**(\"who calls X\") — your agent's built-in search is often*cheaper*. We measured this and published it below; we did not hide it.\n\n**What you cannot get anywhere else:** one graph where your **code, your infrastructure, and your architectural decisions are the same node type** — so a single traversal answers \"what breaks, what does it cost, what governs it, and is that still true?\" — and a substrate that would rather tell you *\"I don't know, this is stale\"* than hand your agent a confident guess.\n\n† **Measured, and it depends on the task** — full numbers in the [Value Scorecard](#value-scorecard--does-it-pay-for-itself). Small/familiar repos + shallow queries *add* overhead; larger codebases + deep questions are a net win (−7%→−21% cost, −26% tool-calls, scaling with repo size), at 100% answer correctness in both arms. The savings hold where OpenLore is designed to help, not on toy queries.\n\n*Comparisons reflect each project's publicly documented capabilities as of July 2026 and describe categories, not verdicts on quality; these are fast-moving projects, and a correction PR is always welcome. Named examples: Serena (MIT), CodeGraph (Apache-2.0), codebase-memory-mcp, Sourcegraph. OpenLore exports SCIP, so it sits alongside them rather than against them.*\n\nThree layers, each usable independently:\n\n| Layer | What it does | API key? |\n|---|---|---|\n1. Static Analysis |\nCall graph, clusters, McCabe CC, IaC, external deps → `CODEBASE.md` digest |\nNo |\n2. Spec & Governance |\nLiving specs, ADRs, drift detection, change certificates, decision & finding gates | For spec generation only |\n3. Agent Runtime |\n73 MCP tools — `orient()` , graph traversal, semantic search, verdicts & gates |\nNo |\n\nUse layer 1 alone for structural context. Add layer 2 for semantic intent and governance. Layer 3 keeps it all continuously accessible through graph-native MCP tools once `openlore mcp`\n\nis running.\n\n``` php\nflowchart TD\n    Code[Codebase] --> Analyze[openlore analyze<br/>tree-sitter · pure static analysis]\n    Analyze --> DB[(SQLite graph store)]\n    Analyze --> Digest[CODEBASE.md<br/>~600-token structural digest]\n\n    subgraph shared[\"Projected onto shared node + edge primitives\"]\n      direction LR\n      CodeNodes[functions + call edges]\n      Iac[IaC resources + references]\n      Dec[decisions + affects edges]\n    end\n    Analyze --> CodeNodes\n    Analyze --> Iac\n    Analyze -. active decision store .-> Dec\n    CodeNodes --> DB\n    Iac --> DB\n    Dec --> DB\n\n    DB --> MCP[73 MCP tools<br/>orient · analyze_impact · certify · verify · enforce]\n    MCP --> Agent((Coding Agent))\n\n    Code -. optional, API key .-> Gen[openlore generate]\n    Gen --> Specs[openspec/specs/*.md<br/>RFC 2119 living specs]\n    Code --> Drift[openlore drift<br/>spec/code drift, ms, no API]\n    Agent -. record_decision .-> Gate[decisions + findings gate]\n    Gate --> Specs\n```\n\nCrucially, application code, Infrastructure-as-Code, and architectural **decisions** all project onto one shared set of node/edge primitives — so a single traversal answers questions that span all three, and impact analysis returns governance as a graph neighbor. See [docs/ARCHITECTURE.md](/clay-good/OpenLore/blob/main/docs/ARCHITECTURE.md).\n\n*Skimmable by design: each bold lead-in is one capability, with the command that runs it and the doc that explains it. Everything is deterministic and local; only the two entries marked \"API key\" ever talk to a model.*\n\n**Analyze** *(no API key)* — Continuously maintains a structural representation using pure static analysis: a full call graph in SQLite, label-propagation community detection, McCabe complexity per function, and extracted DB schemas, HTTP routes, UI components, middleware, and env vars. Outputs `.openlore/analysis/CODEBASE.md`\n\n— a ~600-token digest that compresses tens of thousands of exploratory tokens. The MCP server's file watcher (`--watch-auto`\n\n, on by default) updates the graph incrementally on every save and **converges to what analyze --force would produce**; when a change's reverse-dependency closure exceeds the per-save budget, the un-recomputed files are marked\n\n**explicitly stale** rather than left silently divergent.\n\n**Generate** *(API key)* — Sends the analysis to an LLM in 6 structured stages (survey → entities → services → APIs → architecture → ADRs), producing `openspec/specs/`\n\nliving specifications in RFC 2119 with Given/When/Then scenarios.\n\n**Drift** *(no API key)* — Compares git changes against spec mappings in milliseconds: Gap, Uncovered, Stale, and ADR-gap. Installs as a pre-commit hook. → [docs/drift-detection.md](/clay-good/OpenLore/blob/main/docs/drift-detection.md)\n\n**Test impact selection** *(no API key)* — `select_tests`\n\nwalks the call graph **backward** from a change to every test that transitively reaches it, returning each test with its reaching path. Static, call-graph-based regression test selection at edit time, not after CI. An honest over-approximate prioritizer (\"run these first\"), not a sound replacement for the full suite. → [docs/test-impact-selection.md](/clay-good/OpenLore/blob/main/docs/test-impact-selection.md)\n\n**Reachability & dead-code** *(no API key)* — `find_dead_code`\n\nruns cross-language mark-and-sweep from roots (tests, imports, route handlers, `main`\n\n), and answers \"what becomes dead if I delete X?\" Results are **confidence-tagged candidates, never deletion authority**. The import resolver follows re-export/barrel chains and Python relative imports so a call resolves precisely. → [docs/reachability-dead-code.md](/clay-good/OpenLore/blob/main/docs/reachability-dead-code.md)\n\n**Change-impact certificate** *(no API key)* — `change_impact_certificate`\n\ncertifies whether a diff **newly opens a path into a declared covering surface** (differential reachability — reachable after but not before), plus blast radius, drifted specs, and tests to run. Decays via the freshness lease. Advisory; opt-in blocking on a configured surface severity. CLI: `openlore impact-certificate`\n\n.\n\n**Public API surface contract** *(no API key, opt-in)* — `certify_public_surface`\n\ngives a deterministic breaking-change verdict over a diff: each changed export `breaking / non-breaking / potentially-breaking`\n\n, each break paired with the in-repo consumers it hits. Conservative — a change it can't prove compatible is `potentially-breaking`\n\n, never silently safe. Renamed exports detected via symbol-identity continuity. CLI: `openlore certify-public-surface`\n\n.\n\n**Architecture invariant guardrails** *(no API key)* — `check_architecture`\n\nturns an architectural rule from a post-hoc CI failure into a **pre-write** verdict. Declare `layers`\n\n/ `forbidden`\n\n/ `allowedOnly`\n\nin `.openlore/architecture.json`\n\n(or via an `Invariant:`\n\nmarker on a synced ADR), and the tool answers \"may a file under A import B?\" with a deterministic verdict + the governing rule. Cross-language over the unified graph. → [docs/architecture-invariants.md](/clay-good/OpenLore/blob/main/docs/architecture-invariants.md)\n\n**Claim verification** *(no API key, opt-in)* — `verify_claim`\n\nreturns `confirmed / refuted / unverifiable`\n\nwith a citation receipt (index commit, content hashes, the reaching path) — never an LLM guess. Structural kinds check the call graph; `decision-current`\n\nchecks a recorded decision is still authoritative and names the live superseder if it isn't.\n\n**Finding enforcement** *(no API key, advisory by default)* — `openlore enforce`\n\nis the **unified gate over all governance findings**. A single `enforcement.policy`\n\nblock maps each finding `code`\n\n→ `blocking / advisory / off`\n\n, decoupling a finding's intrinsic severity from your risk posture. The `--hook`\n\ngate fails a commit only on a `blocking`\n\nfinding; a repo that declares no policy never blocks. It always runs the deterministic `stale-decision-reference`\n\ncheck and folds in blast-radius and impact-certificate findings where configured. → [docs/configuration.md](/clay-good/OpenLore/blob/main/docs/configuration.md#enforcement-policy)\n\n**Decisions on the graph** *(API key for consolidation)* — Agents call `record_decision`\n\nbefore writing code; a pre-commit hook gates the commit until verified decisions are reviewed and written back as requirements. Decisions are also **first-class graph nodes**: projected into `decision::<id>`\n\nnodes joined to the files they govern by `affects`\n\nedges, so `analyze_impact`\n\nand `get_subgraph`\n\nreturn the governing decisions of a symbol and its blast radius as typed neighbors. → [docs/specs/openlore-spec-16-decisions-as-graph-nodes.md](/clay-good/OpenLore/blob/main/docs/specs/openlore-spec-16-decisions-as-graph-nodes.md)\n\n**Epistemic Lease** *(no API key)* — Models architectural drift as a **navigation** phenomenon, not a knowledge one: decay is driven by where the agent goes (cross-module trajectory), time since `orient()`\n\n, and weighted cognitive load. Once context ages or the repo moves, every MCP response carries a brief, factual freshness note — *\"informational signal; you decide whether to act on it\"* — never a command. Calling `orient()`\n\nresets it; when fresh, injection is zero-overhead.\n\n**Structural change analysis** *(no API key)* — `structural_diff`\n\nis the structural complement to `git diff`\n\n: functions and edges added/removed, signature changes, and the callers in *other* files now **stale** because a callee's signature moved. → [docs/structural-diff.md](/clay-good/OpenLore/blob/main/docs/structural-diff.md)\n\n**Change-coupling & volatility** *(no API key)* — `get_change_coupling`\n\nmines two facts the call graph can't see: co-change coupling (\"these files always change together\" — invisible coupling with no import edge) and churn (\"changed 23 times\" — a risk flag). Advisory, correlation not causation. → [docs/change-coupling.md](/clay-good/OpenLore/blob/main/docs/change-coupling.md)\n\n**Test-coverage gaps** *(no API key, opt-in)* — `report_coverage_gaps`\n\nanswers the inverse of `select_tests`\n\n: **which load-bearing code has no test reaching it at all?** Walks forward from every test, reports the code *outside* that set, ranked by hub/chokepoint significance. No test run, no instrumentation. Gaps-only and honest — it never claims a symbol is \"tested\". CLI: `openlore coverage-gaps`\n\n. → [docs/coverage-gaps.md](/clay-good/OpenLore/blob/main/docs/coverage-gaps.md)\n\n## More tools — style fingerprint, clone query, error & env-var impact, change briefing, parallel-work planning…\n\n— a descriptive, deterministic idiom profile (arrow vs. declared function,`get_style_fingerprint`\n\n`const`\n\n/`let`\n\n, naming case…) so an agent matches the house style. Honest by construction: a counter below the evidence floor, or a choice the compiler enforces, reports a null signal, never a guess.— the edit-time, scoped \"does a near-duplicate of`find_clones`\n\n*this*already exist?\" companion to the whole-repo`get_duplicate_report`\n\n. One query (a symbol or a raw snippet), clones ranked exact > structural > near. CLI:`openlore find-clones`\n\n.— the exceptions that escape a function vs. those caught within it (TS/JS/Python). A sound lower bound — un-analyzable callees disclosed, never assumed exception-free. CLI:`analyze_error_propagation`\n\n`openlore error-propagation`\n\n.— \"what breaks if I remove this env var?\": line-precise read sites, the upstream callers that reach them, tests to run, and per-site`analyze_env_impact`\n\n`required`\n\n. CLI:`openlore env-impact`\n\n.— the catch-up lens: changed symbols since a base ref, ranked into a fixed tier order (surprising-change > hub-change > chokepoint-change > ordinary) from existing classifiers, no weighted score. CLI:`briefing_since`\n\n`openlore briefing-since`\n\n.— a hazard-typed conflict graph over a task list, or over every in-flight branch/PR/agent-task, with a suggested landing order (opt-in`plan_parallel_work`\n\n/`map_in_flight_conflicts`\n\n`coordination`\n\npreset).— the deterministic per-language capability matrix, derived from the live extractors so it can't over-claim. →`get_language_support`\n\n[docs/language-support.md](/clay-good/OpenLore/blob/main/docs/language-support.md)\n\n**Preflight** *(no API key)* — A CI staleness gate: any PR that edits indexed files fails until the graph is refreshed, weighted so hubs surface first. Drop-in templates in [ examples/ci/](/clay-good/OpenLore/blob/main/examples/ci). →\n\n[docs/preflight.md](/clay-good/OpenLore/blob/main/docs/preflight.md)\n\n**Share the index** *(no API key)* — The graph is a deterministic function of the committed source, so a team analyzes **once** and everyone else imports. `openlore export bundle`\n\nserializes the index into a portable, integrity-stamped `.olbundle`\n\n; `openlore import`\n\nbootstraps a verified index in seconds — **validate-or-rebuild**, never serving a stale or tampered bundle. → [docs/shareable-bundle.md](/clay-good/OpenLore/blob/main/docs/shareable-bundle.md)\n\n**Telemetry** *(opt-in, no API key)* — Gated by `OPENLORE_TELEMETRY=1`\n\n(off by default). Append-only JSONL under `.openlore/telemetry/`\n\nfor empirically measuring Epistemic Lease behavior (obstinacy index, recovery efficiency, trajectory dynamics). Analyze with `openlore telemetry`\n\n.\n\n**Languages**: TypeScript · JavaScript · Python · Go · Rust · Ruby · Java · C++ · Swift · C# · Kotlin · PHP · C · Scala · Dart · Lua · Elixir · Bash — call graphs ride the same node/edge primitives for every language. Per-language extraction limits: [docs/languages.md](/clay-good/OpenLore/blob/main/docs/languages.md).\n\n**Infrastructure-as-Code**: Terraform/HCL · Kubernetes · Helm · CloudFormation · Ansible · Pulumi · AWS CDK · CDKTF · Dockerfile · Docker Compose · GitHub Actions · Azure Bicep — IaC resources and their references project onto the **same graph** as application code, so `orient`\n\n, `search_code`\n\n, and `analyze_impact`\n\nanswer \"what's the blast radius of changing this security group / ConfigMap / IAM role / base image / CI job?\" with zero new tooling. A compose service's `build:`\n\nresolves to its Dockerfile stage to its `FROM`\n\nbase image — so one `analyze_impact`\n\non a base image surfaces every stage and service that would rebuild. → [docs/iac.md](/clay-good/OpenLore/blob/main/docs/iac.md)\n\n**Cross-domain impact**: for embedded IaC (Pulumi/CDK/CDKTF), the code that provisions a resource is linked to it by a `references`\n\nedge, so `analyze_impact`\n\ntraverses the code↔infra boundary **end-to-end** — \"what infrastructure does this handler reach?\" and the reverse. A code-only navigator structurally cannot answer this. → [docs/cross-domain-impact.md](/clay-good/OpenLore/blob/main/docs/cross-domain-impact.md)\n\nThe hardest orientation questions cross repo boundaries: who calls `BillingService.refund`\n\n, where is event `X`\n\nconsumed, how does data flow from service A to B. Each repo keeps its own independently-built `.openlore`\n\nindex; a project-local registry references peers, and federated queries load only what they need — **no merged graph is ever materialized**.\n\n```\nopenlore federation add ../billing-service --name billing   # register a peer repo's index\nopenlore federation list                                     # ✓ indexed / ⚠ stale / ∅ unindexed\n```\n\n`analyze_impact`\n\n, `find_dead_code`\n\n, `select_tests`\n\n, and `find_path`\n\ntake an opt-in `federation`\n\nflag and answer across the fleet — always naming the repos consulted vs skipped, never guessing for an unindexed one. `analyze_impact`\n\non a route handler additionally surfaces its **cross-service consumers** — client call sites in *other* services that target the endpoint, matched by normalized route key. Enable with `openlore mcp --preset federation`\n\n. → [docs/federation.md](/clay-good/OpenLore/blob/main/docs/federation.md) · [docs/cross-service-topology.md](/clay-good/OpenLore/blob/main/docs/cross-service-topology.md)\n\nOpenLore's deterministic value usually flows through an agent calling an MCP tool — but **everyone opens pull requests**. `openlore review`\n\ndrops the same distinctive output into that workflow, no agent required:\n\n```\nopenlore review --base main           # one Markdown briefing for a base..head range\nopenlore review --format json         # machine-readable, for any CI / forge\n```\n\nIt composes the **structural delta** (`structural_diff`\n\n) and the **blast radius** (hubs touched, layers crossed, tests to run, spec/decision/memory drift) into one comment. The bundled **GitHub Action** posts it as **one sticky comment** — created once, updated in place, never spammy — advisory by default. Adoption is one workflow file: [ .github/workflows/openlore-review.yml.example](/clay-good/OpenLore/blob/main/.github/workflows/openlore-review.yml.example). →\n\n[docs/cli-reference.md](/clay-good/OpenLore/blob/main/docs/cli-reference.md#pr-review-openlore-review)\n\nOpenLore exports [SCIP](https://github.com/sourcegraph/scip). Plug it into Sourcegraph code nav, GitHub stack graphs, Glean importers, or any SCIP-aware tool:\n\n```\nopenlore analyze && openlore export scip   # writes ./index.scip\n```\n\nThe SQLite graph stays canonical; SCIP is a one-way export of the subset it can model. → [docs/scip-export.md](/clay-good/OpenLore/blob/main/docs/scip-export.md)\n\n**OpenLore is the inaugural engine and reference plugin** for the OpenSpec marketplace: generate specs from existing code, then hand evolution back to core OpenSpec. OpenSpec discovers OpenLore by a declarative plugin manifest and invokes it as a **subprocess** — it never imports OpenLore's code. → [docs/OPENSPEC-INTEGRATION.md](/clay-good/OpenLore/blob/main/docs/OPENSPEC-INTEGRATION.md)\n\nOpenLore's architecture is governed by the same decision system it ships. These ADRs were recorded with `record_decision`\n\n, gated at commit, synced into `openspec/specs/`\n\n, and projected onto the graph — they are the live, load-bearing constraints behind the design, not aspirational docs.\n\n| Decision | Rationale | Where |\n|---|---|---|\nNorth star is a deterministic structural context substrate |\nLocal-first plumbing (like tree-sitter/SCIP/LSP) agents build on; every feature must make the coding-agent case more useful and stay grounded in static analysis, not LLM guessing |\n|\n\n**IaC resources project onto the existing graph primitives**`FunctionNode`\n\n/`CallEdge`\n\nso every MCP tool works on IaC with zero new tooling`src/core/analyzer/iac/project.ts`\n\n**Decisions project onto the graph the same way**`decision::`\n\nnodes + `affects`\n\nedges — governance becomes a deterministic graph join`src/core/decisions/project.ts`\n\n**EdgeStore uses SCHEMA_VERSION rebuild-on-bump, not migrations**`src/core/services/edge-store.ts`\n\n**BM25 keyword retrieval is the zero-network floor**`orient`\n\n/`search_code`\n\nwork with no API key or embedding server; dense embeddings are an optional upgrade**The default MCP surface is the**`substrate`\n\npreset (navigation core + governance reads)`recall`\n\n, `verify_claim`\n\n, `blast_radius`\n\n); the write face (`remember`\n\n, `record_decision`\n\n) stays opt-in. Cleared the DefaultSurfaceRevealsAllFaces benchmark (no regression across two models / both tiers), so `openlore install`\n\nwires it by default. Lean `navigation`\n\nand the full set are one opt-in awayThis is the live decision log the pre-commit gate enforces. See [docs/governance-dogfooding.md](/clay-good/OpenLore/blob/main/docs/governance-dogfooding.md).\n\nWe'd rather you know these up front than discover them mid-task. Last **validated against the code** on 2026-07-25; two entries were wrong and are corrected below. Each open item names the change proposal that closes it — see [the change set](/clay-good/OpenLore/blob/main/openspec/changes/KNOWN-LIMITATIONS-2026-07.md).\n\n**Static analysis only — but \"dynamic dispatch isn't captured\" is no longer true, and the real boundary is narrower.** Polymorphic dispatch**is** recovered by class-hierarchy analysis, and event channels, route→handler bindings, and callback registrations by a deterministic synthesis pass — every one provenance-labeled`synthesized`\n\n, never mixed with directly-resolved edges. What genuinely is**not** captured: reflective invocation with a non-literal target (`getattr(o, name)()`\n\n,`send(name)`\n\n), computed dispatch (`obj[expr]()`\n\n),`eval`\n\n-built code, DI/plugin registries with no statically-visible binding, and cross-language bridges. Today those constructs are silently absent from the graph. Two proposals close the single-language part from both sides —[disclose them as boundaries](/clay-good/OpenLore/blob/main/openspec/changes/disclose-dynamic-boundary-regions/proposal.md)so a conclusion says where it stopped seeing, and[resolve the structurally-resolvable half](/clay-good/OpenLore/blob/main/openspec/changes/resolve-literal-reflective-dispatch/proposal.md)into real edges.**Cross-language bridges, and CHA's own name+arity over-approximation, stay uncovered and undisclosed.****LLM spec quality varies — and the only automated check on whether the prose is** Generated specs reflect the model's understanding; review complex business logic before treating it as authoritative. Structure, requirement format, import/export coverage, and drift are already checked deterministically; what nothing deterministic covers is whether a requirement's claims about the code are true. Spec verification's accuracy and coverage scores come from an LLM judge (~85% of the reported score) — the main place the product puts a model in a guardrail path, alongside one opt-in LLM gap filter in*accurate*is another LLM.`drift`\n\n.[Proposed fix](/clay-good/OpenLore/blob/main/openspec/changes/ground-generated-specs-in-the-graph/proposal.md): every generated requirement cites the symbols it describes, and a deterministic checker over the call graph — no API key — reports`grounded`\n\n/`partially-grounded`\n\n/`ungrounded`\n\n/`uncited`\n\nper requirement. Grounding proves a requirement is*about code that exists*; it will never prove the prose is correct.**Keyword (BM25) is the first-class default; semantic is an opt-in upgrade.**`orient`\n\n/`search_code`\n\nwork immediately with no API key. Upgrade to hybrid dense+BM25 with`openlore embed --local`\n\n(a bundled, CPU-only, no-API-key on-device embedder, ~23 MB pinned model) or a remote`EMBED_BASE_URL`\n\n. Each surface states its active mode (`keyword`\n\n/`local-semantic`\n\n/`remote-semantic`\n\n). The default's real weakness is vocabulary: the tokenizer splits identifiers into sub-tokens but does no stemming or expansion, so a natural-language task query reaches`chargeCard`\n\n(→`charge`\n\n,`card`\n\n) and misses genuinely abbreviated code (`PmtSvc`\n\n→`pmt`\n\n,`svc`\n\n).[Proposed fix](/clay-good/OpenLore/blob/main/openspec/changes/widen-keyword-recall-with-repo-vocabulary/proposal.md): mine an abbreviation lexicon from your repo and expand queries deterministically — still no model, no download, no key.**Large monorepos** may take several minutes to`analyze`\n\n— AST/symbol extraction is the bottleneck, not graph storage. Parallel extraction and content-hash memoization of Pass 1 have shipped; what's still missing is any concept of a*package*, so one changed package re-walks the whole repo, and the fact cache is machine-local, so every CI job and every teammate starts cold.[Proposed fix](/clay-good/OpenLore/blob/main/openspec/changes/scale-analyze-to-workspace-shards/proposal.md): manifest-detected workspace shards with cross-shard frontier re-resolution, plus a portable content-addressed cache.**Incremental updates converge or flag, never silently diverge.** The watcher re-indexes the changed file's reverse-dependency closure on save; when that exceeds the per-save budget (default 40 files) the un-recomputed files are marked**explicitly stale**(freshness verdicts report non-authoritative) instead of left wrong. A stale region** self-heals**: later edits reconcile the parts they touch, and when OpenLore runs as an MCP server or an`openlore serve`\n\ndaemon a debounced background re-analyze clears the rest. Any`analyze`\n\nclears it immediately, but no manual`--force`\n\nis required. (Stale marks are only created by a running watcher, and persist across a watcher restart until the next analyze.) Open residue: the budget is spent in arrival order, so*which*files stay stale is arbitrary ([proposed fix](/clay-good/OpenLore/blob/main/openspec/changes/prioritize-incremental-closure-budget/proposal.md): spend it on hubs and chokepoints first, and report what the stale region actually contains).**The index is integrity-checked, never served half-built — and it repairs itself.** Every`analyze`\n\nwrites an attestation (schema version, counts, content digest); on load the store is reconciled into`healthy`\n\n/`degraded`\n\n/`mismatched`\n\n, and a non-healthy index is disclosed in the conclusion tools whose soundness depends on completeness — never silently answered over. When a read notices the index has drifted from the code, it kicks off an at-most-once background repair*and says so*, rather than answering from stale structure (`openlore doctor --fix`\n\ncovers the rest). A read never destroys the store: a schema mismatch reports \"not ready\" and a corrupt store is quarantined, never dropped.*(Re-validated 2026-07-25: no open gap.)*\n\n**Node.js 22.13+**(the first line where the built-in`node:sqlite`\n\nis available without runtime flags; launching under an older or incapable Node fails fast with a one-line message and exit code 78, never a stack trace).**No API key** for`analyze`\n\n,`drift`\n\n,`mcp`\n\n,`init`\n\n, and every governance/navigation tool.**API key** only for`generate`\n\n,`verify`\n\n, and`drift --use-llm`\n\n:…or use a CLI-based provider (\n\n```\nexport ANTHROPIC_API_KEY=sk-ant-...    # default provider\nexport OPENAI_API_KEY=sk-...           # OpenAI\nexport GEMINI_API_KEY=...              # Google Gemini\n```\n\n`claude-code`\n\n,`gemini-cli`\n\n,`mistral-vibe`\n\n,`cursor-agent`\n\n) — no key, just the CLI on your PATH.\n\n**Start here:** the [documentation index](/clay-good/OpenLore/blob/main/docs/README.md) maps what you want to do to the one canonical page that answers it.\n\n| Topic | Doc |\n|---|---|\n| Full documentation index (task → canonical page) |\n|\n\n[docs/mcp-tools.md](/clay-good/OpenLore/blob/main/docs/mcp-tools.md)`openlore install`\n\n— auto-configure agent surfaces[docs/install.md](/clay-good/OpenLore/blob/main/docs/install.md)[docs/agent-setup.md](/clay-good/OpenLore/blob/main/docs/agent-setup.md)[docs/AGENT-BENCHMARKS.md](/clay-good/OpenLore/blob/main/docs/AGENT-BENCHMARKS.md)[docs/providers.md](/clay-good/OpenLore/blob/main/docs/providers.md)[docs/language-support.md](/clay-good/OpenLore/blob/main/docs/language-support.md)`enforcement.policy`\n\n)[docs/configuration.md](/clay-good/OpenLore/blob/main/docs/configuration.md)[docs/cli-reference.md](/clay-good/OpenLore/blob/main/docs/cli-reference.md)[docs/architecture-invariants.md](/clay-good/OpenLore/blob/main/docs/architecture-invariants.md)[docs/test-impact-selection.md](/clay-good/OpenLore/blob/main/docs/test-impact-selection.md)[docs/reachability-dead-code.md](/clay-good/OpenLore/blob/main/docs/reachability-dead-code.md)[docs/coverage-gaps.md](/clay-good/OpenLore/blob/main/docs/coverage-gaps.md)[docs/structural-diff.md](/clay-good/OpenLore/blob/main/docs/structural-diff.md)[docs/change-coupling.md](/clay-good/OpenLore/blob/main/docs/change-coupling.md)[docs/drift-detection.md](/clay-good/OpenLore/blob/main/docs/drift-detection.md)[docs/cross-domain-impact.md](/clay-good/OpenLore/blob/main/docs/cross-domain-impact.md)[docs/cross-service-topology.md](/clay-good/OpenLore/blob/main/docs/cross-service-topology.md)[docs/federation.md](/clay-good/OpenLore/blob/main/docs/federation.md)[docs/shareable-bundle.md](/clay-good/OpenLore/blob/main/docs/shareable-bundle.md)[docs/scip-export.md](/clay-good/OpenLore/blob/main/docs/scip-export.md)[docs/preflight.md](/clay-good/OpenLore/blob/main/docs/preflight.md)[docs/ci-cd.md](/clay-good/OpenLore/blob/main/docs/ci-cd.md)[docs/provenance.md](/clay-good/OpenLore/blob/main/docs/provenance.md)[docs/cli-reference.md](/clay-good/OpenLore/blob/main/docs/cli-reference.md)[docs/viewer.md](/clay-good/OpenLore/blob/main/docs/viewer.md)[docs/configuration.md](/clay-good/OpenLore/blob/main/docs/configuration.md)·[docs/api.md](/clay-good/OpenLore/blob/main/docs/api.md)·[docs/pipeline.md](/clay-good/OpenLore/blob/main/docs/pipeline.md)[docs/ARCHITECTURE.md](/clay-good/OpenLore/blob/main/docs/ARCHITECTURE.md)·[docs/ALGORITHMS.md](/clay-good/OpenLore/blob/main/docs/ALGORITHMS.md)[docs/agentic-workflows.md](/clay-good/OpenLore/blob/main/docs/agentic-workflows.md)[docs/TROUBLESHOOTING.md](/clay-good/OpenLore/blob/main/docs/TROUBLESHOOTING.md)·[docs/PHILOSOPHY.md](/clay-good/OpenLore/blob/main/docs/PHILOSOPHY.md)\n\n```\nnpm install\nnpm run build\nnpm run test:run  # 5500+ unit tests, one-shot (npm test is watch mode)\nnpm run typecheck\n```\n\nNew contributor? See ** CONTRIBUTING.md** for setup, the agent-context / MCP wiring, and the commit gate. Please also read our\n\n[Code of Conduct](/clay-good/OpenLore/blob/main/CODE_OF_CONDUCT.md); to report a vulnerability, see\n\n[SECURITY.md](/clay-good/OpenLore/blob/main/SECURITY.md).\n\nIf OpenLore saves your agents from re-reading the same files — or catches one risky change before it lands — **star the repo**. It's the signal that tells us to keep building, and it helps other engineers find it.\n\n[\n](https://github.com/clay-good/OpenLore/stargazers)\n\n[Star history chart](https://www.star-history.com/clay-good/openlore) · [stargazers](https://github.com/clay-good/OpenLore/stargazers)\n\n- ⭐\n**Star** to follow along:[github.com/clay-good/OpenLore](https://github.com/clay-good/OpenLore) - 🐛\n**Found a bug or have an idea?** Open an[issue](https://github.com/clay-good/OpenLore/issues). - 🤝\n**Want to contribute?** Start with[CONTRIBUTING.md](/clay-good/OpenLore/blob/main/CONTRIBUTING.md). - 📦 Install in one line:\n`npm install -g openlore && openlore install`", "url": "https://wpnews.pro/news/openlore-deterministic-local-first-memory-and-guardrails-for-ai-coding-agents", "canonical_source": "https://github.com/clay-good/OpenLore", "published_at": "2026-07-29 23:06:53+00:00", "updated_at": "2026-07-29 23:22:11.915436+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["OpenLore", "Claude Code", "Cursor", "Cline", "Continue"], "alternates": {"html": "https://wpnews.pro/news/openlore-deterministic-local-first-memory-and-guardrails-for-ai-coding-agents", "markdown": "https://wpnews.pro/news/openlore-deterministic-local-first-memory-and-guardrails-for-ai-coding-agents.md", "text": "https://wpnews.pro/news/openlore-deterministic-local-first-memory-and-guardrails-for-ai-coding-agents.txt", "jsonld": "https://wpnews.pro/news/openlore-deterministic-local-first-memory-and-guardrails-for-ai-coding-agents.jsonld"}}