Skip to content

chore(deps): bump @vitest/mocker and vitest in /ui-v2 - #328

Open
dependabot[bot] wants to merge 539 commits into
mainfrom
dependabot/npm_and_yarn/ui-v2/multi-00f7b83f97
Open

dependabot[bot] wants to merge 539 commits into
mainfrom
dependabot/npm_and_yarn/ui-v2/multi-00f7b83f97

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Sep 9, 2026

Copy link
Copy Markdown
Contributor

Bumps @vitest/mocker to 5.0.0 and updates ancestor dependency vitest. These dependencies need to be updated together.

Updates @vitest/mocker from 3.2.7 to 5.0.0

Release notes

Sourced from @​vitest/mocker's releases.

v5.0.0

Vitest 5 is officially out! This release focuses on performance and brings a lot of new features while fixing long-standing bugs. See our blog post for the official announcement.

   🚨 Breaking Changes

... (truncated)

Commits

Updates vitest from 3.2.7 to 5.0.0

Release notes

Sourced from vitest's releases.

v5.0.0

Vitest 5 is officially out! This release focuses on performance and brings a lot of new features while fixing long-standing bugs. See our blog post for the official announcement.

   🚨 Breaking Changes

... (truncated)

Commits
  • f441c6f chore: release v5.0.0 (#11130)
  • d46a747 fix: treat test.describe as a suite during static collection (#11128)
  • 584cf30 fix: add a warning if inline project has duplicate plugins due to unexpected ...
  • f08ce4b fix: apply queued mocks from doMock() in queue order (fixes #10706) (#11127)
  • 897f51f chore: release v5.0.0-rc.4 (#11107)
  • 1339b06 chore(deps): update all non-major dependencies (#11104)
  • 51e9494 feat!: parse files statically in vitest list by default (#11088)
  • 2122ffd fix: propagate --maxWorkers to projects (#11102)
  • dc10f5f fix(browser): report the action error when a task times out (#11101)
  • d4fe198 feat: promote clearCache out of experimental (#11086)
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    You can disable automated security fix PRs for this repo from the Security Alerts page.

linhdmn and others added 30 commits July 16, 2026 13:13
Rebuild release binary and Docker image (freepeak/leankg:0.18.1 +
:latest). Includes the HNSW path fix, MCP-decoupled lookup, INT8 fast
path, and the LeanKG graph workflow fix landed since v0.18.0.
Keep MCP BACKGROUND embed off so HNSW is not dropped, ship offline
INT8 embed profile/script, and warm Xenova cache before quantized ONNX.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add docker-up.sh and install.sh docker target so users can stand up
LeanKG from Hub without Rust; document the flow in README/AGENTS.

Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(vector-engine): add Local/Cloud factory (FR-VE-ABS)

Static enum dispatch over LocalEngine and CloudEngine selected via
LEANKG_VECTOR_ENGINE, matching PRD §5.14 storage abstraction.

* feat(vector-engine): add Tier-1 topology store (FR-VE-T1)

Persist metadata and HNSW adjacency with Local RocksDB options
(mmap off, Zstd, pin L0, BinaryAndHash) per PRD 5.14.1.

* feat(vector-engine): add Tier-2 SQ8 RAM cache (FR-VE-T2)

Keep INT8/SQ8 vectors fully in RAM for SIMD ANN with no disk I/O
on the inner loop (PRD 5.14.1).

* feat(vector-engine): add Tier-3 flat payload file (FR-VE-T3)

Append-only FP32 + source payload binary store read once at
post-filter after ANN (PRD 5.14.1).

* feat(vector-engine): add runtime SIMD dispatch (FR-VE-RT-SIMD)

Detect AVX-512/AVX2/NEON at runtime with scalar fallback so distance
kernels never SIGILL on older CPUs.

* feat(vector-engine): auto-tune RocksDB block cache (FR-VE-RT-MEM)

Plan block cache from available RAM / 2GB Local survival cgroup so the
engine stays under the PRD memory envelope.

* feat(vector-engine): dynamic rayon pool sizing (FR-VE-RT-THREADS)

Local leaves 2 cores free for OS/IDE; Cloud uses the full machine.

* feat(vector-engine): HNSW selectNeighbors with M in 12-16 (FR-VE-HNSW)

Heuristic neighbor selection with raised ef_construction defaults to
protect recall at efSearch=50.

* feat(vector-engine): dual-write Append-fsync-commit-RAM (FR-VE-FS-DW)

Enforce safe write order: flat append, fsync, Tier-1 offsets, then
SQ8 RAM update per PRD 5.14.3.

* feat(vector-engine): crash recovery without dangling ptrs (FR-VE-FS-REC)

Truncate uncommitted flat tails on open and assert Tier-1 offsets never
point past the payload file.

* feat(vector-engine): shadow-page GC when frag > 30% (FR-VE-FS-GC)

Compact live records into a shadow flat file and swap offsets under a
micro-lock so readers stay unblocked.

* test(vector-engine): GC concurrency integrity (FR-VE-TEST-GC)

Assert shadow compaction with concurrent readers keeps vector counts
intact (FR-VE-TEST-GC).

* test(vector-engine): factory env selects Local/Cloud (FR-VE-TEST-FACTORY)

Assert EngineKind parse + VectorEngineFactory open for local and cloud.

* test(vector-engine): SIMD differential abs error (FR-VE-TEST-SIMD)

Require Neon/AVX2/AVX-512/scalar INT8 dots to agree within 1e-6.

* test(vector-engine): dual-write crash simulation (FR-VE-TEST-DW)

Simulate append-without-commit and assert recovery leaves no dangling
Tier-1 offsets.

* feat(vector-engine): ANN query P95 bench harness (FR-VE-BENCH-Q)

Measure SQ8 in-RAM query P95; full 1M/<50ms gate runs via cargo bench.

* feat(vector-engine): I/O reduction vs mmap metric (FR-VE-BENCH-IO)

Hot SQ8 path touches zero disk pages; report >=80% reduction vs mmap
full-scan baseline.

* feat(vector-engine): SQ8 vs FP32 recall harness (FR-VE-BENCH-RECALL)

Measure recall at efSearch=50 against FP32 brute-force (>90% target).

* feat(vector-engine): 2GB cgroup OOM survival plan (FR-VE-BENCH-OOM)

Auto-tune block cache under the Local 2GB survival cap so ANN warm
must not OOM-kill.

* feat(vector-engine): agent A/B floor hooks (FR-VE-BENCH-AB)

Record PRD token/tool/speed floors and optional LEANKG_VE_AB_JSON
ingestion for the release gate.

* feat(vector-engine): LocalEngine default cutover gate (FR-VE-GATE)

Smoke gate keeps ready_for_default=false until full-scale benches and
A/B floors pass; Cozo HNSW remains shipped default.

* docs: mark vector-engine FR-VE-* progress in task tracker

Core storage/runtime/tests DONE; benches/gate/US/REL PARTIAL until
full-scale 1M and live agent A/B prove the release floors.

* test(vector-engine): full A/B unit suite and cargo bench

Add deterministic ≥100-task LocalEngine vs grep/cat simulator with
PRD floor checks, wire gate smoke to the suite, and register
cargo bench --bench vector_engine_ab.

* chore(docker): cap Local MCP at 2GB survival limit

Align docker-compose.rocksdb and docker-up MCP with PRD LocalEngine
2GB envelope; keep offline embed on a higher mem_limit.

* docs(docker): document Local MCP 2GB mem_limit

Keep cold-embed guidance at ≥6g; MCP Local survival is 2g.

* docs(docker): add --memory=2g to MCP-only example
* docs(prd): sync v3.7 vector-engine status to origin/main

Reflect #79 merge on main (crate 0.19.0): FR-VE core/REL-044..047 DONE;
gate, full-scale benches, and live A/B remain PARTIAL.

* feat(vector-engine): prove FR-VE-BENCH-Q 1M ANN P95 under 50ms

Add Sq8Nsw layer-0 search over in-RAM SQ8 and gate cargo bench/default
corpus at 1M. Measured P95=0.065ms (Neon); mark FR-VE-BENCH-Q DONE.

* feat(vector-engine): close FR-VE-BENCH-IO/RECALL/OOM gates

Prove ≥80% modeled I/O cut vs mmap, SQ8 recall≥90% @ efSearch=50, and
1M corpus under 2GB (live RSS≈567MB). Mark REL-048 DONE.

* feat(vector-engine): meet US-VE-01/02 idle RSS and TTC floors

Add kpi helpers: warm SQ8 NSW RSS≈89MB (<150MB) and ANN+JSON
time-to-context P95≈0.094ms (<100ms). Mark REL-050 DONE.

* feat(vector-engine): close FR-VE-BENCH-AB with JSON artifact export

In-process ≥100-task suite meets PRD floors; cargo bench writes
target/vector_engine_ab_result.json for gate/live injection.

* feat(vector-engine): flip FR-VE-GATE ready_for_default on full evidence

evaluate_gate sets ready_for_default when LEANKG_VE_GATE_FULL=1 and all
Q/IO/RECALL/OOM/AB floors pass; preferred_ann_backend returns local_engine.

* test(vector-engine): cover P0 gate paths and record A/B results

Add e2e suite, expand unit/bench coverage, reorder RSS KPI before 1M ANN,
and sync PRD/tracker with measured gate evidence JSON.

* fix(vector-engine): avoid i8 overflow in synth SQ8 patterning

CI debug builds panicked on `% 254 as i8 - 127` when the modulo
exceeded 127; compute the centered value in i32 before casting.

* docs: polish README for clearer project landing page

* fix: idle GC trim once per quiet period

Stop re-trimming empty caches every 30s while idle; honor
LEANKG_GC_POLL_SECS in the watchdog sleep and call trim_heap
only after a real cache release.

* docs: capture semantic MCP verification as PRD v3.7.1 backlog

Record the live Docker MCP probe as GREEN evidence and add US-SEM/FR-SEM
enhancements (token honesty, ontology budgets, HTTP resilience, live smoke)
for a later sprint without displacing P1.

* fix(vector-engine): gate idle RSS on warm delta for CI

Absolute process RSS exceeds 150MB under Linux debug `cargo test --lib`
(~161MB observed). Measure baseline/delta and assert delta_ok in unit/e2e;
keep absolute check for lean bench processes.

* docs: polish README to product landing style

Restructure around CodeGraph-style get-started, agent badges, why/how,
and measured A/B results while keeping LeanKG-accurate install paths.

* docs(prd): sync tracker status to PR #80 tip

Record open PR, CI hardening (i8 overflow + RSS delta_ok), measured
KPI numbers, and awaiting-merge / next-P1 notes in PRD + task tracker.

* docs(prd): clarify FR-VE-GATE coexistence on PR #80

Align status banners with open PR #80 (awaiting merge) without pinning
a moving tip SHA; keep CI/RSS methodology notes.
…81)

* docs(prd): elevate day-2 embed resume to P0

Resume-if-data / cold-if-empty for standalone and all Docker
embed-on paths; FR-HNSW-E marked PARTIAL.

* feat(embed): resume day-2 with HNSW no-op and hash-aware stale

Skip HNSW drop/rebuild and model load when nothing is dirty;
stamp fresh per batch for kill/resume; mark stale only when
content_hash changes so full index does not force re-embed.

* docs(prd): mark embed-resume FRs done after local smoke

Sync tracker/PRD gate checklist to implementation evidence
(unit, e2e, CLI cold→resume→reindex→resume).

* feat(ops,sem): mega-graph 6g/3g/cpus6 + SEM path filter

Drop embed/assets and gate benchmark paths in FilterPolicy;
honor LEANKG_SKIP_FRESHNESS_CHECK; set compose cpus=6,
mem_reservation=3g, MCP mem_limit=6g. Sync PRD/tracker and
3-workspace vector/semantic verification report.

* fix(embed): clear Docker PID 1 stale embed.lock on spawn

Same-PID leftover locks no longer block LEANKG_EMBED_BACKGROUND after
container recreate; only an in-process active embed (or another live PID
with status=running) skips spawn.
* feat(mcp): document semantic_search dual-path (FR-SURF-01)

Align tools/list description with handler: HNSW+rerank when embeddings
exist, else ontology-first safe_discover. Track FR-SURF-01 as DONE.

* feat(mcp): add prefer-order schema hints (FR-SURF-02)

Document search/semantic tool prefer-order on concept_search,
semantic_search, search_code, kg_semantic_context, and kg_context.
Sync AGENTS.md mega-graph guidance; mark US-SURF-01 / FR-SURF-02 DONE.

* feat(mcp): remove superseded tools (FR-SURF-03)

Drop mcp_hello, mcp_impact, and get_doc_for_file from the registry and
handlers. Prefer get_impact_radius, find_related_docs, and status/self-test.
Add redundant_tools_matrix coverage; mark US-SURF-02 / FR-SURF-03 DONE.

* test: add coverage suites and surface analysis docs

Land CLI parse coverage, embedding_state unit tests, MCP redundancy
smoke suite, test-coverage status refresh, and competitive-analysis docs.

* fix(clippy): drop map_identity in threads pool test

Also sync instructions prefer-order with FR-SURF-02 search/semantic triples.
* docs: hybrid LSP Go/TS track (FR-LSP-A..D) + init --with-lsp

Document prefab lsp bootstrap, typed_resolve indexer wiring, and
update tracker session focus ahead of implementation.

* feat(lsp): hybrid typed resolve for Go/TS (FR-LSP-A..D)

Add in-process TypeRegistry + hybrid resolver, wire indexer to emit
resolution_method=typed when typed_resolve=go,ts, and bootstrap prefab
lsp servers via init --with-lsp / empty-yaml catalog fallback (REL-039).

* feat(mcp): soft-deprecate wake_up and search_by_environment

Mark FR-SURF-04/05 replacements in tool schemas, assert in redundancy
matrix, and record REL-053 surface counts in a release note.

* test(lsp): hybrid LSP e2e + mark FR-LSP/SURF tracker DONE

Cover Go/TS typed edges, init --with-lsp prefab yaml, and update the
task tracker statuses for the shipped P1/P2 items.

* test(cli): cover init --with-lsp flag parsing

Update Init match arms for the new with_lsp field and assert the
flag is accepted by the CLI parser.
* fix(mcp): do not block search on ontology sync / mega embed

Ontology sync at boot could hang for minutes on mega RocksDB and
prevent mcp-http from binding (empty health/search). Time out sync by
default, skip in-process background embed on mega-graphs unless
explicitly forced, and count elements without all_elements().

* docs(agents): note ontology sync timeout and mega embed skip

* docs(agents): restore LeanKG-first checklist wording
* feat(graph): US-GF-03 query_graph NL scoped subgraph

Add MCP query_graph and CLI graph-query / query --kind subgraph:
seed retrieval → BFS/path expand → token-budget trim with provenance
labels on every edge (REL-042 / FR-GF-05 / FR-GF-06).

* test(mcp): cover query_graph + fix agent_focus fixture

Strengthen US-GF-03 MCP assertions (seeds, provenance, budget, empty
question), create persona fixture so agent_focus/diary pass, register
schema checks, and add scripts/mcp-smoke-tools.py with honest skip labels.
Add MCP embed_control (on/off/status) with idle-gated partial Incremental
resume, cooperative cancel, and zero-dirty fast path. Classify all MCP
tools in redundant_tools_matrix and document skills/rules removal impact.
* docs: P0 mega HNSW semantic_search OOM (US-SEM-06 / FR-SEM-07)

* fix: keyed HNSW seed hydration without all_elements

* fix: cheap has_any gate for kg_semantic_context

Avoid list_all (~147k embedding_state rows) before HNSW retrieve on mega graphs.

* docs: REL-054 mega semantic smoke evidence

Mark US-SEM-06 / FR-SEM-07 / REL-054 DONE after keyed hydration Docker smoke.
* fix: mega-safe concept_search, query_graph, get_clusters

Keyed code_refs and typed name fallback; frontier-local BFS; serve
precomputed cluster_id on mega instead of live Louvain.

* fix: mega query_graph latency + REL-055 smoke evidence

Avoid unindexed name/edge full scans on mega graphs; close
US-MG-TOOL-01 / FR-ONT-MEGA-01 / FR-GF-MEGA-01 / FR-CL-MEGA-01 / REL-055.
Non-strategic same-file Jaccard clone detection is unused by agents and
refuses on mega-graphs; prefer semantic_search / concept_search instead.
* feat(ui): add LeanKG UI v2 graph shell (Phase 1)

GitNexus-inspired explorer in ui-v2/ with LeanKG REST client, Force/Tree/Circles
layouts, mega-graph skip, Vitest/Playwright parity, and screenshot report.

* fix(ui-v2): viewport fit, visible types, root expand, screenshots

Use Sigma animatedReset for camera fit, skip FA2 on Tree/Circles, show
Property/Method by default, normalize expand root path, refresh README
and screenshot report for PR #89.

* feat(docker): run leankg serve beside MCP for UI v2

Option A: entrypoint starts REST :8080 in the background, then exec
mcp-http as PID 1; compose and docker-up publish 8080 alongside 9699.

* docs: set PRD version to 3.7.7-ui-v2 after main rebase

* docs: show UI v2 screenshots in README

Replace legacy graph.jpeg/obsidian shots with the Phase 1
Force/Tree/Circles/search/query/code captures from this PR.
Phase 1 left src/embed on legacy ui/. Bake ui-v2 into rust_embed and
rebuild Docker/onrender images from ui-v2 so :8080 ships the new shell.
Canonical using-leankg skill; install.sh refreshes from it, uses
Docker container project= default, and drops stale mcp_init/RTK hooks.
Double-click containers expands via expand-service and replaces the
canvas; CodePanel skips /api/file for Service/Folder; clearer directory
errors on the file API. PRD US-UI2-10 / FR-UI2-12 / REL-057.
Sigma mounted once with kg=null callbacks; use refs so Service/Folder
double-click replaces the graph. Refresh src/embed + Dockerfile/render.yaml
for onrender UI v2 bake with npm layer cache and health check.
Graph rows can reference relative paths that only exist on a sibling
Docker mount; probe those roots, return 404 (not opaque 400), and
surface API error bodies in the UI client.
Entrypoint started serve under LEANKG_MCP_PROJECT, and project switch
could label /workspace while still serving a sibling RocksDB. Add
--project/LEANKG_SERVE_PROJECT, atomic switch, skip auto-reindex, and
drop expand ghosts missing on the active root.
Prevents expanding a sibling RocksDB while status still says /workspace.
Document RCA for Workspace dblclick → freepeak/multi-repo graph.
* feat: procedural ontology auto-update while serving

Watch ontology YAML during MCP/serve, fix boot marker for workflows.yaml,
refresh after index, and add ontology_control for sync/status.

* fix: replace ontology layer on sync to avoid GID duplicates

Cozo :put keys the full code_elements tuple, so YAML renames left
duplicate steps. Clear ontology:// rows then batch-insert; serialize
sync and retry on SQLite locks.

* docs: document procedural ontology auto-update and live correction
Default expand page 500 with Load more (+200) merge; fix hasMore on
root all_content; hierarchical Folders & files session tree that
survives Overview back and grows after load-more.
Resolve PR #92 doc conflicts with ont-proc-auto (#93) and Graphify ROI
backlog. Keep main REL-057/058/059; map UI expand proofs to REL-060/061.
linhdmn and others added 22 commits August 23, 2026 15:18
…s, identity/perf/data-quality waves (#247)

* chore(hackathon): R0 setup log + baseline gates green

* test(hackathon): R1 full MCP tool live sweep vs remote PG

* fix(mcp): update_knowledge upserts knowledge_entries on id

update_knowledge re-puts an existing row through create_knowledge_entry's
:put; the PG translator had no pk_for_table entry for knowledge_entries,
so it emitted a plain INSERT that died on the knowledge_entries_id_uniq
unique index (-32603 'Failed to update knowledge entry: db error', R1
sweep repro 2/2). Register id as the table's PK so the put becomes an
ON CONFLICT (id) DO UPDATE upsert.

* fix(mcp): mcp_index_docs yields to watchdog + extended bulk-index budget

R1 sweep: mcp_index_docs returned 'timed out after 30s' even for a
1-file docs dir while the op completed after the deadline. Two causes:

1. The doc walk/parse/ref-resolve pipeline (~75 sequential DB round-
   trips for one markdown file) ran synchronously inside the polled
   future. tokio::time::timeout can only fire between polls, so the
   watchdog surfaced only AFTER all work drained - response lost and
   the hot executor thread blocked throughout. Run the pipeline via
   spawn_blocking so the future yields and deadlines preempt promptly.

2. Budget mismatch: at remote-PG latency (~2s/query) that pipeline is
   minutes of legitimate work (R1: ~15s/file average). Give mcp_index
   /mcp_index_docs an extended watchdog floor of 300s; interactive
   tools keep 30s and an explicit LEANKG_MCP_TOOL_TIMEOUT_SECS above
   the floor still wins.

* fix(mcp): canonical project keys + exports anchored to active project root

R1 sweep issues #4/#5:

Project identity (issue #5): CLI 'index ./src' keyed the PG schema on
the literal string './src' while 'mcp-http --project <abs>' hashed the
real path - the server served an EMPTY DB right after a successful
index. All project-key derivation now flows through one
canonicalization helper (canonical_project_root): existing paths are
fs::canonicalize'd so relative/absolute spellings, trailing slashes,
. / .. components and symlinks collapse to one physical directory;
non-existent paths fall back to lexical normalization, preserving the
cross-mount project_path contract in leankg.yaml. A relative
project_path now resolves against its own project root, so the
documented './src' workaround keys identically from CLI and MCP.

Export escape (issue #4): export_graph_snapshot, export_html,
get_graph_report and the post-index GRAPH_REPORT auto-write resolved
default/relative out paths against LEANKG_MCP_PROJECT or the server
process CWD, writing artifacts into a PARENT repo's .leankg/. They are
now anchored at the request's active project root derived from the
handler's db_path.

* fix(mcp): derive project schema keys via base-aware pure helper

Follow-up to ea74cd8: the relative-vs-absolute schema test mutated the
process CWD, racing pg_url_prefers_env_then_yaml_then_default (another
test that chdirs for its own yaml lookup) under parallel test threads.
Split canonical_project_root/schema_for_path into pure *_in variants
taking an explicit base; the CWD-reading wrappers stay thin. Tests now
exercise relative spellings without touching global state.

* fix(engine): keep dynamic ontology rows visible across identity drift

BUG-B (R1 sweep issue #2): delete_ontology_concept failed with
"Element not found" and kg_ontology_status reported dynamic_concepts:0
after a server restart, yet the rows were still in Postgres. Root cause
was scoping, not durability: project->PG-schema identity keyed a RELATIVE
project_path literally (hex("./src") = leankg_p_2e2f737263), so any boot
that resolved the field differently served an empty schema.

- schema_candidates_for_path(): resolve relative project_path against the
  project root and canonicalize both spellings; expose ordered candidates
  [preferred, legacy literal key] so init adopts a populated legacy schema
  instead of silently serving an empty project (pick_schema_for_init).
- read_only_url(): fix malformed query separator that corrupted URLs with
  an existing ?sslmode=... param - readonly connections against verified-
  TLS remote Postgres never connected (E3D000 bogus database name).
- Regression tests: cross-instance dynamic concept roundtrip (add -> NEW
  engine -> lookup by gid -> delete), yaml sync-cycle preservation of
  source:dynamic rows, identity stability across spellings, legacy
  candidate shape, live R1-schema gid probe.

* fix(engine): batch hang-trio N+1 queries into single round-trips

BUG-D (R1 sweep issue #7): get_context hung 150s+ on remote Postgres.
Measured pre-fix on the R1 corpus: get_context(./src/mcp/tools.rs) took
72.4s - one ~500ms find_element() round-trip per relationship edge.

- get_context_for_file: collect all relationship targets and hydrate them
  with ONE batched lookup instead of a per-edge query.
- get_elements_by_qualified_names: one IN-list query per 500-GID chunk
  instead of one query per GID; also dedups input. This helper underpins
  delete_ontology_concept, retrieval seeding, and HNSW hydration.

Live probes vs the R1 schema (13,389 elements / 92,030 relationships):
get_context 72.4s -> 2.1s; temporal_query 4.5-6.2s (single scan, was
compounding under load); check_consistency ~7s. Regression tests assert
<10s/<15s budgets and correct results.

* fix(engine): agent_focus targeted queries + bounded pool wait

BUG-E (R1 sweep issue #1/#9, most critical): with a persona fixture
present, agent_focus loaded the ENTIRE graph (all_elements +
all_relationships, ~13k rows / ~92k rows per call on the R1 corpus),
blew past the 30s tool watchdog, and wedged every subsequent tool until
server restart. Blocking work runs under block_in_place and cannot be
cancelled by the watchdog, so each poisoned call kept running while
holding pooled Postgres connections; once all slots were held,
ClientPool::checkout blocked forever on its Condvar and every later tool
starved into "timed out after 30s".

- GraphEngine::agent_focus: persona filters (element_types / cluster_id /
  path prefixes) now run inside ONE database query (bound IN-list /
  equality / anchored-regex clauses); relationships are fetched via one
  indexed query per 500-GID chunk of the focused set. Same output as
  before (338 elements / 504 relationships on the R1 fixture), latency
  5.8s -> ~3.4s cold and O(1..#chunks) queries instead of two full scans.
- ClientPool::checkout: bounded wait (LEANKG_PG_POOL_WAIT_MS, default
  10s) - pool starvation now fails fast with a clear error instead of
  blocking forever, so no single tool can wedge the executor.
- Regression tests: agent_focus <5s on the live R1 corpus; focus ->
  immediate follow-up reads complete normally (<15s for keyed lookup +
  get_context + check_consistency sequence).

* docs(hackathon): R2 log

* feat(cli): add leankg connect for one-command MCP client setup

* docs(hackathon): cycle-01 report — sweep, 7 fixes, connect feature

* feat(audit): append-only hash-chained audit log (FR-ENT-1)

Ledger of every MCP tool call and mutating REST call: actor,
agent_client, tool, project, args_hash (never raw args), result_status,
ts — tamper-evident via SHA-256 chain (prev_hash -> entry_hash, genesis
0x64).

- migration 006_audit_log: table + ts/tool indexes + BEFORE UPDATE OR
  DELETE trigger raising 'audit_log is append-only'
- src/audit/mod.rs: AuditRecord/AuditEntry, canonical-JSON chain math
  with independently verified golden vectors, verify_chain naming the
  broken seq, JSONL export/import; AuditRecorder = bounded mpsc (1024)
  + batcher flushing ~100ms/50 rows via ONE multi-row INSERT;
  fire-and-forget, overflow drops+counts with tracing::warn; missing
  table disables after one warn
- hooks: single MCP choke point execute_tool_audited (rmcp call_tool +
  raw JSON-RPC arm; agent_client from clientInfo else transport tag),
  Axum middleware for POST/PUT/PATCH/DELETE /api/* recording route
  patterns; recorder attached in stdio/mcp-http/web server states and
  CLI audit commands
- CLI: leankg audit export --since --until --format jsonl --out FILE,
  leankg audit verify [--since --until] (RFC3339 | epoch | relative)
- backend: ledger read/write on DbBackend trait (PG multi-row INSERT +
  windowed reads); readonly opener pins the project ledger schema even
  while its code index is still empty

TDD: unit tests for chain math/tamper evidence/drop counting/overhead
(<2ms per call); live-PG suite covers migration, 100-event persistence,
append-only enforcement, export round-trip, cross-batch continuity,
tamper detection naming the sequence id.

* ci(npm): enforce npm/crate version parity

* chore(npm): sync wrapper to v0.26.0

* test(quickstart): <5min first-query smoke + CI weekly gate

* feat(export): git-committable markdown graph docs (--markdown)

* docs(hackathon): session handoff — resume queue, branch map, rules

* docs(hackathon): refresh handoff — H11 merged, H9 sole open item

* feat(cli): doctor --deep self-diagnosis suite (H9)

* docs(hackathon): R3 log — six features landed

* test(hackathon): cycle-2 live re-sweep vs remote PG

* fix(identity): preserve leankg.yaml user fields across index

Cycle-2 R2a sweep finding N1: every leankg.yaml writer serialized a
freshly generated ProjectConfig over the existing file, and
ProjectSettings.project_path carried #[serde(skip_serializing)] so even
a fresh init never emitted the schema identity anchor. The next server
boot then resolved a different PG schema and served an empty DB
(database_exists: false) while Postgres held the indexed rows.

- serialize project_path when set (skip only when None)
- merge generated configs UNDER existing files (serde_yaml Value
  read-modify-write): existing keys win, missing keys are filled,
  unmodeled custom keys survive; applies to leankg init, the mcp_init
  tool, and the setup pipeline (fill-missing instead of skip-if-exists)
- index-init self-heal: refill a MISSING anchor with the canonical
  index target before deriving the schema

* fix(identity): legacy schema adopted only when preferred empty

Cycle-2 R2a sweep finding N2: pick_schema_for_init adopted any existing
legacy candidate unconditionally, so a stale 13k-row legacy schema
pinned over a freshly indexed preferred schema (relative project_path
spellings produce both candidates).

- extract the decision into a pure 4-input predicate: adopt legacy ONLY
  when the preferred schema is missing or has no code_elements rows AND
  the legacy candidate is populated
- replace the misnamed schema_exists (which actually probed rows) with
  a precise SchemaState {exists, populated} probe; zero extra probes on
  the populated-preferred fast path
- prefer the .leankg/leankg.yaml store over the stale root-level
  leankg.yaml duplicate when the two anchors diverge, and cover the
  launcher-CWD identity invariants with pure regression tests

* fix(identity): canonicalize --project before schema derivation

Cycle-2 R2a sweep finding N3: mcp-http passed the raw --project value
down, and relative spellings plus yaml project_path/root joins were
resolved against the launcher's CWD further down — the byte-identical
relaunch from a different directory pinned an unrelated populated PG
schema.

- canonicalize --project (and the find_project_root fallback) via
  canonical_project_root at the mcp-http entrypoint, before any schema
  derivation
- canonicalize MCPServer::resolve_project_root's result so relative
  values inside leankg.yaml cannot re-key identity per launch
- add tests/regression_identity_cluster.rs: live-PG regression driving
  leankg index twice across a yaml-anchor corruption window, asserting
  user fields survive and a fresh init_db reopens the same schema

* perf(engine): batch consistency/temporal graph scans

Root cause of the R2 hang trio (check_consistency >150s, temporal_query
>120s, agent_focus >60s on the 93k-edge corpus; ~6s empty) was not the
graph engine — engine-level scans already finish in ~5s. The wedge was
the response path: TokenBudget::truncate_value cloned and re-serialized
the whole array once per popped item (O(n^2)), burning a tokio worker
for minutes on 24k findings / 93k relationships with no await point, so
timers and sibling requests starved (N4 cascade).

- token_budget: single-pass prefix-sum truncation for arrays and
  running-size key removal for objects (O(n^2) -> O(n)); primary payload
  keys (findings/relationships/elements) survive truncation.
- agent_focus: relationship chunk fetch 500 -> 10k QNs per IN-list query
  (~27 sequential round trips -> 1-2 on a 13k-element focus).
- server: 120s watchdog floor for full-graph scan tools
  (check_consistency/temporal_query/timeline/find_tunnels/agent_focus/
  get_impact_radius/query_graph/shortest_path); their old 30s expiry
  never released the pooled connection (sync scan continues inside
  block_in_place), it only discarded the finished response.

tests/perf_c2.rs pins wall-clock budgets through the handler-shaped
path (engine scan + TokenBudget::apply): corpus consistency <15s,
temporal <15s, agent_focus <30s; live before/after in the R2b report.

* fix(pool): recover slots after watchdog expiry

Regression coverage for the R2 N4 starvation path: when a slow query
holds every pool slot, concurrent checkout must fail fast (bounded by
LEANKG_PG_POOL_WAIT_MS, with a clear error) and slots must come back
promptly once the holder drains or its future is cancelled at an await
point — exactly what tokio::time::timeout does when a tool watchdog
expires between PG calls.

No pool-code change required: PooledClient RAII already releases on
drop; the observed wedge was CPU starvation from the response-path
O(n^2) truncation plus premature 30s expiry on graph-scan tools, fixed
in the preceding commit. These tests lock the recovery guarantees:
- fail-fast under full occupancy (< POOL_WAIT + slack), instant
  recovery after release (~3us measured)
- cancelled holder returns its slot within 2s

* fix(indexer): eliminate dangling edges and duplicate file rows at generation

doctor --deep on the hackathon corpus flagged 432/1000 sampled edges
referencing missing elements. Classification of all 24,431 orphans:

- calls (23,340): unresolved bare-name targets persisted after inline
  resolution failed (std/minified-bundle callees). resolve_call_edges_inline
  now drops edges it knows are unresolvable instead of storing them.
- emits/listens_on (230): bare receiver identifiers as sources and synthetic
  event::<name> targets that were never written as elements. Channel edges
  are now anchored on the containing file element and every distinct event
  gets a synthetic event element with a URI file_path.
- extends (31): __unresolved__ minified-JS class targets, plus imports/
  contains/explained_by/tested_by stragglers. A full-index safety valve
  (prune_dangling_relationships) drops any edge whose endpoints are absent
  from the element set; service_calls is exempt.
- file rows (6 dup QNs): sql/swift/objc/sfc extractors emit a lowercase
  "file" node AND the physical pass emits an uppercase "File" row for
  the same path; summary synthesis enriched only the first and silently
  kept the second. Extra rows are now collapsed in phase 2.

Unit tests cover each generator fix.

* fix(doc_indexer): unique heading QNs and resolvable docs hierarchy

doctor --deep flagged 10 duplicated qualified_names topped by
docs/analysis/perf-memory-cpu-issues.md::Fix x8: every repeated markdown
heading in a document produced an identical {doc}::{heading} section QN.
Section QNs now get an occurrence counter per document (#2, #3, ...),
keeping the first occurrence canonical for key stability.

The dir -> document contains edges sourced raw filesystem paths that were
never written as elements (207 orphans). Directory elements are now
synthesized per ancestor under docs_root, and edge targets use the
document graph QN (docs/<rel>) instead of the raw child path.

Repeated index_docs runs also left stale section elements and their
relationships behind (the x5 duplicates). Delete-before-insert now sweeps
stale relationships by endpoint (new GraphEngine::
remove_relationships_by_endpoint_bulk covers documented_by, whose doc QN
is the target), and directory rows join the element deletion keys. The
mcp_index_docs wire contract still counts real documents only.

* test: live-PG data-quality regression for fresh index

Indexes a fixture (two md files with repeated headings + one ts file with
event channels and an unresolvable call) against a scratch PG schema, runs
the docs phase twice to exercise re-index idempotency, then asserts zero
duplicate qualified_names and zero orphaned relationships across the whole
graph. Skipped when LEANKG_PG_URL is unset or unreachable.

* docs(hackathon): cycle-2 log — re-sweep green, identity/perf/data-quality waves

* chore(npm): sync wrapper to v0.26.1
…bels, tool consolidation (#249)

* docs(hackathon): open cycle 3

* feat(mcp): stable tool contract doc + CI drift guard (PLG-5)

* docs(readme): quickstart reflects one-command connect + timed smoke

* feat(graph): single confidence_label helper (ENT-9)

Extract Relationship::derive_confidence_label into
graph::provenance::confidence_label_for so every serializer derives
identical EXTRACTED/INFERRED/AMBIGUOUS labels from raw confidence +
resolver metadata. Adds element synthetic/ontology provenance helpers.

* feat(graph): confidence_label on all graph responses (ENT-9)

Thread the provenance label into every remaining edge serializer:
get_dependencies (DependencyInfo + l1 cache), get_dependents,
get_review_context relationships, and find_tunnels (Tunnel).
Element rows in impact/review responses carry synthetic:true when
derivable (synthetic/summary/event types, ontology:// and event://
paths). Contract test tests/provenance_labels.rs asserts every edge
object in graph-returning tool output carries a valid label against
a live-PG scratch fixture.

* docs(hackathon): c3 batches 1-2 log

* chore(mcp): deprecate 4 consolidation candidates

* refactor(mcp): consolidate tool surface 76->73 (H6)

* docs(hackathon): c3 h6 log
…RE-6) (#251)

* feat(cli): usage dashboard from context_metrics (PLG-8)

* docs(hackathon): c4 h10 log

* test(perf): deterministic perf regression gate + CI workflow (CORE-6)

scripts/perf_gate.sh compare/update modes; scripts/run_perf_workload.sh median-of-3 collector; gen_perf_fixture.py deterministic 20-module rust fixture; perf-gate.yml CI (pg16+pgvector service); baseline from live remote-PG run: index 18315ms, boot 19ms, search_code 13ms.

* bench: record initial baseline

* docs(hackathon): c4 h8 log
* docs(plan): adopt SQL-first datalog removal plan (W8)

* feat(db): SQL-first seam adoption (W8 P0)

* refactor(keys,content-hash): convert to parameterized SQL (W8 wave 1)

* docs(hackathon): c5 w8-p0+wave1a log
…s-only naming) (#246)

* refactor: purge residual legacy-engine and vendor references (Postgres-only naming)

- rename internal identifiers: cozo_to_pg→datavalue_to_sql,
  unescape_cozo_string→unescape_datalog_string_literal,
  convert_cozo_vec_literals→convert_legacy_json_array_literals,
  strip_cozo_vec_literals→strip_legacy_vec_literals, tracing field cozo→script
- rewrite comments/docstrings/tool descriptions/UI labels to PostgreSQL +
  pgvector-present phrasing across src, tests, docs, configs
- remove dead LEANKG_COZO_ROCKS_BULK no-op check in embeddings/build.rs
- update ui-v2 QueryFAB title to 'Raw graph query' (+ test expectations)
- add tests/no_legacy_terms_test.rs guard: zero legacy terms outside
  allowlisted historical records (531 violations fixed to 0)

No logic changes: mechanical diff audit proves every changed line is a
comment, string literal, approved identifier rename, or the documented
dead-code removal. 1043 lib tests + clippy CI gate + fmt green; live MCP
tool matrix against remote Postgres matches pre-change baseline profile.

* test(removal): fix embeddings-gated suites for remote PG + env-var robustness

Follow-up to the CozoDB removal waves: make the full --features
embeddings matrix pass against managed remote Postgres (WAN RTT,
tight connection caps) without losing local-latency guarantees.

Test infra fixes:
- batch_delete_stress: 1M-edge variant skips on remote LEANKG_PG_URL
  (budget is local-calibrated); 10k resolve-time assertion is opt-in via
  LEANKG_TEST_RESOLVE_SECS so matrix contention can't flake it
- overview_mega_tests: chunked batch seed (15k single inserts were
  WAN-bound minutes) + LEANKG_TEST_OVERVIEW_SECS budget override
- concurrent_mcp_during_embed: >=1 RO success per kind during worker
  hold (>=2 assumed local-latency round trips)
- full_index_wipe_test: unique scratch db_path per engine (fixed path
  shared one schema across tests -> cross-test row bleed); restore
  caller's LEANKG_PG_URL instead of removing (later tests fell back to
  dead localhost default); drop leaked admin connections (role cap E53300)
- multi_model_smoke_live: gate behind LEANKG_MULTI_MODEL_LIVE=1 so a
  bare LEANKG_PG_URL can't silently aim live embeds at shared PG;
  BuildOptions initializer updated for summary-primary fields

Stale assertions updated for current behavior:
- lib.rs: 'c' resolves via registry grammars now (was asserted None)
- mcp/tools.rs tool-count test: 76 base / 79 with embeddings feature
- retrieval::pipeline: merged 4 resolve_ef tests into one sequential
  test (they raced each other's set/remove of LEANKG_HNSW_EF)
- pg_regression_tools: add missing deterministic vector_for helper +
  quote embedded_at epoch (TEXT column)
- embed_doc_inventory: trigger deferred inventory refresh after direct
  index_file_sync call
* fix: index .tsx/.jsx files and route them to the TSX grammar

.tsx/.jsx files were silently producing zero extracted elements in
every recent release, despite commit 2d47635 ("index *.tsx/*.jsx
files") having fixed this once before. Root-caused via a Postgres-
backed multi-project setup where .ts files in the same directory tree
indexed correctly (confirmed via direct DB inspection: 0 rows for any
.tsx file vs. hundreds of correct rows for sibling .ts files) while
.tsx never produced a single element, for any project, regardless of
project config, project_path, or cache state.

Two independent regressions combined to fully break this:

1. `find_files_sync`'s extension whitelist in src/indexer/mod.rs
   (the walker `mcp_index` actually calls) dropped "tsx"/"jsx" in
   commit 6210ef0 (PR #40, unrelated web UI/UX rework, 2026-04-12,
   3 days after 2d47635 added them). Several other extensions
   removed in that same commit were restored later; tsx/jsx were not,
   so affected files were never even discovered by the walker.

2. Even once discovered, src/indexer/lang/registry.rs's LanguageSpec
   table never had a dedicated tsx/jsx entry — only "typescript"
   (extensions: ts/mts/cts) existed, using
   tree_sitter_typescript::LANGUAGE_TYPESCRIPT, which cannot parse JSX
   syntax. tree_sitter_typescript::LANGUAGE_TSX (the JSX-aware grammar
   variant, bundled in the same crate) was never referenced anywhere
   in the codebase.

Fix:
- Add "tsx"/"jsx" back to the find_files_sync extension list.
- Add a new "tsx" LanguageSpec entry (extensions: tsx/jsx) using
  LANGUAGE_TSX, so get_language()/get_parser_for_language() route
  these files to a grammar that actually understands JSX.

Verified against a real-world Next.js App Router codebase: indexed
file count went from 166 to 261 (the missing .tsx/.jsx files),
producing 178 correctly extracted elements (functions, classes,
methods) from .tsx files that previously yielded zero, across every
release tested (v0.19.33 through v0.24.0).

* test(indexer): regression tests for tsx/jsx routing + JSX parse

Adds the test coverage PR #237 lacked:
- tsx_and_jsx_route_to_tsx_spec: .tsx/.jsx resolve to the tsx spec,
  plain .ts still routes to typescript.
- tsx_grammar_parses_jsx_without_errors: the LANGUAGE_TSX grammar
  parses JSX with no error nodes (LANGUAGE_TYPESCRIPT could not).

Full lib suite: 1216 passed, 0 failed.

* docs: tracker reflects post-#246 reality (W8 in-progress, W12 done, v2 roadmap pointer)

- W8: P0/P1 + wave-1a/1b + PR #246 merged; 223 run_script sites remain.
- W12: npm wrapper already at 0.26.1; release.yml publish-npm job syncs on tag.
- Companion docs: point at roadmap-2027-v2.md (operational) + the SQL-migration
  plan doc (now on main, not a worktree branch).

* feat(w8): wave-2 SQL-first code_elements reads with typed backend seam

W8 wave-2 converts three element-lookup reads in graph/query.rs
(find_element, find_element_by_name, get_elements_by_qualified_names)
from Datalog run_script to a typed backend seam. The trait now owns
find_element_by_key / find_element_by_name_col / elements_by_qualified_names
with PostgresBackend impls; the engine calls those directly. FakeBackend
gains matching in-memory parity so the unit-test surface (which uses
init_db() under cfg(test)) stays green.

Live-PG parity covered by tests/pg_sql_wave2_test.rs
(#[ignore]-gated, runs against LEANKG_PG_URL). Unit tests: 1216 pass / 0 fail.

* docs: W8 wave-2 landed + FR-HEA-01..05 track opened

- prd.md: §3.31 (US-HEA) + §5.36 (FR-HEA-01..05) added; version bumped to 3.8.7-harness-era-positioning
- prd-task-tracker.md: P1 Harness-era repositioning track (FR-HEA-01..05, US-HEA-01..05, all NOT_DONE)
- roadmap-2027-v2.md: punch list rows 13-14 (FR-HEA-01 alias metric, FR-HEA-02 fallback hint); §4.1/§4.3 harness-era repositioning bullet
- roadmap-tracker.md: W14 (Harness-era live-probe fixes) added as PENDING/hackathon owner

Cross-references the 2026-08-30 live-probe assessment; total tracked 546 → 560.

* feat(mcp): streamable-HTTP SSE envelope + Mcp-Session-Id on initialize

POST /mcp responses now wrap the JSON-RPC payload in a single
'event: message' SSE frame (text/event-stream + cache-control: no-cache)
so streamable-HTTP clients honoring 'Accept: application/json,
text/event-stream' get a spec-shaped response either way.

initialize additionally allocates a Mcp-Session-Id (UUIDv4) per the MCP
streamable-HTTP spec; the server remains stateless across requests, so
follow-ups accept any session id (including none) — issuing one is the
minimum needed to stop clients from tearing the transport down on first
reconnect.

Live-verified handshake: initialize -> 200 + session id + SSE frame;
notifications/initialized -> 204; tools/call (mcp_status) with echoed
session id -> 200 + SSE frame, no session-id header on non-initialize.

* fix(ontology): backfill workflow_step aliases + apply YAML step aliases (FR-HEA-01)

get_ontology_status.nodes_missing_aliases counted every YAML-loaded
workflow_step as missing an alias: WorkflowStepMetadata::new seeded
`aliases: vec![]` (unlike WorkflowMetadata::new / FailureModeNode::new,
which seed normalize_alias(name)), and the YAML loader parsed
WorkflowStepDef.aliases but never applied it.

- WorkflowStepMetadata::new now takes `name` and seeds the name-derived
  alias (parity with the other two node kinds)
- New WorkflowStepMetadata::with_aliases / WorkflowStepNode::with_aliases
  builders
- loader.rs applies YAML step aliases via .with_aliases before the rest of
  the chain
- handler.rs dynamic path forwards step_name (drops manual re-assignment)

Live probe on leankg mcp-http (:9699): nodes_missing_aliases 57 -> 0
(workflow_steps 57 -> 0). Unit + integration tests green: 1216 lib / 12 r2.

---------

Co-authored-by: Daniel Buona <danielbuona@gmail.com>
* chore: gitignore local secret files, certs/, tooling state; add gitleaks config

* docs: move 66 historical docs to docs/archive/, single SoT = prd.md + tracker

* refactor: load CA bundles via rustls-pki-types (pem_file_iter), hermetic TLS tests; bump notify to 8

* chore: untrack certs/ (local Aiven CA bundle stays on disk, now gitignored)

* docs: prd v4.1.1 — OMP memory-backend audit (roots/list cwd channel, mnemopi contract) + zvec-grep embedding-correctness audit (FR-ZCP-11)
… contract (#267)

* docs: prd v4.3.0 — one-tool degradation ladder (FR-ZCP-03 rewrite), first-run setup contract (FR-ZCP-13), FR-ZCP-05 bridge tier; v4.2.0 measured-simplicity contract (FR-ZCP-12)

* docs: tracker sync — implementation sprint FR-ZCP-01/02, 03, 05-bridge, 12-T1, 13 DONE (35 open)

* docs: restore mcp-tool-contract.md (drift-guard diff target lost in docs/archive cutover)

* docs(tracker): add FR-ZCP-05/12 tier-remainder live rows; reconcile counts to 36 open (9 live)

* docs(tracker): DONE count 7→9 (6 sprint + 3 prior rows); footer tally clarified
…project-less URLs, register-cwd scope (#269)

Expand section 3.4 from a stub into the full contract:
- per-target config writers table (claude/cursor/codex/gemini exist;
  opencode/omp are the new writers) with exact file paths and entry shapes
- URL contract: no ?project= by default (FR-ZCP-01 clauses 1-2);
  --project escape hatch; Docker is the single documented exception
- --register-cwd = session-start hook running 'leankg add <cwd>'
  (FR-ZCP-13); persistent cwd->project table stays FR-ZCP-01 clause 3
- zero dead ends, env inventory table (FR-ZCP-12 T1), byte-identical
  config-block snapshot tests, --remove, idempotency
- tracker FR-ZCP-04 row gains the matching scope note
… in-review (PR #268), not landed (#270)

The initial §3.4 expansion cited 87e1828 (roots/list) and b251046
(leankg add) as 'landed', but both are on the v4.3.0 sprint branch,
not main. Reworded to 'in review on PR #268' so main's PRD makes no
claim about behavior main's code does not have (§3.4's own
zero-unverifiable-claims AC).
The '116 names: 88 runtime + 28 script-only + 1 docs-only' parenthetical
summed to 117 and matched no honest derivation of main's actual inventory
(src literals: 94 unique; scripts-only: 32; union: 185). The sentence
already makes the generated, CI-pinned table the source of truth, so the
manual figure beside it was drift bait — removed per §3.4's own
zero-unverifiable-claims AC.
…3.x router (#268)

* feat(mcp): server-initiated roots/list for HTTP project resolution (FR-ZCP-01 clause 2)

HTTP MCP clients that advertise the roots capability now answer a
server-to-client roots/list request at initialize; the first file://
root becomes the connection's working directory for DB routing.

- new src/mcp/roots.rs: wire-shaped roots/list request builder,
  file:// URI parsing (non-file schemes and remote hosts rejected),
  response payload parsing (first usable root wins), and a
  SessionRootCache keyed by Mcp-Session-Id (pending -> answered
  lifecycle, settle expiry, listChanged invalidation)
- the probe rides the initialize SSE response as a second
  event: message frame; the client answers with an ordinary POST /mcp
  echoing the session id, so no bidirectional channel is required
- resolution order per request: session root (roots/list) first, then
  the legacy ?project= query param, then the existing FS walk fallback
- notifications/roots/list_changed invalidates the cached root for
  clients that declared roots.listChanged; others cache for the
  connection lifetime
- clients without the roots capability (OMP sends capabilities: {})
  are never probed and resolution is unchanged (graceful degradation)

* feat: first-run setup contract + leankg add command (FR-ZCP-13)

- src/setup_config.rs: typed read/write of <project>/.leankg/config.json
  {setup: auto|manual, embed: bool}; missing file = NotConfigured, corrupt
  file degrades to default (never errors the flow); pure resolve_setup_mode
  precedence: flag > LEANKG_SETUP_MODE env > stored > TTY-gated prompt >
  manual default (non-interactive never blocks)
- leankg add [path] [--auto|--manual] [--embed]: canonicalize path, run the
  de-facto .leankg init, persist the resolved choice, print an
  mcp_status-shaped JSON summary (project root, schema, freshness, counts).
  AUTO kicks indexing as a detached child (returns <2s; no PG required);
  --embed chains a background embed after indexing via LEANKG_ADD_CHAIN_EMBED
- leankg setup --reset clears the stored setup choice so the next add re-asks
- leankg status lists every known project (LEANKG_PROJECT_DIRS + current)
  with freshness + element/relationship/vector counts when Postgres is
  reachable (--json for one machine-readable document)
- leankg install prints the leankg add hint
- db::backend::pg_reachable(): block_in_place-guarded PG probe (no nested
  block_on on the CLI tokio runtime)
- single-flight: process-local ADD_AUTO_SPAWNED flag + existing per-project
  PG advisory lock in index_codebase + embed.lock PID check for chained
  embeds

* feat: FR-ZCP-05 pg_trgm fuzzy bridge tier for the L2 keyword rung

Migration 007_trgm_fuzzy.sql (per-schema ledger): best-effort
CREATE EXTENSION pg_trgm wrapped in EXCEPTION with a NOTICE —
absence degrades to ILIKE-only recall instead of failing. GIN
trgm indexes on code_elements(name), code_elements(qualified_name),
knowledge_entries(title), knowledge_entries(content) plus one
b-tree text_pattern_ops index on code_elements(name) for
anchored-prefix LIKE. schema.sql mirrors all five indexes for
fresh installs (code_elements indexes beside its table,
knowledge_entries indexes after its table).

Query seam on DbBackend (no MCP handler changes):
- fuzzy_find_elements(query, limit) -> Vec<CodeElement>: ranks by
  GREATEST(similarity(name,$1), similarity(qualified_name,$1)),
  unioned with a literal-substring ILIKE OR-match (LIKE-escaped
  needle), rows carry the standard 11-column element projection
  plus score; degrades to ILIKE-only with constant score.
- suggest_element_names(query, limit) -> Vec<String>: did-you-mean
  via word_similarity($1, name) with <% indexable operator.
- trgm_available() probes similarity() resolution once per URL
  (LazyLock-cached) to pick the trgm or degraded path.

Live tests own a throwaway database (leankg_trgm_test_<pid>) with
a localhost-only guard — never schemas in the shared dev database;
pure-contract unit tests cover LIKE escaping, limit clamping, bind
shapes, and parameterization. 007 documents the ordering invariant
(001 creates the base tables it indexes).

Seam contract for the wave-2 FR-ZCP-03 router:
  DbBackend::fuzzy_find_elements(&self, query: &str, limit: usize)
    -> Result<Vec<CodeElement>, Box<dyn Error>>
  DbBackend::suggest_element_names(&self, query: &str, limit: usize)
    -> Result<Vec<String>, Box<dyn Error>>

* feat(mcp): lazy auto-attach + background first index, kill silent fallback (FR-ZCP-02)

- Kill the silent wrong-project fallback: an unresolved project no longer
  routes to the server-default schema (execute_tool routing + engine-path
  resolution). Structured LEANKG_ERROR_UNKNOWN_PROJECT errors carry cause +
  runnable fix (leankg add / mcp_init), naming LEANKG_AUTO_ATTACH=0 when
  auto-attach is opted out.
- LEANKG_AUTO_ATTACH (default ON): first query in a repo without a .leankg
  marker attaches the nearest marker or de-facto-inits the nearest repo root
  (mkdir + default config, mcp_init merge semantics), serves the query cold,
  and kicks a single-flight background first index. Read-only servers never
  create project state; unresolved projects error there too.
- Background indexing: ensure_project_indexed no longer runs inline in the
  request path. spawn_background_index runs parser init, incremental-or-full
  index, call edges, docs parity and ontology refresh in a spawned task with
  file-progress atomics (RunGuard clears state on exit); on completion the
  per-project engine + L1 caches are invalidated so the next query sees the
  populated graph. mcp_status-injected kick hook kick_background_index() is
  the integration seam for the FR-ZCP-03 router L0 preamble.
- Never fail with 'not initialized': mcp_status reports state (freshness:
  fresh|possibly_stale|cold|unknown + indexing {state: idle|indexing,
  files_done, files_total}) instead of erroring; empty-graph auto-attach
  responses are zero-verbosity successes with freshness:cold + indexing
  provenance. Auth/param errors unchanged.
- Watcher stays single-project-per-process (multi-project = FR-ZCP-09).

* feat(mcp): leankg_context capability router with L0-L3 degradation ladder (FR-ZCP-03)

- New src/mcp/router.rs: one default tool whose params express intent
  (semantic|lexical|impact|graph|files, auto-classified from query shape).
  Per-project capability probe (embedding_state limit-1, ::relations HNSW
  scaffold, index_inventory.total_vectors, has_elements) selects the rung:
  L3 vector -> semantic_search pipeline; L2 keyword -> DbBackend
  fuzzy_find_elements + ontology discover fusion; L1 exact ->
  search_by_name_typed + suggest_element_names; L0 cold -> non-error
  guidance + background-index kick hook. Zero-result rungs fall down the
  ladder instead of serving empty pages or errors. Every response shape
  embeds retrieval {rung, reason, freshness} (FR-ZCP-06 strings).
- kg_semantic_context no longer hard-errors without vectors: degrades to
  the L2 fusion with a structured hint + retrieval block.
- Handler: leankg_context dispatch arm; vectors_missing_hint /
  low-confidence / no-corpus hints now point at leankg_context.
- tools.rs: leankg_context registered; every tool description carries a
  'Tier:' marker (core 12 / setup 5 / advanced) for FR-ZCP-12 T3.
- safe_discover recommended_tools: drop pruned find_function/query_file,
  leankg_context is the single source of next-tool recommendations.
- DbBackend::trgm_available moves to the trait (default false) so the L2
  rung degrades honestly; FakeBackend implements the fuzzy seam + a
  test-flippable trgm flag; test-only 3s PG connect timeout so live-PG
  tests skip instead of hanging when the dev database is down.

* feat(mcp): wire router L0 index-kick to auto-attach dispatch state (FR-ZCP-03/02 integration)

* fix(mcp): skip auto-attach full index pass under test FakeBackend (FR-ZCP-02)

The background full-pass populated the cwd-ancestor repo through the
test-only FakeBackend, turning unrelated audit-dispatch tests (which
share the process cwd) into minutes-long tree-sitter walks. Guard the
full pass on a real Postgres URL; incremental and cold-attach behavior
are unchanged.

* fix: FR-ZCP-05 live tests migrated the shared database, not the scratch one

CREATE DATABASE does not switch an existing connection: the admin
client kept targeting the base URL's database, so run_migrations
applied 001..007 to the shared database's public schema (harmless
no-op there) while the scratch database stayed empty — trgm_available()
then correctly reported false and the test failed, leaving the empty
scratch DB behind.

Reconnect the admin client to the scratch database (via a shared
swapped_db_url helper) before migrating. Verified: both live tests
pass against the local pgvector:pg18 container, pg_trgm lands in the
scratch DB, teardown leaves zero leankg_trgm_test_* databases, and
the shared database's public schema is untouched.

* feat(mcp): error catalog with stable codes, causes, runnable fixes (FR-ZCP-12 T1)

Add src/errors.rs: a dependency-free catalog (ErrorCode { code, cause,
fix, doc_anchor }) with lookup()/all()/render() and a const entry per
stable code:

  LEANKG_ERROR_PG_UNREACHABLE, LEANKG_ERROR_PG_URL_MALFORMED,
  LEANKG_ERROR_PROJECT_NOT_INITIALIZED, LEANKG_ERROR_UNKNOWN_PROJECT,
  LEANKG_ERROR_AUTO_ATTACH_FAILED, LEANKG_ERROR_UNAUTHORIZED,
  LEANKG_ERROR_UNKNOWN_TOOL, LEANKG_ERROR_NO_VECTORS,
  LEANKG_ERROR_TRGM_UNAVAILABLE, LEANKG_ERROR_METHOD_NOT_FOUND,
  LEANKG_ERROR_READ_ONLY, LEANKG_ERROR_UNKNOWN_ACTION,
  LEANKG_ERROR_MISSING_PARAM, LEANKG_ERROR_PERMISSION_DENIED

CI lint (4 tests in errors.rs): entries complete (non-empty code/
cause/fix/doc_anchor), every LEANKG_ERROR_* literal in src/ resolves to
a catalog entry (100% coverage), every entry referenced by a live use
site (no dead entries), lookup/render behavior.

Migrate the top error sites to the canonical
'<CODE>: <cause>. Fix: <fix> (docs: <anchor>)' format: HTTP/SSE
Unauthorized bodies (now name --auth / MCP_HTTP_AUTH), unknown tool
(suggests nearest registry match + leankg_context hint), missing
params, permission denied, method not found, read-only enforcement,
unknown embed/ontology_control actions, auto-attach failures,
not-initialized/unknown-project paths, pg_trgm degrade notice,
malformed LEANKG_PG_URL, semantic no-vector degrade hint, status
Postgres-unreachable line, unknown --format.

No success-path behavior changes: same errors, same failures, now with
stable codes and runnable fixes.

* docs(readme): exact tool count, router-first prefer-order (FR-ZCP-12 T1)

Replace the '85+ MCP tools' claim with the code-verified count (77
registered: 77 with embeddings / 74 without — pinned by the exact-count
test in src/mcp/tools.rs) in both the Enterprise table and the
capability matrix.

MCP prefer-order: lead with leankg_context (the FR-ZCP-03 router) and
drop the pruned find_function/query_file references; exact-symbol row
now points at search_code.

All Get Started / CLI verbs verified against src/cli/mod.rs
subcommands (init, migrate, index, connect, mcp-http, serve, impact,
path, explain, graph-query, embed, mcp-stdio, ontology sync/trace,
status, update) — all exist, no changes needed.

* docs: regenerate mcp-tool-contract.md for the 77-tool surface (drift-guard target)

* feat(mcp): hard one-tool cutover — leankg_context serves ~76 capabilities via verb envelope (FR-ZCP-03 end-state, v4.3.1)

- ToolRegistry: exactly 1 registered tool; every former tool name is a verb
  (verb namespace = legacy namespace; resolve_envelope unwraps {verb},
  hard-refuses unknown names with LEANKG_ERROR_UNKNOWN_TOOL + nearest verb)
- Envelope resolved BEFORE read-only gate, write-lock, RBAC check_permission,
  and audit recording (audit records the effective capability; refusals
  recorded under the raw name)
- JSON-RPC tools/call arm: envelope before RBAC + tool_timeout_for + TOON wrap
  keyed on capability; rmcp call_tool + audited choke point likewise
- verb_catalog(): 74 feature-independent + 2 embeddings-gated capabilities
- tests: registry-length==1 invariant, envelope contract suite, server-level
  dispatches migrated to verb_args helper; removed stale test pinning pruned
  get_callers; docs/mcp-tool-contract.md regenerated (1 tool); README claim
  updated; PRD v4.3.1 + tracker (FR-ZCP-03 end-state DONE, T3 re-scoped)

* docs(tracker): v4.3.1 cutover note + repo hygiene (dependabot backlog, pre-existing test hangs)

* fix(mcp): single envelope resolution on the JSON-RPC tools/call arm

The arm resolved the envelope (for RBAC-before-gate) and then passed the
resolved verb into execute_tool_audited, which resolved AGAIN and
hard-refused the verb as an unregistered legacy name — every verb call
over streamable HTTP failed with LEANKG_ERROR_UNKNOWN_TOOL. New
execute_capability_audited dispatches a pre-resolved capability with the
same audit guarantees (capability-keyed ledger, refusal path unchanged
for outer-boundary callers via execute_tool_audited). Caught by the live
smoke test, not by the suite; lib tests 1302/0.

* docs: AGENTS.md storage wording — local Docker Postgres only (remote configs removed 2026-09-05)

* test(scale): nested-repo + mega-graph live harness wired into perf-gate CI

Adds a Tier-2 harness that proves mega-workspace behaviour on a
deterministic nested-repo fixture without touching the real index:
scratch DB, dedicated port with stale-server guard, and mega-graph mode
engaged by auto-deriving LEANKG_MAX_CACHE_ELEMENTS below the fixture's
element count (same code path a 50k repo takes, in seconds).

- gen_scale_fixture.py: nested repos at depth 1+3, noise dirs, cross-file
  edges, Rust+TS; emits .fixture-manifest so coverage iterates real repos
  (no hardcoded names); refuses --repos >8 unique slots.
- scale_harness.sh: docker (local) + direct (CI) PG modes; psql URI-vs-d
  conflict avoided via pg_admin/pg_scratch split; numeric element-count
  guard prevents vacuous mega passes; check_consistency refusal asserted
  deterministically; truncation-safe impact assertion.
- perf-gate.yml: scale-harness job (pgvector service, LEANKG_BIN set).

Verified 13/13 in docker default, docker --repos 2, and direct mode.

* test(scale): refuse vacuous mega pass when threshold derives to 0 (ELEMENTS=0 is numeric)

* feat(db): SQLite storage backend — cozo-sqlite engine, sqlite session default (v4.4.0 dual-backend)

* docs(prd): v4.4.0-three-tools-dual-backend — 3-tool surface + SQLite backend (FR-3T-01..04, M9)

* feat(mcp): 3-tool registry — set / get / status (FR-3T-01, v4.4.0)

- registry: exactly 3 tools with per-tool action namespaces; legacy verb
  names accepted as actions (zero-loss migration); get with no action =
  multi-layer NL router (L0-L3)
- resolve_3tool replaces resolve_envelope at all three dispatch sites
  (server execute_tool, audited choke point, JSON-RPC arm) — security
  ordering preserved: resolution before RO-gate/write-lock/RBAC/audit
- server tests migrated from one-tool envelope to 3-tool surface;
  tools.rs tests rewritten for the 3-tool contract (len==3, tier counts,
  descriptions); cli_tests Status pattern updated for struct variant

* docs: regenerate tool contract for 3-tool surface (set/get/status)

* fix(test): schema-derivation test uses symlink-resolved base (macOS /tmp); pg_regression doc comments

The relative_and_absolute_spellings_derive_same_schema test was env-dependent:
TempDir paths under /tmp resolve to /private/tmp on macOS, and the test
compared schemas derived from literal vs symlink-resolved spellings. Both
flows now use the symlink-resolved dir as the base, making the test
deterministic on any platform. Also un-gated PathBuf import (needed by
SqliteBackend under release builds).

* fix(db): search_knowledge_entries — use environment filter parameter

* fix(mcp): 3-tool dispatch on every transport — rmcp arm + RO listing

execute_tool_audited (the rmcp call_tool arm: stdio + streamable HTTP)
still resolved through the one-tool envelope and hard-refused
set/get/status; only the raw JSON-RPC HTTP arm had been rewired. Both
transports now share resolve_3tool:

- resolve_3tool accepts the legacy leankg_context verb envelope
  (back-compat) plus action/verb aliases on set/get/status
- resolve_envelope deleted (dead one-tool gate)
- read-only servers hide the set tool from tools/list
  (is_ro_hidden_tool) instead of advertising it and failing at dispatch
- SQLite backend: embedding-state tables now ensured in
  SqliteBackend::open (needs a DbBackend handle; fixes --all-features
  build) instead of init_schema (raw CozoDb)
- drop uncompilable benches/redundant_tool_overhead.rs (criterion
  unlinked; benchmarks removed tools)
- ontology_e2e: WorkflowStepMetadata::new gained the name arg

Live smoke (SQLite engine): stdio RO tools/list=[get,status], set
refused as LEANKG_ERROR_READ_ONLY at resolved mcp_index, NL router +
legacy envelope dispatch OK; writable stdio+HTTP list 3 tools, set
add_knowledge + mcp_index succeed on SQLite.

* fix(status): storage_engine reflects actual backend (sqlite|postgres)

mcp_status hardcoded storage_engine:"postgres" — the status tool
description promises sqlite|postgres and the live 3-tool smoke showed
sqlite:// paths labeled postgres. DbBackend gains engine_name()
(default derived from redacted_url) and mcp_status reads it.
…289)

* docs(prd): v4.4.1 — live-tested 3-tool SQLite server, issues #286-#288

Changelog entry + tracker rows for the live 3-tool SQLite validation
(this repo: 581 files, 9522 vectors, L1/L2/L3 verified) and the Datalog
repairs that landed with #285. Open follow-up bugs #286/#287/#288.

* bench: leankg 3-tool surface vs grep baseline — 16-question dataset + runners

Dataset (benchmark/grep_vs_leankg/questions.json): exact / concept /
structure / impact questions over this repo with file-level ground
truth. Baseline: scripts/grep_vs_leankg.sh (grep -rn, top-5 files, no
ranking). Subject: scripts/leankg_vs_baseline.py (router get, top-5
elements) against the live sqlite server.

First-run results (report.md): grep 6/16, leankg 6/16 hit@5 (3-3 in
direct wins) but grep is 100x faster. Defects filed: #290 (identifier
queries not classified to L1 exact), #291 (minified JS pollutes ANN),
#292 (embedder reloaded per request ~18s/query).

* bench v2: exclusions + rg ranking + impact-via-search_code; report + issues #293/#294

* bench: limitations — impact-row protocol asymmetry; embeddings not validated by this run

* bench: summary correction — exact rows ran via router, not search_code
…n recorded (#318)

Syncs the PRD and tracker to the merged state: all 8 fix PRs (#298/#301/
#307/#304/#311/#312/#313/#305/#315) landed on main with fanout review;
live validation on this repo (578 files, 10,202 elements, 12,984
vendor-free vectors, router 2.31s warm); open items and follow-ups
listed with owners.
…ove Docker/Postgres triggers (#326)

Per repo direction: sqlite is the default engine; nothing in the default
flow may require or trigger Postgres or Docker.

Removed (docker/PG-only tooling):
- scripts/docker-up.sh, docker-reload.sh, docker-sync-binary.sh
- scripts/embed-all-workspaces-then-mcp.sh, test-cold-embed-perf.sh,
  test-live-embed-no-stop.sh (docker-compose orchestration)
- Dockerfile, Dockerfile.embed-worker, docker-compose.yml,
  docker-compose.embed.yml, .dockerfile.example
- install.sh: the 'docker' subcommand + stale Docker MCP help text
  (default MCP project= is now the checkout directory)

Workflows (all CI now runs sqlite, no service containers):
- perf-gate.yml: both PG services + pgvector step removed
- quickstart.yml: PG service removed
- leankg-update.yml: PG service removed (LEANKG_DB_ENGINE=sqlite)

Scale harness rewritten for sqlite: fixture-local .leankg instead of a
scratch PG database; engine-aware counting via 'leankg status --json';
per-repo coverage via get_impact_radius on each repo's m01.rs (ranked-
search probes raced the boot auto-index and were flaky); auto-index
settle wait; RSS gate widened to 3 GB (mega-mode peaks ~1-2 GB).
Root cause of the week-long perf-gate red: the harness still asserted
the pre-#283 one-tool registry — read-only now exposes {get, status}.

quickstart_smoke.sh + start-mcp.sh: sqlite pinned as default engine.
status CLI: reads counts from the sqlite db directly (no PG probe, no
PG-unreachable error on the default path); JSON output gains 'engine'.
User-facing PG hint text no longer suggests docker compose.
README/AGENTS.md: storage docs rewritten (sqlite default, PG opt-in).

Local verification:
- scale harness on sqlite: 12/12 PASS (fixture 3333 elements, nested
  discovery, noise skip, incremental reindex, RO tool contract
  get+status, search/impact verbs, mega-mode refusal, RSS bounded)
- status on this repo (sqlite): elements 13842 / relationships 76804 /
  vectors 12984 without any PG probe
- bonus: the #322 signal logger captured SIGTERM with name during
  harness shutdown — silent-death attribution works as designed
Bumps [@vitest/mocker](https://github.com/vitest-dev/vitest/tree/HEAD/packages/mocker) to 5.0.0 and updates ancestor dependency [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). These dependencies need to be updated together.


Updates `@vitest/mocker` from 3.2.7 to 5.0.0
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/mocker)

Updates `vitest` from 3.2.7 to 5.0.0
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/vitest)

---
updated-dependencies:
- dependency-name: "@vitest/mocker"
  dependency-version: 5.0.0
  dependency-type: indirect
- dependency-name: vitest
  dependency-version: 5.0.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Sep 9, 2026
@linhdmn
linhdmn force-pushed the dependabot/npm_and_yarn/ui-v2/multi-00f7b83f97 branch from d87a753 to 82e0c83 Compare September 14, 2026 05:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants