feat(mcp): capability-family taxonomy + substrate both-faces preset (one substrate, two faces)#217
Merged
Merged
Conversation
…one substrate, two faces) Implements the unify-navigation-and-governance-substrate change: turns the ~72-tool surface from a flat registry into one discoverable-by-family substrate. - TOOL_CAPABILITY_FAMILY (tool-contract.ts): every tool declares one of six closed families (navigate/change/remember/verify/coordinate/federate), parallel to TOOL_OUTPUT_CLASS. Emitted in each tool's MCP annotations.family so the full surface is groupable on the wire; groupToolsByFamily() helper for docs/help. - substrate preset: navigation core + recall + verify_claim + blast_radius (both faces, governance READS only). Active default stays navigation; substrate flips to default only on benchmark evidence (ADR-0022) — shipped as a selectable preset. - NoRedundantConclusions: ADJACENT_TOOL_GROUPS + sibling cross-references on the four adjacent groups (blast_radius/structural_diff/change_impact_certificate, plan_parallel_work/map_in_flight_conflicts, find_clones/get_duplicate_report, report_coverage_gaps/select_tests). - tool-contract.test.ts guards: family completeness/closed-set/no-stale + distinct-question cross-reference. Payload budgets bumped consciously (full 84_000->86_000, nav 13_300->13_700) for the family annotation key. - Docs: CLAUDE.md + docs/mcp-tools.md grouped-by-family section + substrate preset. Build clean; vitest run src examples green (279 files / 5525 tests); e2e-dogfooded over a live stdio tools/list (all 72 tools carry annotations.family across 6 families; substrate resolves to 13 both-faces tools). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng (PR #217 review pass) Adversarial review of the capability-family taxonomy surfaced gaps; this pass closes them. - mcp --list-tools: prints the active surface grouped by capability family and exits (renderToolSurfaceByFamily over groupToolsByFamily). This is the human-facing production consumer of the taxonomy — previously groupToolsByFamily was exported and tested but rendered nowhere; the spec's "discoverable by family" now has a CLI surface beyond the on-the-wire annotations.family and the static docs. Respects --preset/--all-tools; bogus preset exits 2 without starting the transport. - Family-assignment audit fix: moved check_spec_drift change -> navigate so it shares a family with its spec<->code-parity sibling audit_spec_coverage; `change` is now cleanly diff-scoped (detect_changes stays). navigate 52->53, change 7->6, total unchanged. - Stale/incomplete preset enumerations fixed: openlore install help ("all 62 tools", missing substrate/coordination) and openlore serve advisory help now list substrate. Verified install/serve accept --preset substrate (both validate dynamically against TOOL_PRESETS). - Docs: README + docs/install.md + docs/cli-reference.md document the substrate preset, the six capability families, and --list-tools. - Tests: +3 renderToolSurfaceByFamily cases (grouping/coverage, pluralization, all-six). Build/typecheck/lint clean; vitest run src examples green (279 files / 5528 tests); e2e-dogfooded: --list-tools across presets, install accept/reject, exit codes, stdio annotations.family on all 72 tools after the check_spec_drift move. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ty + docs) Second adversarial review pass (Pi-parity + annotation-consumer audits) found one real test gap and two minor doc/comment refinements; this closes them. - Test gap: the `--list-tools` option registration and the startMcpServer short-circuit were untested in CI — only the pure renderToolSurfaceByFamily was. Extracted renderActiveToolSurface() (selection ∘ family-grouping; throws on unknown preset, like selectActiveTools) so the composition is unit-testable, and the short-circuit is now thin glue over it. Added: multi-family grouping (minimal → navigate+change+remember), unknown preset throws (→ exit 2, never starts the server), and an mcpCommand --list-tools option registration guard (mirrors the existing --minimal/--preset guards). - Pi parity: confirmed genuinely moot (NAV_TOOLS is a native curated superset of `substrate`; family/preset/breadth are MCP-wire concepts the Pi host never emits or consumes). Refreshed the extension.ts header comment to the post-taxonomy vocabulary (doc-only; no behavior). - Docs: agent-setup.md (the deferred full-surface + Tool Search doc) now notes the six capability families + `--list-tools`, where family discoverability is most relevant. - Deliberately NOT changed: serve /health reports tool names as a flat array — advisory only, nothing consumes family there; adding it would be speculative (discoverability is delivered by the MCP wire annotations + --list-tools + docs). Build/typecheck/lint clean; vitest run src examples green (279 files / 5531 tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…es.ts (slice 1/5) First safe slice of modularize-call-graph-builder, behind the stable barrel. Moves the TYPES section (edge/node/class model, CallGraphResult/SerializedCallGraph, CALL_DISTANCE_COSTS, callDistance, layerOf, classifyLayerEdge) out of the 5,425-line call-graph.ts into a new dependency-light call-graph-types.ts. call-graph.ts re-exports every public name, so none of the 155 importers move; RawEdge and CALL_DISTANCE_FALLBACK stay internal (off the surface). call-graph.ts: 5,425 → 5,150 lines. Behavior-preserving, verified four ways: - Byte-level snapshot oracle (multi-language CallGraphBuilder.build() + the moved pure helpers, serialized canonically + SHA-256'd) hashes IDENTICALLY before/after: 131ba4c6… - Public export surface byte-for-byte identical (multi-line-aware diff of call-graph.ts exports). - tsc compiles all 155 importers clean; build/lint/typecheck green. - Full suite green: 279 files / 5531 tests. New `stable call-graph barrel` test locks the invariant (re-export identity === source binding + moved-helper behavior) so the remaining slices (nodes/extract/dispatch/grammar-loader) can't silently break the surface. No feature, no dependency, no LLM, no persisted artifact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ph-extract.ts (slice 3) Next safe slice of modularize-call-graph-builder. Moves the DOCSTRING / SIGNATURE EXTRACTION HELPERS section (extractDocstringBefore, extractDeclaration) out of call-graph.ts into call-graph-extract.ts — two pure string-scanning functions with zero dependency on the rest of the analyzer (no tree-sitter, no module state). They were file-internal helpers (never on call-graph.ts's public surface), so they are imported back, NOT re-exported — the public import surface is unchanged. Taken before slice 2 as the safest small slice. call-graph.ts: 5,150 → 4,951 lines. Behavior-preserving, verified four ways: - Snapshot oracle FIRST strengthened to serialize each node's docstring + signature (fixture now has a JSDoc'd TS fn, an exported async fn, and a Python docstring + annotated signature, so both moved functions are exercised across TS and Python), then captured before/after: IDENTICAL — SHA-256 58107ac0… - Public export surface byte-identical (multi-line-aware diff: 0 added, 0 removed). - tsc compiles all importers clean; build/lint/typecheck green. - Full suite green: 279 files / 5534 tests. No feature, no dependency, no LLM, no persisted artifact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rnal.ts (slice 3b) Next safe slice of modularize-call-graph-builder. Moves the EXTERNAL NODE HELPER section (classifyExternal, the EXTERNAL_* regex/set tables, getOrCreateExternalNode) out of call-graph.ts into call-graph-external.ts — pure external-call classification + leaf-node interning, depending only on the ExternalKind/FunctionNode types. All file-internal helpers (never on call-graph.ts's public surface): only getOrCreateExternalNode is imported back; classifyExternal + the tables stay private to the new module. Dropped the now-unused internal ExternalKind import (it is still re-exported on the barrel — surface unchanged). call-graph.ts: 4,951 → 4,887 lines. Behavior-preserving, verified four ways: - Snapshot oracle first extended to exercise external nodes across externalKind http/db/unknown, then captured before/after: IDENTICAL — SHA-256 3a118017… - Public export surface byte-identical (multi-line-aware diff: 0 added, 0 removed; ExternalKind still exported). - tsc compiles all importers clean; build/lint/typecheck green. - Full suite green: 279 files / 5534 tests. No feature, no dependency, no LLM, no persisted artifact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-graph-complexity.ts (slice 3c) Next safe slice of modularize-call-graph-builder. Moves the CYCLOMATIC COMPLEXITY section (computeCyclomaticComplexity + the CC_PATTERN_* regex tables) out of call-graph.ts into call-graph-complexity.ts — a pure, dependency-free McCabe estimator. computeCyclomaticComplexity was exported from call-graph.ts (though no external importer today), so it is imported back AND re-exported on the barrel to preserve the public surface exactly; the CC_PATTERN_* tables stay private to the new module. call-graph.ts: 4,887 → 4,879 lines. Behavior-preserving, verified four ways: - Snapshot oracle first extended to exercise cyclomaticComplexity > 1 (branchy TS + Python fixtures: risky cc=5, handler cc=3), then captured before/after: IDENTICAL — SHA-256 7b765f31… - Public export surface byte-identical (0 added, 0 removed; computeCyclomaticComplexity still exported). - tsc compiles all importers clean; build/lint/typecheck green. - Full suite green: 279 files / 5534 tests. No feature, no dependency, no LLM, no persisted artifact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (slice 3d) Next safe slice of modularize-call-graph-builder. Moves the CFG / DATA-FLOW OVERLAY HELPER section (buildCfgFor) out of call-graph.ts into call-graph-cfg.ts — a pure, fail-soft wrapper around buildFunctionCfg (./cfg.js) that body-resolves a declaration wrapper (const-arrow, decorated-Python def) before building the per-function CFG overlay. File-internal (referenced only in test COMMENTS, never imported), so imported back, NOT re-exported — surface unchanged. Dropped the now-unused buildFunctionCfg import from call-graph.ts (FunctionCfg/CfgNode still used). call-graph.ts: 4,879 → 4,846 lines. Behavior-preserving, verified four ways: - Snapshot oracle FIRST extended to serialize the full per-function `cfgs` overlay (blocks/edges/defUse/params) and to exercise buildCfgFor's body-digging via an arrow-assigned-to-const and a decorated Python def; then captured before/after: IDENTICAL — SHA-256 34c7bce5… - Public export surface byte-identical (0 added, 0 removed). - tsc compiles all importers clean; build/lint/typecheck green. - Full suite green: 279 files / 5534 tests. call-graph.ts is now 5,425 → 4,846 lines across five extracted sibling modules (types, extract, external, complexity, cfg). No feature, no dependency, no LLM. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-graph-builtins.ts (slice 3e) Next safe slice of modularize-call-graph-builder. Moves the callee-filtering sub-part of the grab-bag CONSTANTS section (the *_IGNORED per-language tables, IGNORED_BY_LANGUAGE / ALL_IGNORED_CALLEES, isIgnoredCallee, SELF_CALL_RECEIVERS, isSelfReceiver) out of call-graph.ts into call-graph-builtins.ts — pure data + string predicates, zero deps, zero state. The safest remaining slice (no module state, no re-export juggling). File-internal: only the two predicates (isIgnoredCallee, isSelfReceiver) are imported back; the *_IGNORED tables stay private. HUB_THRESHOLD and the style-tally helper stay put (different concerns). call-graph.ts: 4,846 → 4,741 lines. Behavior-preserving, verified four ways: - Snapshot oracle FIRST extended with ignored-builtin calls (print/len/JSON/Math) so isIgnoredCallee's drop-or-keep outcome is captured (print/len/JSON dropped as ignored; Math.max kept), then captured before/after: IDENTICAL — SHA-256 5fbe0719… - Public export surface byte-identical (0 added, 0 removed). - tsc compiles all importers clean; build/lint/typecheck green. - Full suite green: 279 files / 5534 tests. call-graph.ts is now 5,425 → 4,741 lines across six extracted sibling modules (types, extract, external, complexity, cfg, builtins). No feature, no dependency, no LLM. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… slices won't-do / deferred Records the scope decision for the call-graph modularization. The change is now SUBSTANTIALLY COMPLETE and intentionally bounded: the StableCallGraphBarrel invariant plus a proven, byte-identical-verified extraction methodology is satisfied (demonstrated six times). call-graph.ts: 5,425 → 4,741 lines across six sibling modules (types, extract, external, complexity, cfg, builtins). Remaining candidates deliberately not taken, on a value-vs-risk basis: - call-graph-nodes.ts — WON'T DO. 27-fan-in hub + mutable CFG side-table + IaC coupling; low-churn core machinery (little merge-contention value) and the highest-risk code in the file (a CFG side-table regression already occurred here). Net-negative ROI. - grammar-loader.ts — DEFERRED. Not the clean leaf the illustrative table implied: grammar loading is two subsystems split ~1,500 lines apart (native singletons/getters + a _grammarHandleCache handle system), coupled by the shared __resetGrammarCacheForTests reset a test imports. A two-location stateful job, not a small slice. - call-graph-dispatch.ts — DEFERRED. Largest/highest-churn section (dispatch synthesis); best future payoff but needs the oracle extended to synthesized edges first. The barrel pattern + snapshot-oracle recipe are documented, so any deferred extraction can be picked up opportunistically later — the trigger this proposal always wanted. Doc-only; no source change (code byte-identical to the last CI-green commit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…→ 4,745, wc -l) Final adversarial review pass found one inaccuracy: the proposal's headline undercounted the post-refactor call-graph.ts by 4 lines. True size is 4,745 (`wc -l`), so the net is −680, not −684 — the difference is the import-back lines re-added at the top of the barrel after each slice's assembly count. Corrected the headline and noted that the per-slice figures are the post-extraction assembly snapshots (which match the commit messages). Doc-only; no source change. Verified this pass: full suite green (279 files / 5534 tests); real `openlore analyze` e2e over the whole repo exit-0 with sane artifacts; public export surface byte-identical to origin/main (23 names, 0 added/removed); 6 new modules acyclic with no dead code; substrate feature live (substrate 13/4 families, full 72/6 families, all 72 tools carry annotations.family). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…EADME demo Stages the v2.1.4 release (cut the tag after merge): - Bump package.json + package-lock.json (root + packages['']) 2.1.3 → 2.1.4. The CLI and MCP server read the version from package.json, so both report 2.1.4 automatically. - CHANGELOG: convert [Unreleased] → [2.1.4] (2026-06-27) with a summary; add the PR #217 items (capability-family taxonomy + substrate preset + --list-tools in Added; call-graph modularization in Changed); fresh empty [Unreleased]; Full Changelog compare link. - New RELEASE-v2.1.4.md narrative covering everything since v2.1.3 (PRs #183–#217): the substrate unification, the call-graph split, the new conclusion tools, substrate-correctness work, onboarding/distribution, new IaC + cross-service coverage, and Pi parity. - README: replace the top demo with a REAL terminal recording (charmbracelet/vhs) of the actual `openlore` CLI on this repo — `openlore --version` → `orient --task` (relevant functions + insertion points, no API key) → `mcp --list-tools` grouped by capability family. Updated alt-text/caption to match; tests badge 5400+ → 5500+. Everything additive and backward-compatible; deterministic and local-first (no LLM in any serving path). Build/lint/typecheck clean; full suite green (279 files / 5,534 tests); doc-count + version guards pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… auto-recorded
Replaces the top README GIF with the complete new-user journey, recorded from a real
terminal (charmbracelet/vhs) on a brand-new sample repo — no edits, no faked output:
1. `ls src/` — a fresh project, OpenLore not set up
2. `openlore install` — detects the agent, wires MCP, builds the
index in ~180ms (no API key); ends on
"✓ Index built — orient() will return
results in your next session."
3. `openlore orient --task "add auth ..."` — one deterministic call returns the
relevant functions + insertion points
So a new user sees exactly what to do, start to finish. Updated the alt-text/caption to
describe the onboarding flow. The .tape recipe is in memory for one-command re-recording.
Doc-only (README + the GIF binary); doc-count guard passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rnance · proof
Replaces the top README GIF with three clean beats, auto-recorded from a real terminal
(charmbracelet/vhs) on a real ~114-file TypeScript project (proxilion) — no edits, no cuts:
1. memory `openlore orient --task "add rate limiting to the proxy"`
→ the relevant functions (rate-limiter, threat-intelligence, load-balancer)
and insertion points, in one deterministic call — no grep-and-read.
2. governance `openlore blast-radius`
→ flags a config-hub change before commit: "highest risk: critical;
1 hub affected. Hubs: getConfig (6 callers)".
3. proof `openlore prove --estimate`
→ "Cost -37%, Round-trips -50%, Verdict: ✅ OpenLore helps on this repo"
(deterministic estimate, no API key).
One clean screen per beat: saves memory, governs risk, and pays off — all on a real repo.
Updated the alt-text/caption to match. Doc-only (README + GIF); doc-count guard passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ect help A methodical pre-v2.1.4 e2e dogfood (clean-room install on real TS + Python repos, idempotency, merge-never-clobber, uninstall, --json purity, MCP stdio, docs audit). Onboarding/auto-setup verified solid: install wires CLAUDE.md block + .mcp.json + SessionStart/UserPromptSubmit hooks + settings.local permission + AGENTS.md, is idempotent, MERGES into pre-existing user files (never clobbers), and uninstall strips only its own block. MCP e2e (initialize → tools/list → tools/call orient) works. Two real issues found and fixed: 1. install printed a CONTRADICTORY agent-onboarding epilogue. `openlore install` runs analyze internally, which then printed "Agent config files: not generated / Re-run with --ai-configs" and "Run 'openlore generate'" — directly contradicting install's own "[did create] AGENTS.md" / "[did update] CLAUDE.md" / "Index built" output, on the primary first-run path. Root cause: analyze's human agent-setup tips have no business running when install owns the agent wiring. Fix: a hidden `analyze --embedded` flag (set by install's buildIndex) suppresses the agent-setup block, the "not generated" tip, and the "run generate" next-step. Standalone `openlore analyze` is unchanged. 2. `openlore connect --help` was stale: "all 62 tools" (the surface is 72) and omitted the `substrate` + `coordination` presets it actually accepts. Fixed to match the install command's wording (72 tools; full current preset list). Also: README "5400+ unit tests" → "5500+" (align with the badge; real count ~5,536). Regression tests (fail before, pass after): install-analyze asserts the contradictory epilogue is absent from install output; connect.test asserts the --preset help lists substrate+coordination+72 and not 62. Build/typecheck/lint clean; full suite 279 files / 5536 tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o (no narration)
Replaces the top README GIF with the complete OpenLore lifecycle, recorded straight from
a real terminal (charmbracelet/vhs) on a real Rust project (proxilion, 81 source files /
5,050 functions) — the actual CLI, no commentary, no edits, one clean screen per step:
openlore install one command sets it up, no API key →
"✓ Index built — orient() will return
results in your next session" (5s)
openlore orient --task "enforce the rate limit" finds the relevant code across the
proxy adapters (memory)
openlore blast-radius "highest risk: high; 1 hub affected;
xff (6 callers)" before commit (governance)
openlore prove --estimate "Cost -84%, Round-trips 17→3 (-82%),
Verdict: ✅ OpenLore helps" (proof)
So a newcomer clicking the repo sees, in ~25s, exactly what OpenLore does and how trivial
it is to set up. Optimized to ~757 KB (gifsicle). Updated alt-text/caption; doc-count
guard passes. Recorded against the local 2.1.4 build (the published 2.1.3 still has the
install-epilogue bug just fixed). Real proxilion repo untouched (recorded on a temp copy).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e jargon
The old strapline was a 38-word run-on of abstractions ("intelligence graph… unifies
code, infrastructure, and architectural decisions into a single, safety-gated governance
runtime") — accurate but unscannable, and it led with jargon instead of the pain/value.
New tagline is concrete and reads in 3 seconds: what it is (deterministic, local-first
memory + governance), the pain it kills (stop re-reading the same files every task), what
one call returns (the code a task touches + the risk of changing it), and the
differentiators (no API key, no LLM in the hot path). The demo GIF + benchmark section
carry the numbers. Doc-only; doc-count guard passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e hero
The gallery.png banner repeated the H1 ("OpenLore — memory & knowledge graph for AI
agents") and carried a "GraphRAG" pill that contradicts the core positioning (deterministic,
no LLM in the hot path). Now that the demo GIF concretely shows setup → memory → governance
→ proof on a real repo, the banner only delayed the asset that actually sells. Removed the
reference so the hero reads: name → value tagline → trust badges → the real-terminal demo.
(The PNG file is left in place, unreferenced, in case a value-accurate banner is wanted later.)
Doc-only; doc-count guard passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fix broken links/tape Pre-release deep-dive QA dogfood (two adversarial audit agents + manual onboarding/edge testing) before cutting v2.1.4. Findings fixed: - orient: `openlore orient "<task>"` (a bare positional — the most natural thing a user/agent types) silently fell through to the no-task session primer and exited 0, doing no orientation; a stray --limit also went unvalidated. Now the positional is honored (--task still wins); no task still prints the primer so the SessionStart hook (`orient --json`) is unaffected. Regression test added (+2). - README honesty: the hero GIF caption claimed "−84% cost, −82% round-trips" with no label, while the MEASURED agent benchmark 35 lines down is −26% — at odds with the README's own honesty contract. Caption + alt now mark −84%/−82% as a deterministic `prove --estimate` PROJECTION and link to the measured −26% scorecard. - README broken links: `docs/telemetry.md` (never existed; telemetry is documented in-README) removed; `adopt-agent-behavioral-governance/` repointed to its `archive/` path. All docs/ links now resolve. - docs/openlore-demo.tape was the stale Django version (narration style, ends at orient) and no longer matched the GIF. Rewritten to the current 4-beat Rust lifecycle (install → orient → blast-radius → prove) with an honest header (per-repo estimate; hero recorded on a large real Rust repo). Verified clean by dogfooding: .mcp.json merge preserves a pre-existing OTHER server, install idempotent + merge-never-clobber, MCP full surface (72/6 families) initialize + non-orient get_map over stdio, --json purity, exit codes, doctor/prove/update. typecheck + lint clean; full suite 279 files / 5538 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…, add hygiene + gate docs
v2.1.4 pre-release QA, contributor-experience pass (fresh-clone build verified ALL_GREEN:
npm ci → build → typecheck → CLI runs 2.1.4). Two broken commands a newcomer hits
immediately, plus the missing "feel at home" scaffolding:
Broken commands (P0 — a newcomer is misled):
- README "Development" said `npm test` — but that's vitest WATCH mode, which hangs a
newcomer who copy-pastes the block (and typecheck never runs). Now `npm run test:run`
(one-shot), matching CONTRIBUTING and CI.
- CONTRIBUTING referenced `npm run embed:up` (a Docker embed server) twice — that script
no longer exists; the zero-config on-device path superseded it. Now `openlore embed
--local` (no Docker, no API key).
Feel-at-home scaffolding (P1):
- CONTRIBUTING "Agent Context Setup" now explains the repo dogfoods its own MCP tools and
how to wire them (`openlore install --preset full` — the lean default omits
record_decision/search_specs that CLAUDE.md's decisions workflow needs; .mcp.json is
intentionally git-ignored and regenerated, merges-never-clobbers CLAUDE.md).
- New CONTRIBUTING "The commit gate (decisions)" section — a newcomer whose `git commit`
is blocked with `{"gated": true}` JSON now knows it's expected and how to resolve it
(this backs README's existing "decision gate ... documented" claim).
- Added CODE_OF_CONDUCT.md (Contributor Covenant 2.1) and SECURITY.md (private disclosure
to hi@claygood.com / GH advisory), both linked from the README Development section.
- Added .github/ISSUE_TEMPLATE/bug_report.md + PULL_REQUEST_TEMPLATE.md, and a .nvmrc (24).
Verified: all README + CONTRIBUTING root-relative links resolve; doc-count guard passes;
full suite 279 files / 5538 passed. No source changed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Implements the
unify-navigation-and-governance-substratechange. OpenLore's ~72 MCP tools had grown into a flat registry that read like two bolted-together products (navigation vs. governance/memory). This change names the shared spine — one structural substrate with two faces — and turns it into enforceable structure, with no new tool, no new dependency, no LLM, no persisted artifact.Grounded in the north star (
overview/spec.md, decisionc6d1ad07): deterministic, locally-computed structural context, conclusion over graph.What changed
mcp-qualityCapabilityFamilyTaxonomy). Every tool declares exactly one of six closed families —navigate·change·remember·verify·coordinate·federate— inTOOL_CAPABILITY_FAMILY(tool-contract.ts), parallel to the existingTOOL_OUTPUT_CLASS. The family is emitted in each tool's MCPannotations.family, so the full surface is discoverable by family on the wire.annotations.family(machine-readable, on the wire);openlore mcp --list-tools(human-facing CLI —renderToolSurfaceByFamily()/renderActiveToolSurface()overgroupToolsByFamily(), respects--preset/--all-tools, exits without starting the transport); and the docsCapability familiessection.substrateboth-faces preset. Thenavigationgraph-traversal core plus the three highest-value governance reads —recall,verify_claim,blast_radius(reads only; noremember/record_decisionwrite, no commit gate).navigation;substrateships as a selectable preset and flips to default only when an agent benchmark shows no regression. The flip is a one-lineLEAN_DEFAULT_PRESETchange, intentionally deferred to that benchmark run.mcp-qualityNoRedundantConclusions). The four genuinely-adjacent tool groups are not merged; each names its near-sibling in its own description, enforced byADJACENT_TOOL_GROUPS:blast_radius↔structural_diff↔change_impact_certificateplan_parallel_work↔map_in_flight_conflictsfind_clones↔get_duplicate_reportreport_coverage_gaps↔select_testsarchitectureUnifiedStructuralSubstratespec (one graph / one anchored-fact store / one freshness lease; read-face vs write-face).tool-contract.test.ts: family completeness + closed-set + no-stale, plus the distinct-question cross-reference check.84_000 → 86_000, nav13_300 → 13_700) for thefamilyannotation key. Docs size figure updated (~83 KB / ~21k tokens).README,CLAUDE.md,docs/mcp-tools.md,docs/install.md,docs/cli-reference.md,docs/agent-setup.mddocument thesubstratepreset, the six families, and--list-tools.Adversarial review passes
Two rounds of multi-agent adversarial audit (family-assignment correctness, doc completeness, Pi-extension parity, annotation-consumer safety) drove hardening:
Round 1
check_spec_driftchange→navigateso it shares a family with its spec↔code-parity siblingaudit_spec_coverage;changeis now cleanly diff-scoped. navigate 52→53, change 7→6.openlore mcp --list-tools(groupToolsByFamilywas exported + tested but rendered nowhere).openlore install(was "all 62 tools", missingsubstrate/coordination) andopenlore serve. Verifiedinstall/serveaccept--preset substrate(validate dynamically againstTOOL_PRESETS).Round 2
--list-toolsoption registration and thestartMcpServershort-circuit were untested in CI (only the pure renderer was). ExtractedrenderActiveToolSurface()(selection ∘ grouping; throws on unknown preset) and added tests for multi-family grouping (minimal → navigate+change+remember), unknown-preset throw (→ exit 2, never starts the server), and themcpCommand--list-toolsregistration guard.NAV_TOOLSis a native curated superset ofsubstrate, and family/preset/breadth are MCP-wire concepts the Pi host never emits or consumes. Refreshed theextension.tsheader comment to post-taxonomy vocabulary (doc-only).annotations(presets/size-figure/conformance tests, agent-eval scorecard, serve). Addingfamilyis safe everywhere; the two byte-size guards were already bumped for it.openlore serve /healthreports tool names as a flat array — advisory only, nothing consumesfamilythere; adding it would be speculative. Discoverability is delivered by the wire annotations +--list-tools+ docs.openspec/specs/{architecture,mcp-quality}are intentionally not hand-edited — this repo consolidates change-dir deltas at archive time (verified).Verification
npm run buildclean;tsc --noEmitclean;eslintclean on changed files.vitest run src examples→ 279 files / 5531 passed, 2 skipped.--list-toolsacross every preset (navigation 10/1-family, substrate 13/4, minimal 6/3, memory 3/2, federation 10/4, coordination 5/2, full 72/6 — correct grouping + pluralization);install --preset substrateaccepted,--preset bogusrejected (error lists substrate); bogus--list-toolsexits 2 without starting the server; live stdiotools/listcarriesannotations.familyon all 72 tools across 6 families.Notes / follow-ups
substrateawaits a benchmark run (tracked in the change'stasks.md).🤖 Generated with Claude Code