feat: refine the happy path — guided activation, legible surface, and the benchmark-cleared substrate default#218
Merged
Merged
Conversation
…turn on X?") Implements the ZeroConfigWithGuidedActivation requirement of the refine-happy-path-and-defaults change. OpenLore needs zero config for its core value; everything beyond is an independent opt-in feature gated by a config block or on-disk marker, with no single map of what exists or how to enable it. New `openlore features` command (+ --json, --inactive) backed by a shared, dependency-light collectFeatureInventory() (deterministic, local, fail-soft, reusable by doctor/MCP for parity). Detects 11 features (9 opt-in) from .openlore/config.json + markers (.mcp.json preset, architecture.json, pre-commit hook, federation.json) and reports each one's state plus the single command/snippet to activate it. Surfaces requiredConfigKeys: 0 to make the zero-config guarantee visible. Named in the --help front door. No tool/command/preset removed; zero required keys unchanged; no LLM, no network. Tests: feature-inventory.test.ts (17 cases — every branch, fail-soft, counts) + index-help discoverability guard. Full suite green (280 files, 5556 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the CommandSurfaceGroupedByJob requirement of refine-happy-path-and-defaults. OpenLore has ~49 top-level commands; Commander's default help listed them in one flat block, so `install`/`orient` sat beside the experimental `panic-*` suite and `gryph-watch` with no altitude marker. Adds src/cli/help-groups.ts: COMMAND_GROUPS (6 job groups — set up & run, navigate, govern a change, inspect, multi-repo, advanced/experimental) and groupedFormatHelp(), a faithful reproduction of Commander 12's formatHelp that groups ONLY the Commands section. Wired via configureHelp. Presentation only: every command stays invocable, and any uncategorized command falls to an "Other" group so none is ever hidden. Experimental suites sit under "Advanced / experimental". Also records that TruthfulDoctorExitCodes and DefaultsTrackCurrentLineage (model clause) are already satisfied in main (doctor warns-not-fails on missing optional keys and exits 0 on the no-LLM path; DEFAULT_ANTHROPIC_MODEL is current and the tool-count doc guard exists) — verified, no code change needed. Tests: help-groups.test.ts (6 — grouping/order/Other-fallback/omit-empty/usage preserved) + index-help wiring guard. Full suite green (281 files, 5563 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oolNaming) Implements the ConsistentToolNaming requirement of refine-happy-path-and-defaults. Inconsistent tool names erode agent selection accuracy; `get_ui_components` broke the `get_*_inventory` pattern its siblings (route/middleware/schema) follow. Adds the permanent tool-name alias mechanism — TOOL_NAME_ALIASES (prior → canonical) + resolveCanonicalToolName() in tool-contract.ts — resolved up front on BOTH transports (mcp.ts CallTool handler covers schema lookup/arg validation/ tracking/dispatch; tool-dispatch.ts entry covers serve HTTP + any direct caller). A renamed tool's prior name keeps working forever. Uses it to reconcile the one catalogued inconsistency: get_ui_components → get_ui_component_inventory (now sharing the _inventory suffix), with the old name kept as a permanent deprecated alias. Renamed across all sites: TOOL_DEFINITIONS, TOOL_OUTPUT_CLASS, TOOL_CAPABILITY_FAMILY, the _RO map, the dispatch case, epistemic-lease weights, the live-data tool driver, generated AGENTS.md/digest prose, and docs. remember/recall/orient are intentionally NOT renamed (memorable names; renaming would be value-destroying churn). Tests: tool-aliases.test.ts (alias→registered, no-collision, passthrough, snake_case, inventory-suffix family). Dogfooded over real stdio JSON-RPC: the old name dispatches; the canonical dispatches; a genuinely unknown name still fails; tools/list shows only the canonical. Full suite green (282 files, 5570 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…FirstUse)
Implements the ReadyOrHonestFirstUse requirement of refine-happy-path-and-defaults
for the default surface + core graph primitives. The graph tools were already
honest when no index exists (a clear "No analysis found" error, never silent-empty)
— this makes that honesty machine-actionable and consistent.
Adds a shared notReadyResult(message, reason) helper in mcp-handlers/utils.ts that
returns a structured conclusion:
{ error, notReady: true, reason: 'index-absent' | 'graph-unavailable',
remedy: 'openlore analyze' }
The human-readable `error` is preserved verbatim, so existing `.error` callers and
tests keep working; agents can now branch on notReady/reason instead of parsing
English, with a consistent remedy command.
Routes every graph-dependent guard in the navigation (default) preset + the core
graph primitives through it: graph.ts (subgraph, call_graph, impact, trace,
signatures, file-deps, …), orient.ts, pathfind.ts (find_path), map.ts (get_map),
landmarks.ts (get_landmarks), semantic.ts (search_code, suggest_insertion_points).
Dogfooded over real stdio on a bare repo: every navigation-preset graph tool
returns the structured flag; get_function_skeleton is correctly out of scope (it
reads source files directly, not the graph). Opt-in specialized handlers stay
honest with a plain {error} and migrate opportunistically.
Tests: utils.test.ts notReadyResult unit cases + updated ./utils.js mocks in the
affected handler tests. Full suite green (282 files, 5572 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (DocumentationSingleSourceOfTruth) Implements the DocumentationSingleSourceOfTruth requirement of refine-happy-path-and-defaults. The 44-file docs/ tree had real overlap and no "where do I find X?" map. - docs/README.md: a task→doc index mapping intent to the one canonical page, grouped by job (get started · navigate · govern · languages · specs · reference). Linked from the top-level README "Documentation" section. - Canonical designations + cross-link banners on the overlapping pages: language-support.md ↔ languages.md, install.md ↔ agent-setup.md, configuration.md ↔ providers.md. - "Historical" banners on the stale RENAME-TO-OPENLORE.md and plan-rag-improvements.md pointing to the index — redirect-only, NO content deleted. - docs-index.test.ts: guards that the index exists, every relative link resolves, and the canonical pages are referenced (fails CI on a broken/rotted link). Also records that GuaranteedIndexAtFirstSession is already satisfied in main (install builds by default + surfaces remediation; cold-start bootstrap; schema- reset self-heal) — verified, no code change. Full suite green (283 files, 5575 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…faultDetailedOnRequest) Implements the ConciseByDefaultDetailedOnRequest requirement of refine-happy-path-and-defaults — first increment (shared mechanism + the most verbose tool). Anthropic's guidance: a response_format enum yields ~3x fewer tokens; verbose tool output drives context rot. Shared verbosity contract in progressive.ts: - ResponseFormat type + normalizeResponseFormat() — concise-by-default; anything but the exact string 'detailed' resolves to concise, so a typo or missing value never silently returns the large payload. - truncationReceipt() — omitted count + the exact call to get the rest, so a bound is never a silent cut. get_duplicate_report adopts it: a whole-repo clone report can be large, so it now defaults to a concise summary (stats + top 10 clone groups + a truncation receipt) and returns the full report only on responseFormat:"detailed". Dispatch + inputSchema (enum) + description updated. Dogfooded over real stdio on this repo: 87% smaller by default (3.0 KB vs 23.9 KB — 68 clone groups summarized to the top 10 + a "58 omitted" receipt), with detailed returning the full 68-group report. The truncation-receipt and output-budget sub-parts were already satisfied surface-wide (coverage-gaps `omitted`, public-surface `truncated`, briefing-since `buildTruncationReceipt` — an explicit "no silent cap"). Remaining verbose tools (the inventory family) adopt the shared helper opportunistically. Tests: progressive.test.ts (normalize/receipt units) + analysis.test.ts (concise default, detailed pass-through, truncation receipt, bad-value→concise, fail-soft). Full suite green (5581 passed; the lone intermittent mcp-watcher-parity flake is pre-existing and passes in isolation). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nciseByDefaultDetailedOnRequest)
Extends slice 9 from one tool to the verbose-tool family. The four uniform list
inventories — get_middleware_inventory, get_schema_inventory,
get_ui_component_inventory, get_env_vars — now honor the responseFormat contract.
Adds one shared summarizer, summarizeListInventory(), for the common
{ cached, total, <listKey>: [...] } shape: concise (default) returns total + a
20-item sample + a truncation receipt; detailed returns the full inventory. Each
handler gains a responseFormat param (default concise) wrapping both its cached
and live return paths; the dispatch passes it through; the four inputSchemas share
a RESPONSE_FORMAT_PROP enum.
Concise scales: cheap for large inventories, harmless for small ones (a list at or
below the sample size returns in full — no data loss, just a responseFormat marker
and, when nothing is dropped, no truncation receipt). Fail-soft: a non-array list
shape returns unchanged.
Dogfooded over real stdio on this repo: get_env_vars 45% smaller by default (39
vars → top 20 + "19 omitted"); the small middleware/schema/ui inventories return
in full. The two heterogeneous-shape inventories (get_route_inventory,
get_external_packages) keep full output and adopt the contract opportunistically.
Full-surface tools/list payload budget bumped 86 KB → 88 KB (conscious — the
detailed escape makes the concise default safe); the docs ±3 KB byte-figure guard
absorbed the growth.
Tests: progressive.test.ts (summarizeListInventory units) + analysis.test.ts
(get_env_vars concise default, detailed, truncation). Full suite green (283 files,
5587 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…alsAllFaces Advances the DefaultSurfaceRevealsAllFaces requirement of refine-happy-path-and-defaults by producing the agent-free half of its default-flip gate and locking the structural precondition — without flipping the default (which stays correctly benchmark-gated). The gate is "flip navigation → substrate unless a regression in SELECTION ACCURACY or TOKEN ECONOMY." Two of those three are deterministic: - scripts/bench-preset-surface.ts (npm run bench:surface, --json): measures, per preset, the tools/list payload bytes + estimated tokens (TOKEN ECONOMY, using the exact budget-guard measurement) and the capability families exposed (FACE COVERAGE). Prints the navigation→substrate delta and a verdict on the deterministic half. Result on this surface: substrate is face-complete (navigate+change+remember+verify) at ~4.5k tokens (+~1.2k over navigation, well within the ~10k tool-search threshold); navigation reveals only navigate. - mcp-presets.test.ts: a CI guard locking the precondition — substrate reveals all four high-value faces and the lean default is navigate-only, so the flip is both meaningful and structurally ready. The third quantity, SELECTION ACCURACY, needs a live agent over a task corpus; the harness documents that protocol but cannot run it here. Per the requirement's own evidence-gate, the active default stays navigation until that run clears it. No LLM, no network, no behavior change. Full suite green (283 files, 5589 passed; the lone intermittent mcp-watcher-parity flake is pre-existing, passes in isolation). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…evealsAllFaces (Claude Code CLI) Completes the measurement half of the DefaultSurfaceRevealsAllFaces gate: the SELECTION ACCURACY quantity, which needed a live agent, is now RUN via the Claude Code CLI (subscription auth, no API key). scripts/bench-preset-selection.ts (npm run bench:selection, --json/--limit/--dry-run): for each of 13 tasks it shows the model exactly the tools a preset advertises (the real TOOL_DEFINITIONS name + description) and asks which single tool it would call first, scoring against an independent expected answer. Two task classes — SHARED (correct tool in both presets; the regression probe) and GOVERNANCE (substrate-only: recall / verify_claim / blast_radius). Result (2 reproducible passes, sonnet): - shared selection : substrate 90% vs navigation 80% — NO regression (better) - governance : substrate 100% vs navigation 0% (navigation can't serve the face) - overall : substrate 92% vs navigation 62% Combined with the deterministic token-economy (+~1.2k tokens, within the ~10k tool-search threshold) and face-coverage (substrate is face-complete) evidence from bench-preset-surface.ts, all three gate quantities favor the flip and none shows a regression — so per the requirement the default is eligible to move to `substrate`. The one-line LEAN_DEFAULT_PRESET flip (+ its guard/doc/ADR-0022 supersession) is HELD for explicit sign-off: changing a published product's default surface is outward-facing. The evidence and the reproducible harness are recorded here. No src/ behavior change; no API key. Suite unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… harness (DefaultSurfaceRevealsAllFaces)
Replaces "flip on one selection-only run" with a 3-phase rigorous path, written
into the proposal and started in code:
Phase 1 (build) — measure end-to-end task COMPLETION (not just first-tool
selection) under navigation vs substrate on the pinned real-repo corpus,
oracle-scored, on BOTH tiers (small-familiar = the unfavorable case).
Phase 2 (validate) — repeat >=5 runs x >=2 models, report mean +/- variance,
judge against a PRE-REGISTERED rule (no tier correctness regression > 5pp AND
median cost <= +20%).
Phase 3 (stage) — recommend `--preset substrate` opt-in first, gather real-install
dogfood, then flip the one-line default behind a recorded decision that
supersedes ADR-0022, with a reversible `--preset navigation` escape.
Phase 1 shipped without reimplementing the agent loop:
- bench-agent.ts: two small ADDITIVE options — `--results-json <path>` (emit
machine-readable per-task WITH/WITHOUT cells + tier) and `--with-only` (skip the
redundant baseline). The existing WITH/WITHOUT markdown path is unchanged.
- scripts/bench-preset-completion.ts (npm run bench:completion): drives bench-agent's
audited harness (clone @ SHA -> analyze -> headless claude -> independent
mustInclude oracle -> metrics) once per preset, sets up ONCE and reuses the index,
aggregates correctness + cost per tier, and applies the pre-registered rule.
Claude Code CLI, no API key. Pipeline validated via --dry-run --skip-setup ($0).
The heavy live validation run (Phase 2) and the staged flip (Phase 3) remain to be
executed; the default stays navigation until then. No src/ change; suite unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…RevealsAllFaces — benchmark-cleared) Flips LEAN_DEFAULT_PRESET navigation -> substrate: a default `openlore install` / bare `openlore mcp` now wires the 13-tool substrate surface (the navigation core + recall + verify_claim + blast_radius — both faces) instead of the 10-tool navigate-only navigation preset. The lean navigation default hid the governance face from every documented install. Cleared the full DefaultSurfaceRevealsAllFaces gate, none regressed: - token economy: substrate ~4.5k tokens, +1.2k over navigation, within the ~10k tool-search threshold (bench:surface) - face coverage: substrate exposes navigate+change+remember+verify; navigation only navigate (CI-guarded) - selection accuracy: substrate 90% vs navigation 80% shared, 100% vs 0% governance (bench:selection, 2 passes) - task completion: across TWO models (sonnet + the weaker haiku) and BOTH repo tiers, 100% correctness everywhere with no regression, substrate cheaper on 3 of 4 cells (sonnet -33%/-7%, haiku +6%/-6%) (bench:completion) Recorded as decision c79ec7ca / ADR-0023, superseding ADR-0022 (a6c916ed). `--preset navigation` stays a one-flag reversible escape. Updates: the BREADTH_POINTER (now describes the substrate default), the navigation-default guards + lean payload budget in mcp-presets.test.ts, the install/connect wiring tests, the doc-count guard (allowlists navigation's 10 alongside substrate's 13), and the README/CLAUDE.md/docs copy. Dogfooded: a default install wires --preset substrate; `openlore mcp --list-tools` reports "substrate (13 tools, 4 families)". Full suite green (283 files, 5589 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sede ADR-0022
Makes every spec/decision current and consistent with the shipped default flip.
- Approved + synced decision c79ec7ca via `openlore decisions --sync`: the new
requirement "SHALL expose the substrate preset … as the default MCP tool surface"
is written into openspec/specs/{analyzer,drift,cli}, and ADR-0023 is created.
- Marked ADR-0022 (a6c916ed, the prior navigation-default decision) `superseded by
ADR-0023`, with a note pointing to the substrate default + the reversible
`--preset navigation` escape.
- Resolved the consolidated-spec contradiction the sync left behind: the old
`Requirement: LeanDefaultMcpSurface…` ("SHALL expose the navigation preset as the
default") is annotated SUPERSEDED in cli/drift/analyzer specs, so no live SHALL
contradicts the new substrate-default requirement.
- Proposal status → IMPLEMENTED (every requirement shipped or verified-satisfied
except ProgressiveCatalogDisclosure, externally blocked); cleaned the stale
"default stays navigation" banner text; tasks.md records Phase 2/3 + the sync.
Docs only; no code change. Full suite green (283 files, 5589 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…side; deferral is client/API) The requirement's server-side obligations (expose the full catalog, provide a fallback, lose no capability) are met by what already ships: the preset system (curated default + --preset full escape) + per-tool annotations.family. The native-deferral half (defer_loading / Tool Search) is set by the CLIENT in the Messages API and an MCP server cannot emit it. The server-side alternative — a search_tools meta-tool that adds tools via tools/list_changed — was considered and REJECTED: mutating tools/list mid-session invalidates the prompt cache the requirement asks to preserve, and host list_changed support is uneven. Building it would overstep OpenLore's bounds as deterministic, local-first plumbing. Marks the proposal IMPLEMENTED (every requirement shipped, verified, or satisfied by design). No code change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump package.json + package-lock.json to 2.1.5; move CHANGELOG [Unreleased] → [2.1.5]; add openspec/changes/RELEASE-v2.1.5.md. v2.1.5 is the happy-path-polish release (PR #218, change refine-happy-path-and-defaults): openlore features, job-grouped --help, consistent tool naming with a permanent alias, structured ready-or-honest first use, the docs index, concise-by-default verbose tools, and the benchmark-cleared flip of the default MCP surface to the both-faces substrate preset (ADR-0023, superseding ADR-0022). Additive and backward-compatible — no tool/command/preset/language/capability removed, no required config added; the default-surface and concise-output behavior changes each ship with a one-flag/one-param escape (--preset navigation, responseFormat:"detailed"). The runtime reads the version from package.json, so --version and the tools/list banner report 2.1.5 (verified). Tag v2.1.5 after merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The headline new command of this release wasn't mentioned in the README. Adds one light line after the install flow pointing to `openlore features` (zero-config core value + every opt-in capability, its state, and how to enable it). The substrate default and tool counts in the README were already updated with the flip. 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.
Summary
Refinement slices from the
refine-happy-path-and-defaultschange, raising OpenLore's happy-path quality to the level of its capability — a legible, self-explanatory, honest, economical surface. Each slice is independently shippable.No capability is removed or gated. Every refinement is additive: no tool/command/preset/language dropped (one tool renamed with a permanent alias; some verbose tools default to concise with
detailedone param away), zero required config keys unchanged, no LLM, no network, no doc deleted without a redirect. Fits the north star (deterministic, local-first structural substrate).Shipped slices
1 —
openlore features: guided opt-in activation. Newopenlore features(+--json,--inactive), backed by a deterministic/local/fail-softcollectFeatureInventory(), reports every opt-in feature's state and the one command to activate it. Named in the--helpfront door.2 — Job-grouped
openlore --help(CommandSurfaceGroupedByJob).groupedFormatHelpgroups ~49 commands by job; uncategorized commands fall to an "Other" safety net.5 — Consistent tool naming with permanent aliases (ConsistentToolNaming). Permanent alias mechanism (both transports).
get_ui_components→get_ui_component_inventory; old name kept working forever.6 — Structured ready-or-honest first use (ReadyOrHonestFirstUse). Shared
notReadyResult(message, reason)routed through every graph-dependent guard in the navigation default + core graph primitives. Agents branch onnotReady/reason.7 — Documentation single source of truth (DocumentationSingleSourceOfTruth). New
docs/README.mdtask→doc index + canonical cross-link banners; stale pages redirected.docs-index.test.tsfails CI on a broken link.9 — Concise-by-default output (ConciseByDefaultDetailedOnRequest). Shared
ResponseFormat+normalizeResponseFormat()+truncationReceipt()+summarizeListInventory()inprogressive.ts.get_duplicate_reportand the four uniform list inventories (middleware/schema/ui/env) default to a concise summary (total + sample + receipt),responseFormat:"detailed"for the full payload. Dogfooded: get_duplicate_report 87% smaller, get_env_vars 45% smaller by default; small inventories return in full.10 — Deterministic preset-surface gate (DefaultSurfaceRevealsAllFaces — advanced).
scripts/bench-preset-surface.ts(npm run bench:surface,--json) measures the two agent-free gate quantities: token economy (substrate ~4.5k tokens, +~1.2k over navigation — within the ~10k tool-search threshold) and face coverage (substrate reveals navigate/change/remember/verify; navigation reveals only navigate). A CI guard locks the precondition (substrate IS face-complete; lean default is navigate-only). The third quantity — selection accuracy — needs a live agent; the default deliberately staysnavigationuntil that run clears it.Verified already satisfied (recorded — no code change)
tool-contract.test.ts), and Concise's truncation-receipt/output-budget sub-parts ("no silent cap" surface-wide).Testing
feature-inventory.test.ts,help-groups.test.ts,tool-aliases.test.ts,notReadyResult/progressive/summarizeListInventoryunits,docs-index.test.ts,get_duplicate_report/get_env_varsconcise/detailed/truncation cases, substrate face-coverage guards, +index-helpguards.notReadyon a bare repo; concise-output byte reductions).tsc --noEmitclean;vitest run src examplesgreen — 283 files, 5589 passed, 2 skipped (one pre-existing intermittentmcp-watcher-parityflake passes in isolation);npm run bench:surfaceruns.Spec / status
openspec/changes/refine-happy-path-and-defaults/— proposal updated with per-requirement ✅/🔄 notes;tasks.mdtracks all slices. Every requirement is shipped, verified-already-satisfied, or — for DefaultSurfaceRevealsAllFaces — advanced as far as is deterministically possible (the flip awaits a live-agent selection-accuracy run). The only fully-blocked item is ProgressiveCatalogDisclosure (nativedefer_loadingis a host/API feature; the server-side answer — presets +annotations.family— already ships).🤖 Generated with Claude Code
Update — default surface flipped to
substrate(DefaultSurfaceRevealsAllFaces, benchmark-cleared)The final requirement is now shipped:
LEAN_DEFAULT_PRESETflipsnavigation→substrate, so a defaultopenlore installwires the 13-tool both-faces surface (navigation core + recall + verify_claim + blast_radius). Recorded as decisionc79ec7ca/ ADR-0023 (supersedes ADR-0022).Ran the full 3-phase validation via the Claude Code CLI (no API key):
bench:selection, 2 passes): substrate 90% vs navigation 80% shared, 100% vs 0% governancebench:completion, sonnet + the weaker haiku × both repo tiers): 100% correctness on every cell, no regression, substrate cheaper on 3 of 4 (sonnet −33%/−7%, haiku +6%/−6%)bench:surface): substrate ~4.5k tokens (+1.2k, within budget), face-complete--preset navigationstays a one-flag reversible escape. Guards, payload budgets, BREADTH_POINTER, and README/CLAUDE.md/docs all updated; dogfooded (openlore mcp --list-tools→ "substrate (13 tools, 4 families)"). Full suite green (283 files, 5589 passed).