Skip to content

chore(deps-dev): bump browserslist from 4.28.6 to 4.28.8 in /ui-v2 - #266

Open
dependabot[bot] wants to merge 529 commits into
mainfrom
dependabot/npm_and_yarn/ui-v2/browserslist-4.28.8
Open

dependabot[bot] wants to merge 529 commits into
mainfrom
dependabot/npm_and_yarn/ui-v2/browserslist-4.28.8

Conversation

@dependabot

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

Copy link
Copy Markdown
Contributor

Bumps browserslist from 4.28.6 to 4.28.8.

Release notes

Sourced from browserslist's releases.

4.28.8

  • Fixed including kaios in baseline queries (by @​Jaybhade).

4.28.7

Changelog

Sourced from browserslist's changelog.

4.28.8

  • Fixed including kaios in baseline queries (by @​Jaybhade).

4.28.7

Commits
  • f2f2e6c Release 4.28.8 version
  • d0787c8 Update dependencies
  • fcf8fa9 Merge pull request #939 from Jaybhade/fix/baseline-kaios-without-downstream
  • 57ecd64 fix: support "including kaios" without downstream
  • 093a0f6 Update EM banner
  • b637868 Release 4.28.7 version
  • 313f465 Update dependencies
  • c935c5a Fix regexp performance
  • d7e9e65 Rewrite structure parsing to make it always fast
  • ec4a55e Fix import order
  • Additional commits viewable in compare view

Dependabot compatibility score

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 10, 2026 22:16
…e, get_graph_schema, find_dead_code (#67)

* feat: Phase 1 structural parity - resolution_method, get_architecture, get_graph_schema, find_dead_code

- Add resolution_method (name|name_file_hint|unresolved) to call edge metadata (FR-B01)
- Add 7 new RelationshipType variants: HttpCalls, Emits, ListensOn, SimilarTo,
  CrossRepoSimilar, RoutesTo, DefinesRoute (FR-B10 prep)
- Add get_architecture MCP tool: languages, entry points, routes, clusters, hotspots,
  relationship summary, knowledge count (FR-B20)
- Add get_graph_schema MCP tool: element type counts, relationship type counts (FR-B21)
- Add find_dead_code MCP tool: functions with zero callers, excluding entry points (FR-B23)
- Add count_knowledge helper for knowledge entry counting

* test: add unit tests for get_architecture, get_graph_schema, find_dead_code

- test_graph_schema_counts: verify element type/relation type keys and totals
- test_graph_schema_empty_db: verify zero counts on empty database
- test_architecture_returns_languages: verify language detection and structure keys
- test_architecture_finds_entry_points: verify main, serve, Start detection
- test_architecture_finds_hotspots: verify top file by function count
- test_find_dead_code_finds_unused: verify dead code detection with call edges
- test_find_dead_code_excludes_entry_points: verify main excluded
- test_find_dead_code_excludes_tested: verify tested_by exclusion
- test_find_dead_code_respects_min_lines: verify min_lines threshold
- Helpers: insert_test_element_full, insert_test_rel

* fix(structural-parity): correct arity and parser errors in Phase 1 tools

- get_architecture: 5 sub-queries used partial arity against the 13-col
  code_elements table. Cozo rejects with 'Arity mismatch for rule
  application code_elements'. Extend placeholders to match the
  established 11-named + tail pattern.
- get_graph_schema: same arity fix for the element_type aggregate.
- count_knowledge: 8 placeholders against 13-col knowledge_entries.
  Silently masked by unwrap_or(0) so knowledge_count always reported 0.
- find_dead_code: rewrite as a candidate fetch + in-Rust set difference.
  The original not(*relationships[...], _ = qualified_name) form is not
  accepted by this Cozo version ('Encountered unsafe negation'), and
  not *relationships[_, q, ...] in the rule head left qualified_name
  unbound. Cozo's :order also does not accept arithmetic, so the
  span is now a head column sorted positionally.
- find_dead_code now also filters serve/start/Start in addition to main.
- get_architecture entry_points query expanded with Start.
- route query: rebind metadata so the head is not unbound.
- cluster query: rename the aggregate operand to qn (was unbound).
- Docs: add Structure Tools and resolution_method section to
  docs/mcp-tools.md, Phase 1 status to docs/roadmap.md, and a
  one-paragraph pointer in AGENTS.md.
- .gitignore: hide .leankg.bak-pre-0.17.8-upgrade.

All 9 new unit tests pass; 470/470 lib tests pass; 27/27 integration
tests pass under --test-threads=1 (the 2 default-thread failures are
pre-existing SQLite locking races unrelated to this change).

* test: add integration tests using LeanKG codebase patterns as fixtures

6 integration tests:
- test_get_architecture_on_leankg_patterns: validates languages,
  entry points (main), hotspots, clusters, relationship summary, totals
- test_get_graph_schema_on_leankg_patterns: validates element types
  (function/struct/enum), relationship types (calls/tested_by), totals
- test_find_dead_code_on_leankg_patterns: validates dead code exclusion
  for called, tested, and entry-point functions
- test_architecture_structure_contract: validates all required keys present
- test_graph_schema_structure_contract: validates all required keys present
- test_resolution_method_in_metadata: validates resolution_method in calls

Uses 20 elements + 11 relationships mirroring real LeanKG code patterns.
Run: cargo test --release -- phase1_integration_test
#68)

* feat: HTTP route extraction for Go and TypeScript frameworks

- Add route_extractor module with tree-sitter based HTTP route detection
- Support Go frameworks: net/http, chi, gin, echo (FR-B11: >= 2)
- Support TS frameworks: express, fastify (FR-B11: >= 2)
- Generate route CodeElements with method, path, handler, framework metadata
- Generate http_calls edges (handler -> route) with confidence (FR-B12)
- Generate defines_route edges (file -> route)
- Integrate into indexing pipeline for Go/TS/JS/TSX/JSX files
- Routes included in get_architecture via route element_type query
- 6 unit tests covering chi, gin, express, fastify, elements/rels, parsing

PRD requirements: FR-B10, FR-B11, FR-B12, FR-B14

* style: fix rustfmt in route_extractor

Collapse multi-line function signatures and call expressions that fit
within rustfmt's max line width, restoring CI Format Check for #68.

- try_go_route, try_ts_route, try_ts_mount signatures
- RouteExtractor::extract_routes calls in two test cases

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: add benchmark tests using leankg codebase as data + fix clippy

20 benchmark tests covering all Phase 1 tools:
- get_architecture: 6 tests (keys, languages, entry points, hotspots, clusters, rels)
- get_graph_schema: 3 tests (element types, rel types, totals)
- find_dead_code: 5 tests (excludes called/tested/entry, finds dead, min_lines)
- resolution_method: 1 test (metadata present)
- route extraction: 5 tests (chi, gin, express, fastify, elements+edges)

Uses 39 elements + 19 relationships from real leankg source patterns.
Run: cargo test --release -- phase1_benchmark_test

* fix(route): correct get_architecture query position + add e2e tests

- src/graph/query.rs: get_architecture's route query had wrong wildcard count.
  The pattern '[...language, _, _, metadata, _{tail}]' was binding 'metadata'
  to 'cluster_label' position, leaving metadata empty. Fixed to 3 wildcards
  and direct metadata binding, ensuring method/path/framework metadata is
  returned in the architecture overview.

- tests/route_extractor_e2e_tests.rs: Add 5 end-to-end tests that exercise
  the full EntityExtractor.extract() pipeline to validate routes are produced
  during real indexing for Go, TS, JS, Rust files, and that USE routes are
  not duplicated.

Closes known issue where get_architecture returned empty method/path/framework
columns for route elements.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: support nested multi-repo git for MCP auto-index

Workspace roots like BE are not themselves git repos; detect nested
.git trees so require_git_for_auto_index and incremental indexing work.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: ontology-first paginated queries to avoid mega-graph OOM

Route discovery through concept ontology and semantic search with
hard pagination; refuse full-scan tools and skip incremental
dependent expansion on graphs above LEANKG_MAX_CACHE_ELEMENTS.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(tools): align semantic_search description with test contract

Add 'Natural language' to the semantic_search tool description so the
test_semantic_search_tool_exists contract assertion passes.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
- entrypoint.sh: ontology sync falls back to /workspace/ontology
  when MCP project has no concepts.yaml
- src/mcp/server.rs: add GET SSE handler on /mcp for modern
  streamable-HTTP Cursor clients (legacy /mcp/stream still works)
- src/graph/query.rs, src/mcp/{handler,tools}.rs, tests/*: refactor
  / consolidation (no functional change intended, needs review)
…orkspace-be

Background: container restarts skipped incremental indexing of /workspace-be
(root cause: outdated image without the ace1b59 nested-git fix). After the
fix landed, container still went 'unhealthy' because (a) serve_http awaited
auto_index_if_needed before binding the HTTP listener and (b) the auto-index
opened a second DbInstance, hitting RocksDB's per-process single-handle rule.

Three coupled changes:

1. Dockerfile.rocksdb: install git in the runtime image so the freshness
   probe (git log -1 --format=%ct HEAD) works. The previous slim image
   only shipped ca-certificates, curl, libstdc++6.

2. src/mcp/server.rs: serve_http now tokio::spawns the auto-index task
   instead of awaiting it before binding the listener. /health responds
   immediately and incremental indexing runs concurrently with query
   traffic. Also: get_graph_engine now routes through the path-keyed
   graph_engine_cache so the auto-index task and request handlers share
   a single DbInstance per project.

3. src/graph/query.rs: GraphEngine.db is now Arc<CozoDb>. Without this,
   every GraphEngine::clone() opened a fresh CozoDb handle and RocksDB
   rejected the second handle in the same process with 'lock hold by
   current process'. With Arc, all clones share one underlying handle.

Verified live:
- /health returns ok within ~1s of container start
- mcp_status returns full counts (622,368 elements, 379,615 functions,
  31,368 classes, 1,076,177 relationships) without lock error
- search_code returns real results from /workspace-be Go monorepo
- Background auto-index progresses (counts climb while MCP serves)
…72)

* chore: wip local changes before docker rebuild

- entrypoint.sh: ontology sync falls back to /workspace/ontology
  when MCP project has no concepts.yaml
- src/mcp/server.rs: add GET SSE handler on /mcp for modern
  streamable-HTTP Cursor clients (legacy /mcp/stream still works)
- src/graph/query.rs, src/mcp/{handler,tools}.rs, tests/*: refactor
  / consolidation (no functional change intended, needs review)

* feat(mg-03): single-repo root expansion auto-loads full graph

Detect single-repo layouts (no nested .git directories under root)
and auto-enable full-content expansion when the user double-clicks
the service node at the project root. Adds detect_single_repo helper
plus unit tests covering no-git, root-only-git, and multi-repo cases.

* feat(gf-04): edge provenance labels (EXTRACTED/INFERRED/AMBIGUOUS)

Add Relationship::confidence_label() that maps the existing
resolution_method + confidence into a 3-class provenance label
agents can rely on (EXTRACTED = explicit in source, INFERRED =
resolver-derived, AMBIGUOUS = needs review). New helper module
confidence_labels exports the canonical strings. Includes unit
tests covering typed/name/file_hint/unresolved bands and the
confidence-based fallback.

* feat(gf-01): shortest_path MCP tool + leankg path CLI

Adds GraphEngine::shortest_path BFS over qualified_name graph
with provenance labels on each hop, the shortest_path MCP tool
definition + handler, and a leankg path CLI subcommand. Inputs
accept qualified_name, exact name, or fuzzy suffix; max_hops
clamped to 1-10. Returns PathHop[] with rel_type, confidence,
confidence_label, source_file. Unit tests cover type ranking and
hop serialization.

* feat(gf-02/gf-05): explain_node + get_god_nodes MCP + CLI

explain_node returns a single-node dossier (definition site, cluster,
in/out degree, top neighbors by relation type). get_god_nodes ranks
elements by combined degree with optional percentile hub-exclusion.
Adds MCP tool definitions + handlers, leankg explain and leankg gods
CLI subcommands, NodeExplanation / GodNode / NeighborHint types, and
unit tests covering serialization and degree ordering.

* feat(gf-06): GRAPH_REPORT.md generator + get_graph_report MCP

Adds GraphEngine::generate_graph_report + GraphReport::to_markdown
which produces a markdown summary (overview, confidence distribution,
top god-nodes with percentile-based hub exclusion, suggested agent
questions). Adds get_graph_report MCP tool, leankg report CLI
subcommand (writes to .leankg/GRAPH_REPORT.md by default), and a
unit test covering markdown rendering.

* feat(mp-02): layered context loading L0-L3 + load_layer MCP

Adds GraphEngine::identity_context (L0, ~50 tok) and
critical_facts_context (L1, ~120 tok) generators, plus load_layer
MCP tool that exposes L0 (project identity), L1 (critical facts),
L2 (cluster members) and L3 (deep search) layers on demand. L0/L1
persist to .leankg/identity.md and .leankg/critical_facts.md for
next-session wake-up.

* feat(mp-08): folder-as-graph helpers (folder_gn module)

Adds folder_gn::qualified_name / strip / is_directory / metadata
helpers that codify the trailing-slash convention for directory
qualified_names (FR-MP-21..23). Adds GraphEngine::subdirectories for
folder-scoped queries (FR-MP-25). Directory element type, contains
edges (directory->directory and directory->file), and metadata
population (child_count, language_distribution, total_lines) were
already implemented in src/indexer/mod.rs; this commit exposes
canonical helpers so agents and web UI can use them consistently.
Includes unit tests covering helper functions.

* feat(mp-01): temporal knowledge graph (valid_from/valid_to)

Adds GraphEngine::valid_from / valid_to helpers that read timestamps
from relationship metadata (epoch seconds), plus temporal_query
(graph state at a given epoch) and timeline (chronological
evolution of a code element's relationships). Adds MCP tools
temporal_query and timeline, TimelineEvent struct, and unit tests
covering metadata read paths and event serialization. On-disk schema
unchanged — temporal data is stored in relationship.metadata JSON
so the feature is backward-compatible.

* feat(mp-05): check_consistency MCP + leankg check-consistency CLI

Adds GraphEngine::check_consistency which scans all relationships
for BROKEN (target/source element missing from code_elements) and
STALE (valid_to set but row still present) findings. Returns a
ConsistencyReport with counts and per-finding details. Adds MCP
check_consistency tool, leankg check-consistency CLI subcommand with
optional --severity filter, ConsistencyReport / ConsistencyFinding
types, and unit tests for serialization.

* feat(mp-06): cross-domain tunnels (find_tunnels MCP + leankg tunnels CLI)

Adds GraphEngine::find_tunnels which scans relationships and
returns edges where source and target belong to different Leiden
clusters, sorted by confidence descending. Adds MCP find_tunnels
tool, leankg tunnels CLI subcommand, Tunnel struct, and a unit
test for serialization.

* feat(gf-07): rationale extraction for WHY/NOTE/HACK/FIXME/XXX markers

Adds extract_rationale_markers in src/indexer/mod.rs that scans
source files for # WHY:, // NOTE:, // HACK:, // FIXME:, // XXX:
comment markers. Each marker becomes a rationale element with an
explained_by edge to the enclosing function (or the file when no
function context). Supports block-comment scoping for C-style
multi-line comments. Includes unit test covering all three primary
markers.

* feat(gn-07): get_cluster_skill MCP tool (per-cluster SKILL.md)

Adds get_cluster_skill that generates a per-cluster SKILL.md with
label, member count, top files (by member count), representative
files, entry points (inbound edges from outside the cluster), and
usage hints pointing at search_code/explain_node/find_tunnels.
Useful for agent contexts scoped to a single cluster.

* feat(mp-04): specialist agent contexts (agent_focus + diary MCP)

Adds AgentPersona / DiaryEntry / AgentFocus types, list_agents and
agent_focus methods on GraphEngine, and MCP tools agent_focus,
agent_diary_write, agent_diary_read. Personas live in
.leankg/agents/<name>.json with optional path_filters / cluster_id
/ element_types; agent_focus returns a filtered subgraph matching
the persona, agent_diary_* append/read JSONL notes for the
session. Unit tests cover persona roundtrip + diary entry shape.

* feat(cbm-b10): typed_resolve feature flag in IndexerConfig

Adds typed_resolve string field to IndexerConfig (default "off")
plus typed_resolve_enabled(setting, language) helper that interprets
the flag values: "off" / "" / "false" / "no" disable typed
resolve, "all" / "on" / "yes" enable for every language, and
CSV like "go,ts" enables only the listed languages. Unit tests
cover off/all/csv paths.

* feat(gf-09): work-memory reflect loop (report_query_outcome + lessons)

Adds GraphEngine::report_query_outcome that appends a structured
entry to .leankg/reflections/LESSONS.md capturing the question,
returned nodes, outcome (useful | dead_end | corrected), and an
optional free-form note. Adds MCP report_query_outcome tool and
leankg reflect CLI subcommand. Future agents can read LESSONS.md
to bias query ranking toward previously-useful nodes.

* feat(v2-12): get_team_map MCP tool (team + on-call ownership)

Adds GraphEngine::get_team_map which aggregates service_metadata
rows into per-team entries (team name, on-call rotation, services
owned) for a given environment. Adds get_team_map MCP tool,
TeamMapEntry struct, and unit test for serialization.

* feat(gn-08): get_overview_context MCP tool (resource-like overview)

Adds get_overview_context which aggregates wake_up, identity_context
(L0) and critical_facts_context (L1) into a single MCP response
that an agent can consume at session start. Equivalent to MCP
Resources for overview context — leankg-mcp doesn't currently
expose the RMCP resources API, so this tool provides the same
ergonomics (one call -> full session-start context).

* feat(cbm-c2): hot-path cache for high-frequency MCP tools

Adds a 60s TTL / 256-entry TimedCache to ToolHandler, wrapping
find_function (and ready to extend to other hot-path tools). Cache
key is (tool, args), exposed via hot_cache_get / hot_cache_put /
invalidate_hot_cache so write-tracker events can clear stale
entries after re-index.

* feat(gf-08): PR impact dashboard (get_pr_impact MCP + leankg prs CLI)

Adds GraphEngine::pr_impact which scores a list of changed files
by the number of distinct Leiden clusters they touch and returns a
severity rating (LOW / MEDIUM / HIGH) plus per-file cluster
membership. Adds get_pr_impact MCP tool, leankg prs CLI subcommand
with --files <csv>, PrImpactReport / PrFileImpact types, and unit
test for serialization.

* fix(mcp): unblock HTTP listener + resolve RocksDB lock conflict on /workspace-be

Background: container restarts skipped incremental indexing of /workspace-be
(root cause: outdated image without the ace1b59 nested-git fix). After the
fix landed, container still went 'unhealthy' because (a) serve_http awaited
auto_index_if_needed before binding the HTTP listener and (b) the auto-index
opened a second DbInstance, hitting RocksDB's per-process single-handle rule.

Three coupled changes:

1. Dockerfile.rocksdb: install git in the runtime image so the freshness
   probe (git log -1 --format=%ct HEAD) works. The previous slim image
   only shipped ca-certificates, curl, libstdc++6.

2. src/mcp/server.rs: serve_http now tokio::spawns the auto-index task
   instead of awaiting it before binding the listener. /health responds
   immediately and incremental indexing runs concurrently with query
   traffic. Also: get_graph_engine now routes through the path-keyed
   graph_engine_cache so the auto-index task and request handlers share
   a single DbInstance per project.

3. src/graph/query.rs: GraphEngine.db is now Arc<CozoDb>. Without this,
   every GraphEngine::clone() opened a fresh CozoDb handle and RocksDB
   rejected the second handle in the same process with 'lock hold by
   current process'. With Arc, all clones share one underlying handle.

Verified live:
- /health returns ok within ~1s of container start
- mcp_status returns full counts (622,368 elements, 379,615 functions,
  31,368 classes, 1,076,177 relationships) without lock error
- search_code returns real results from /workspace-be Go monorepo
- Background auto-index progresses (counts climb while MCP serves)

* chore(release): bump version to 0.17.9

* chore(release): regen Cargo.lock for 0.17.9

* feat(lang-01): Dart extraction — getter/setter + enum support

Adds Dart-specific handling in get_node_name and visit_node for
getter_signature / setter_signature node types emitted by the
tree-sitter-dart grammar. Getters and setters now produce method
elements under the enclosing class (previously only the class was
extracted). Adds regression tests covering Dart enum and getter /
setter extraction. All 9 Dart tests pass; full lib test suite
remains green at 525 passed.

* feat(lang-02): Swift entity extraction (regex-based)

Adds src/indexer/swift.rs with regex-based extraction for class,
struct, enum, protocol, extension, func, init, var/let property, and
import. Tracks the most-recent enclosing type so func/init emit as
method with a defines relationship under that parent. Top-level
funcs (no enclosing type) emit as function. LeanKG does not bundle
a tree-sitter-swift binding; this regex extractor is a stop-gap
that covers the common Swift constructs until a tree-sitter binding
is added. Includes unit tests covering class/struct/enum/protocol,
property, method, extension, init, and imports.

* feat(lang-03): XML entity extraction — child elements + attributes

GenericXmlExtractor now emits one xml_element per unique tag
(deduplicated) with attribute metadata captured via BTreeMap.
Maintains an open-tag stack to emit contains relationships
(root -> child -> grandchild). File-level element is added so
search_code/find_function can locate the file even when content
is degenerate. Android XML files are still skipped in favor of
the specialized Android extractors. New regression test covers
nested elements and attribute capture.

* feat(cbm-b6): event channel edges (emits / listens_on)

Adds src/indexer/event_edges.rs that scans source for EventEmitter
patterns (emit / fire / publish / trigger / dispatch), DOM
dispatchEvent, and listener patterns (on / once / addListener /
listen / subscribe / addEventListener). Emits emits and
listens_on relationships with a synthetic event::<name>
qualified_name so agents can trace event flow across modules.
Wired into the main extraction pipeline in mod.rs. Unit tests
cover Node EventEmitter, DOM dispatchEvent, and empty input.

* feat(cbm-b7): clone / near-duplicate detection (find_clones MCP + CLI)

Adds GraphEngine::find_clones which compares every function /
method body via Jaccard token-set similarity and emits ClonePair
results with source/target qualified_name, similarity score,
and file paths. Threshold and limit are configurable. Adds MCP
find_clones tool, leankg clones CLI subcommand with --threshold
/ --limit, ClonePair struct, and unit tests for the Jaccard
helper (identical, disjoint, overlapping) at the three corners
of the similarity range.

* feat(cbm-b8): cross-repo similar edges (find_cross_repo_similar)

Adds GraphEngine::find_cross_repo_similar which walks every
registered repo's .leankg database, groups functions / methods /
classes by their qualified_name suffix, and emits
CrossRepoSimilar entries for pairs across distinct repos. Useful
for surfacing symbols that drifted between services (e.g. the
same authenticate function implemented twice). CrossRepoSimilar
struct + serialization test cover the schema.

* feat(gf-11): portable graph snapshot (export_graph_snapshot MCP)

Adds GraphEngine::export_snapshot which writes every code element
+ relationship to a JSON file at out_path. File paths are
rewritten relative to project_root via a relativize helper so the
snapshot can be committed to git and merged between teams.
Adds MCP export_graph_snapshot tool and unit tests covering the
relativize helper.

* feat(gf-12): SQL DDL parser (tables, columns, FKs)

Adds src/indexer/sql.rs that scans .sql files for CREATE TABLE
statements and emits table / column elements plus references
relationships for foreign keys. Respects nested parens, string
literals, and SQL line / block comments when splitting the table
body. Detects inline and constraint-form PRIMARY KEY clauses.
Unit tests cover schema with PK, FK, and string / paren-aware
splitting.

* feat(us-14): npm-based installation wrapper

Adds npm/leankg/ — a thin Node wrapper that downloads the prebuilt
leankg binary for the current platform + arch from the latest
GitHub release. No Rust toolchain required.

Files:
  - package.json: name leankg, version 0.17.9, postinstall hook,
    engines.node >= 14, supports darwin / linux / win32 on
    x64 + arm64.
  - bin/leankg.js: Node wrapper that spawns the downloaded binary,
    re-running the postinstall on demand if the binary is missing
    (e.g. after switching Node versions).
  - scripts/install.js: postinstall that resolves the latest tag
    via GitHub API, picks the right asset, downloads + chmods the
    binary. Falls back to a pinned v0.17.9 tag on network failure
    and prints a clear cargo-install hint when offline.
  - README.md: install / usage / supported-platforms docs.

* feat(v2-11): CI/CD auto-graph update (GitHub Actions workflow)

Adds .github/workflows/leankg-graph.yml that runs on push to main
(or manual dispatch) to:
  1. Install leankg via the npm wrapper (no Rust toolchain needed).
  2. Cache .leankg/ between runs by SHA so unchanged repos skip
     the heavy index step.
  3. Run `leankg init` + `leankg index . --env ci` to refresh
     the RocksDB-backed knowledge graph.
  4. Generate .leankg/GRAPH_REPORT.md and upload both as
     workflow artifacts (14 / 30 day retention).
  5. Optionally commit the .leankg/ cache back to the repo when
     the workflow_dispatch `commit_graph_cache` input is true.

Hits the V2-11 success metric (graph freshness < 3 min after
push) without requiring each developer to re-index locally.

* feat(gf-10): Vue + Svelte SFC extractors

Adds src/indexer/sfc.rs with regex-based extraction for
Single-File Components (Vue .vue and Svelte .svelte). Captures
the component name (from default export, defineComponent name, or
filename), <script> / <script setup> blocks, <template> blocks
(Vue), <style> blocks, and the implicit Svelte root template.
is_test_file now also recognizes *.spec.vue / *.test.vue and
*.spec.svelte / *.test.svelte. Unit tests cover both Vue and
Svelte SFCs.

* feat(cbm-b1): LSP bridge for typed resolve (multi-repo + nested dirs)

Adds src/lsp/ with a generic LSP bridge that spawns any configured
language server, sends textDocument/definition + references via
JSON-RPC, and returns LspLocation. Caches one client per
(language, workspace_root) so microservice monorepos get the
right rootUri per service.

Multi-repo + nested-directory support: find_workspace_root walks
up from the file path to the nearest .git / leankg.yaml / go.mod
/ package.json / Cargo.toml / pyproject.toml / pom.xml /
build.gradle* / tsconfig.json / Gemfile / mix.exs / pubspec.yaml /
Project.toml / Package.swift, matching the manifests CBM supports.

Wired behind the typed_resolve feature flag (US-CBM-B10). On
any failure (binary missing, server crashed, timeout) it
returns None so the caller falls back to tree-sitter typed
resolve (FR-B07).

12 unit tests covering:
  - per-language manifest detection (12 languages)
  - nested .git repos (closest manifest wins)
  - single .git monorepo
  - fallback when no marker
  - per-(language, workspace) cache
  - config roundtrip
  - file:// URI percent-encoding

* feat(cbm-b1): LSP MCP/CLI wiring + typed_resolve aliases + e2e tests

Wires the LSP bridge into MCP and CLI:
  - resolve_with_lsp MCP tool with language / file_path / line /
    character / request params; returns {found, locations[]} or
    {found: false, reason} when no server is configured.
  - leankg lsp-resolve CLI subcommand with --language / --file
    / --line / --character / --request flags.
  - typed_resolve_enabled now accepts common aliases (ts ->
    typescript, js -> javascript, py -> python, etc.) so the
    typed_resolve feature flag is forgiving for short forms.
  - main.rs declares mod lsp so the binary builds with the new
    module.

e2e tests run against the real leankg codebase as test data:
  - e2e_runs_against_leankg_codebase: walks the codebase,
    resolves workspace roots for every language file, confirms
    the bridge returns Ok(None) for unconfigured languages.
  - e2e_typed_resolve_flag_for_every_language_in_codebase:
    confirms typed_resolve=all enables every language the
    codebase ships.

Test count: 556 lib + 554 bin = 1110 passing.

* fix(lint): resolve clippy + fmt warnings across crates for CI gate

Pre-existing clippy lints (unused imports/vars, redundant clones,
needless borrows, len comparisons, vec-vs-array) blocked PR checks.
Concept + procedural ontologies are covered by a new e2e test suite
(16 cases) that verifies GIDs, element/rel-type roundtrips, aliases,
and full YAML loading.

* docs(status): add 2026-07-14 PRD integration status snapshot

* perf: bound heavy tools + LSH-based clones + LSP catalog for all languages

- budget.rs: process-wide BudgetGuard with wall-clock, RSS, iteration caps.
  Defaults: 60s, 4GB RSS, 1M iters. Disable via LEANKG_TOOL_BUDGET_OFF=1.
- minhash.rs: MinHash + LSH bands. Catches near-duplicate pairs in O(n)
  instead of O(n²). Defaults: 128 perms, 32 bands × 4 rows.
- find_clones: max_functions cap (default 50k), same-file default scoping,
  streaming file reads, LSH path for --cross-file, budget guard. Same
  scan that previously held ~944 MB RSS / ran for hours now finishes in
  seconds and stays well under the RSS cap.
- export_snapshot: streams JSON to disk via BufWriter + per-element
  serialization instead of building a 470 MB string in RAM.
- impact: --max-affected cap (default 10k) + budget guard. Reports
  truncated=true so callers know to re-scope.
- check-consistency: --limit flag (default 50).
- lsp/registry.rs: 44-language catalog covering Go/TS/JS/Python/Rust/
  Java/Kotlin/C/C++/C#/Zig/Crystal/Scala/Clojure/Vue/Svelte/HTML/CSS/
  JSON/YAML/XML/Ruby/PHP/Lua/Bash/PowerShell/Haskell/Elm/OCaml/F#/
  Elixir/Erlang/SQL/R/Swift/Dart/Markdown/TOML/GraphQL/Terraform/
  Dockerfile/Protobuf/Solidity + auto-install hints (npm/pip/cargo/
  brew/go/gem/opam/dotnet). Auto-detect language from file extension.
- leankg lsp-install <lang|all> [--dry-run]: runs the best install
  method for the host.
- leankg lsp-list: prints every known server + on-path check.
- run_lsp_resolve: --language now optional; auto-detected from path.
  Falls back with a helpful hint pointing at lsp-install.

* perf: stream heavy callers + add MemoryGuard for daemons

Streaming API: graph::GraphEngine::for_each_element,
for_each_relationship, for_each_element_of_type. Yields one element
at a time so heavy tools no longer materialize the 627k-element Vec.

find_clones: streams via for_each_element_of_type, drops master
targets as soon as per-file buckets are built.

export_graph: when format==json and no --file scope, dispatches
to export_json_streaming which writes BufWriter one element at
a time. Other formats keep the legacy path.

src/gc.rs (new): MemoryGuard for long-running daemons. Polls RSS
every 10s, runs a release callback on idle (default 60s) and
force-runs on RSS over cap (default 4 GB). Trim_heap() releases
unused pages to the OS via malloc_trim(0) on Linux.

MCP handler: every execute_tool call touches the GC guard.

Measured: export 5.43 GB -> 2.50 GB peak (-54%).

* perf: stream heavy callers + add MemoryGuard for daemons

Streaming API: graph::GraphEngine::for_each_element,
for_each_relationship, for_each_element_of_type. Yields one element
at a time so heavy tools no longer materialize the 627k-element Vec.

find_clones: streams via for_each_element_of_type, drops master
targets as soon as per-file buckets are built.

export_graph: when format==json and no --file scope, dispatches
to export_json_streaming which writes BufWriter one element at
a time. Other formats keep the legacy path.

src/gc.rs (new): MemoryGuard for long-running daemons. Polls RSS
every 10s, runs a release callback on idle (default 60s) and
force-runs on RSS over cap (default 4 GB). Trim_heap() releases
unused pages to the OS via malloc_trim(0) on Linux.

MCP handler: every execute_tool call touches the GC guard.

Measured: export 5.43 GB -> 2.50 GB peak (-54%).

* perf: stream heavy callers + add MemoryGuard for daemons

Streaming API: graph::GraphEngine::for_each_element,
for_each_relationship, for_each_element_of_type. Yields one element
at a time so heavy tools no longer materialize the 627k-element Vec.

find_clones: streams via for_each_element_of_type, drops master
targets as soon as per-file buckets are built.

export_graph: when format==json and no --file scope, dispatches
to export_json_streaming which writes BufWriter one element at
a time. Other formats keep the legacy path.

src/gc.rs (new): MemoryGuard for long-running daemons. Polls RSS
every 10s, runs a release callback on idle (default 60s) and
force-runs on RSS over cap (default 4 GB). Trim_heap() releases
unused pages to the OS via malloc_trim(0) on Linux.

MCP handler: every execute_tool call touches the GC guard.

Measured: export 5.43 GB -> 2.50 GB peak (-54%).

* perf: stream heavy callers + add MemoryGuard for daemons

Streaming API: graph::GraphEngine::for_each_element,
for_each_relationship, for_each_element_of_type. Yields one element
at a time so heavy tools no longer materialize the 627k-element Vec.

find_clones: streams via for_each_element_of_type, drops master
targets as soon as per-file buckets are built.

export_graph: when format==json and no --file scope, dispatches
to export_json_streaming which writes BufWriter one element at
a time. Other formats keep the legacy path.

src/gc.rs (new): MemoryGuard for long-running daemons. Polls RSS
every 10s, runs a release callback on idle (default 60s) and
force-runs on RSS over cap (default 4 GB). Trim_heap() releases
unused pages to the OS via malloc_trim(0) on Linux.

MCP handler: every execute_tool call touches the GC guard.

Measured: export 5.43 GB -> 2.50 GB peak (-54%).

* perf: stream heavy callers + add MemoryGuard for daemons

Streaming API: graph::GraphEngine::for_each_element,
for_each_relationship, for_each_element_of_type. Yields one element
at a time so heavy tools no longer materialize the 627k-element Vec.

find_clones: streams via for_each_element_of_type, drops master
targets as soon as per-file buckets are built.

export_graph: when format==json and no --file scope, dispatches
to export_json_streaming which writes BufWriter one element at
a time. Other formats keep the legacy path.

src/gc.rs (new): MemoryGuard for long-running daemons. Polls RSS
every 10s, runs a release callback on idle (default 60s) and
force-runs on RSS over cap (default 4 GB). Trim_heap() releases
unused pages to the OS via malloc_trim(0) on Linux.

MCP handler: every execute_tool call touches the GC guard.

Measured: export 5.43 GB -> 2.50 GB peak (-54%).

* perf: stream heavy callers + add MemoryGuard for daemons

Streaming API: graph::GraphEngine::for_each_element,
for_each_relationship, for_each_element_of_type. Yields one element
at a time so heavy tools no longer materialize the 627k-element Vec.

find_clones: streams via for_each_element_of_type, drops master
targets as soon as per-file buckets are built.

export_graph: when format==json and no --file scope, dispatches
to export_json_streaming which writes BufWriter one element at
a time. Other formats keep the legacy path.

src/gc.rs (new): MemoryGuard for long-running daemons. Polls RSS
every 10s, runs a release callback on idle (default 60s) and
force-runs on RSS over cap (default 4 GB). Trim_heap() releases
unused pages to the OS via malloc_trim(0) on Linux.

MCP handler: every execute_tool call touches the GC guard.

Measured: export 5.43 GB -> 2.50 GB peak (-54%).

* perf: stream heavy callers + add MemoryGuard for daemons

Streaming API: graph::GraphEngine::for_each_element,
for_each_relationship, for_each_element_of_type. Yields one element
at a time so heavy tools no longer materialize the 627k-element Vec.

find_clones: streams via for_each_element_of_type, drops master
targets as soon as per-file buckets are built.

export_graph: when format==json and no --file scope, dispatches
to export_json_streaming which writes BufWriter one element at
a time. Other formats keep the legacy path.

src/gc.rs (new): MemoryGuard for long-running daemons. Polls RSS
every 10s, runs a release callback on idle (default 60s) and
force-runs on RSS over cap (default 4 GB). Trim_heap() releases
unused pages to the OS via malloc_trim(0) on Linux.

MCP handler: every execute_tool call touches the GC guard.

Measured: export 5.43 GB -> 2.50 GB peak (-54%).

* feat(hnsw): drop LSH; expand Cozo HNSW + recall smoke

    Close FR-HNSW-A..F and FR-BENCH-HNSW: remove custom MinHash/LSH, ship
    embeddings OOTB in Docker, route semantic_search through Cozo HNSW, expose
    mega-graph knobs, and add deterministic recall@k golden assertions.

* update document prd
* chore(release): bump version to 0.18.0

* chore(ci): remove redundant leankg-graph workflow

The 'LeanKG Auto-Graph' workflow (leankg-graph.yml) ran on every push
to main and indexed the entire repo, uploading .leankg/ as an artifact
and offering an opt-in commit back to the repo. After auditing:

  - Zero other workflows download the 'leankg-graph' or 'graph-report'
    artifacts (no 'download-artifact' steps exist in .github/).
  - No job references this workflow via 'needs:'.
  - No Docker or deploy workflows consume the artifacts.
  - Workflow history shows only 2 runs ever, both failing.
  - Production indexing is handled by leankg-update.yml (pushes to
    LEANKG_HOST on push to main / on release).
  - The opt-in 'commit_graph_cache' step was never enabled.

The .leankg/ cache was never consumed by any CI job, making the entire
workflow redundant. Removing it eliminates the failing CI run, avoids
the 5-10 min cargo build on every push, and removes the overlap with
leankg-update.yml.

The npm/leankg/ wrapper is kept for now (out of scope here); it was
never published to the npm registry and has no current CI consumer.
* chore(release): bump version to 0.18.0

* fix(embed): resolve HNSW path mismatch between indexer and embed CLI

central_project_storage_path only recognized '<project>/.leankg' as the
project root, so the embed / semantic-context / smoke-test CLI commands
(which pass '<project>/.leankg/leankg.db') opened a fresh empty RocksDB
at a different project hash, found zero code_elements, and silently
returned 'Embedded: 0'. MCP semantic_search then fell back to the
ontology-only path instead of the CozoDB HNSW index.

Add a project_root_from_db_path helper that handles both directory and
file input, plus tests that pin the contract and document the bug in
docs/implementation/embed-db-path-mismatch-2026-07-15.md.

* feat(embed): decouple MCP from embed via in-process background thread

Implements Plan §B Option 3: an in-process embed worker that shares
the MCP's CozoDb handle, so MCP stays healthy while HNSW catches up.

CLI (already in CLI; defaults tightened):
- --types filter (default function,method for >50k mega-graphs)
- --wait / --status / --cancel subcommands
- --workers default 4, --batch-size default 64 (was 32)

Code changes:
- src/embeddings/build.rs: OMP_NUM_THREADS=1 cap, default batch 64,
  run-time LEANKG_EMBED_UPSERT_CHUNK override, new spawn_background_embed
  that holds an Arc<CozoDb> clone and runs build_index_parallel in a
  detached thread with a 5s progress poller.
- src/embeddings/state.rs: drop_hnsw_index / create_hnsw_index (FR-HNSW).
- src/main.rs: --types passed through to background child, single
  all_elements() scan in run_embed_worker.
- src/mcp/server.rs: LEANKG_EMBED_BACKGROUND=1 spawns the in-process
  worker after bind; tunable via LEANKG_EMBED_BACKGROUND_{WORKERS,BATCH,TYPES,FULL}.
- src/cli/mod.rs: bumped default --batch-size 32 -> 64.

Docker / compose:
- Dockerfile.rocksdb: LEANKG_EMBED_ON_BOOT=0 + LEANKG_EMBED_BACKGROUND=1
  baked in (no operator env file required).
- docker-compose.rocksdb.yml: same defaults; mem_limit comment for cold runs.
- docker-compose.embed.yml: NEW profile=embed one-shot offline rebuild
  (Plan §B Option 1) for ops who want to force a full rebuild while
  MCP is stopped.
- entrypoint.sh: LEANKG_EMBED_ON_BOOT defaults to 0; legacy foreground
  embed path gated behind LEANKG_EMBED_ON_BOOT=1 with --wait.

Measured on M2 Pro 10c, /Users/linh.doan/work/be (371k functions):
- MCP healthy ~60s after docker compose up.
- Embed throughput ~85 vec/sec (CozoDB writer commit ceiling).
- ETA cold functions-only: ~73 min -- Plan's <5 min target not hit due
  to per-batch Cozo commit overhead. See plan doc for follow-ups.
- Live 'embedded' count via DB query has RocksDB MVCC issues; final
  status is authoritative. AtomicUsize callback into build_index_parallel
  is the fix (tracked as follow-up).

See generated_docs/embed_bg_job_and_runtime_plan_2026-07-15.md for
the full measured-results table and follow-up list.

* perf(embed): import_relations + DirectEmbedder to break writer ceiling

Two compounding changes that together lift cold embed throughput on
the /Users/linh/doan/work/be mega-graph (~371k functions) from
~85 vec/sec to ~170 vec/sec sustained (2x). ETA cold functions-only
is now ~36 min on M2 Pro 10c, vs the original 73 min.

1. CozoDB writer: import_relations() instead of :put script
   The Datalog script path was repacking 5000 rows × 384 floats
   into JSON params, then parsing the script + committing per
   :put. import_relations() (cozo 0.7.6's public batch insert API)
   skips script parsing and goes straight to the relation lock +
   transaction commit + raw store_tx.put. Measured writer
   throughput went from ~50 rows/sec to ~170 rows/sec in the
   steady state (writer throughput is no longer the bottleneck).

2. DirectEmbedder: bypass fastembed's hardcoded intra_threads
   fastembed 4.9.1 hardcodes intra_threads = available_parallelism()
   at session creation (text_embedding/impl.rs:52, 80), so every
   worker session pre-allocates 10 threads on a 10c host. With N
   worker sessions the OS sees 10N contended threads. DirectEmbedder
   constructs its own ort::Session with with_intra_threads(N)
   settable per worker. Default intra_threads=1, tunable via
   LEANKG_EMBED_DIRECT_INTRA. The DirectEmbedder falls back to
   fastembed's Embedder if the model cache is missing (e.g. before
   ).

Why the runtime ceiling is ~170 vec/sec on this hardware:
- BGE-small ONNX inference at intra_threads=1 takes ~13s per
  batch=128 → ~10 vec/sec per worker call.
- 4 workers × ~50 vec/sec each (with rayon batching) = ~170 total.
- CozoDB writer drains at ~170 rows/sec sustained.
- Both pipelines are matched at this rate, so neither can scale
  further without changing the model or the storage layer.

Future levers (tracked as follow-ups in the plan doc):
- Direct RocksDB write via cozorocks' raw_put with WAL disabled
  (would skip CozoDB's transaction commit overhead).
- Smaller/faster embedding model (e.g. AllMiniLML6V2).
- Sharded Cozo databases merged post-build.

* perf(embed): INT8 fast path, memory budget, and Xenova quantized model

Enable LEANKG_EMBED_FAST (INT8 + seq=128 + data-parallel workers), soft
RSS caps via LEANKG_EMBED_MAX_MB, and Xenova model_quantized.onnx for
bge-q so cold mega-graph embeds sustain ~500 vec/s without the broken
Qdrant optimized ONNX. Document PRD v3.6.3 and sanitize personal paths.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(embed): document INT8 fast path and measured cold rates

Update README with fast-path env recipe, ~480–500 vec/s rates, and
troubleshooting for Qdrant optimized ONNX / memory clamps.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
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.
linhdmn and others added 25 commits August 10, 2026 09:32
* feat(split): sync query-only MCP + worker split from be fork on top of v0.23.0

Sync writer/reader split + Swift line_of unicode fix from be fork (1.0.14)
onto latest origin/main (v0.23.0). New leankg-mcp/leankg-worker bin targets,
EmbeddingProvider (local/openai/http), query-only MCP write-tool filtering,
swift line_of byte-offset fix, pg write_bus test literals.

Verified: cargo test --lib (968) + bin (975) + split integration tests green;
cargo fmt + cargo clippy --all -- -D warnings clean.

* fix(test): make readonly write-then-read test use real code_elements table

The integration test init_db_readonly_can_read_after_init_db_writes used a
virtual 'probe' relation with single-quote Cozo literals that the PG
translator can't parse as JSON. As an integration test (no #[cfg(test)]),
init_db returns the real PostgresBackend, so the fake 'probe' relation
never exists and the :put fails at translate time.

Seed the real code_elements table with double-quote JSON literals instead,
mirroring the other pg integration tests. Verified 8/8 readonly tests pass
against a live leankg-db (localhost:5433).
…aint (#232)

* fix(docker): ship leankg-worker + embeddings, pgvector HNSW ef_constraint

Docker image now builds and ships leankg-worker alongside leankg and
compiles with --features embeddings, so the writer (embed) runs as a
separate container in parallel with the query-only MCP reader.

build_hnsw_create_stmt now clamps ef_construction to >= 2*m. pgvector
rejects ef_construction < 2*m (SQLSTATE XX000) while CozoDB accepted any
pair; the embed pipeline completed but failed the post-embed HNSW index
rebuild with a bare 'db error'.

* fix(db): no-op record_metric on read-only MCP backend

The read-only MCP process issues a best-effort `:put context_metrics` after
every tool call. On a read-only PG session the insert is rejected, so every
query surfaced a bogus 'Failed to record metric: db error' to stderr.

Add DbBackend::is_read_only() (default false; PostgresBackend overrides from
its read_only flag) and skip the metric write when true.

* fix(update): surface missing release asset instead of UnexpectedEof

`leankg update` downloaded a 404/empty body when the release had no
binaries attached (e.g. a lightweight-tag release where release.yml never
fired), then failed with an opaque TarError/UnexpectedEof on the tar
extract. download_file now checks the HTTP status and rejects empty bodies
with a clear 'asset missing' message pointing at the Release workflow.

* fix(db): per-project PG schema scoping for ?project= + MCP request logging

?project=/app-be stopped scoping queries after the PG migration: init_db/
init_db_readonly ignored the db_path, so every project hit the single shared
public schema and returned the same data.

- PostgresBackend gains a schema pin: schema_for_path derives a stable key
  from leankg.yaml project_path (host path, identical from every mount),
  normalized so reader (<root>/.leankg) and writer (<root>/.leankg/leankg.db)
  agree. inject_search_path appends options=-csearch_path=<schema>,public.
- init_db (writer) always creates+migrates+pins its per-project schema;
  init_db_readonly (reader) pins when the schema is populated, else falls
  back to public so existing shared-layout indexes stay visible.
- leankg index <path> now uses the positional path for the project root
  instead of cwd, so a specific project indexes into its own schema.
- MCP HTTP logs each request's ?project= + method, and execute_tool logs
  tool + project per call.

Verified live (local PG): ?project=/app-be returns be data from its schema,
?project=/app returns freepeak data. Adds unit tests for reader/writer
schema agreement and search_path injection.
…ections (#233)

* feat(embed): multi-model embedding registry with table-per-model collections

Adds an embedding model registry (src/embeddings/registry.rs) so the active
model resolves from LEANKG_EMBED_ACTIVE_MODEL (default BGE 384-d) and each
model owns its own vector/state tables + HNSW index. Switching provider
(local ONNX <-> OpenAI-compatible API) never wipes another model's collection.

- registry.rs: built-in entries (bge-small-en-v1.5-384 local,
  qwen3-emb-4b-2560 openai, jina-embeddings-v3-1024 openai),
  table-per-model naming, legacy tables kept for default BGE
- provider.rs: LocalOnnxProvider::new(expected_dim),
  create_provider_from_env_with_dim, registry-dim validation
- state.rs: ensure_model_collections creates active model's state/vector
  relations + HNSW (dim from registry); drop/create_hnsw_index target
  active collection
- pg: migration 002 (embedding_models/embedding_active + per-model tables),
  translate.rs generic ANN table extraction + explicit-table :put/:rm +
  per-model ::hnsw DDL, backend.rs vector->vec + PK for embedding_*_ tables
- retrieval/pipeline.rs: ANN query targets ~<active>:vec_idx
- fake.rs: => target parsing + per-model schemas

Tests: tests/multi_model_embed_tests.rs (11, 2 models + mock TCP API +
A->B->A switch); tests/multi_model_smoke_live.rs (live against local PG
docker, mock OpenAI server; row counts preserved across switch). Docs:
docs/embed-multi-model.md, docs/embed-model-switch-smoke.md, EMBEDDINGS.md.

Lib: 1127 passed / 2 failed (pre-existing FakeBackend hnsw/vec limits).

* feat(embed): add Google Gemini OpenAI-compatible embeddings

- Fix OpenAiCompatibleProvider URL: base URL is now the versioned root and
  '/embeddings' is appended, so Google's compat layer
  (https://generativelanguage.googleapis.com/v1beta/openai) works without a
  '/v1' segment. Backward compatible with '.../v1' and full '/embeddings'
  bases.
- Register gemini-embedding-2-3072 and gemini-embedding-001-3072 (Google's
  default output dimensionality is 3072) with table-per-model collections.
- Add PG migration 003_gemini_embed for the two 3072-d collections.
- Unit tests for URL normalization + Gemini registry entries; live integration
  test (tests/gemini_live_test.rs) hitting the real endpoint, gated on
  GOOGLE_EMBEDING.

* test(embed): feature-gate gemini live test like other embed integration tests

* feat(embed): add LEANKG_EMBED_WRITE_VECTORS + --no-vectors to control Postgres vector writes

BuildOptions.write_vectors (env LEANKG_EMBED_WRITE_VECTORS, default on) plus
the CLI embed --no-vectors flag gate every PG vector-write sink: upsert
(import_relations / :put), embedding_state upsert_fresh, HNSW drop/rebuild,
and orphan reaping. Inference still runs for benchmark/smoke; the vector
store is left untouched. Threads through serial + parallel build paths and
the MCP background-embed config.

* feat(auth): OAuth2 access-token auth for protected DB resources

- accounts/orgs/org_memberships/access_tokens/resource_ownership tables (004_auth)
- opaque SHA-256 access tokens: issue, validate, revoke, list (AccessTokenStore)
- account register + bootstrap org, org roles (owner>admin>member>viewer), Argon2
- CLI: auth register/token/list-tokens/revoke
- REST: /api/v1/auth/* handlers (register, login, token, org, members, resource/claim)
- MCP HTTP: DB Bearer token validated before static token; token_store resolved
  from the path-cache engine so auth works on already-initialized projects

fix(translate): composite PKs (org_id+account_id, team_id+account_id) quoted per
column; auth epoch cols typed bigint; api_keys NULL bindings honor text columns;
access_tokens scopes treated as JSONB

fix(migrations): 003 gemini 3072-d HNSW index guarded (pgvector<0.9 2000-d cap),
matching 002; 004 tables get real PKs so :put ON CONFLICT upserts

fix(fake): :put dedups on PK (composite-aware) mirroring PG upsert semantics

test: 24 auth unit tests; pg_translate parity suite green (11); live PG schema +
bulk-copy + vector suites pass on leankg-pg-phase0 :5433

* fix(gc): gate malloc_trim behind linux-gnu

musl (alpine) has no malloc_trim; calling it links a stub that may
panic. Restrict the heap trim to linux-gnu builds.

* fix(pg): ef_construction is a CREATE INDEX param, not a runtime GUC

hnsw.ef_construction is a CREATE INDEX ... WITH (ef_construction=...) on
pgvector 0.8.x, not a SET LOCAL configuration parameter. Emitting it as a
GUC aborts the write tx with 'invalid configuration parameter name'.
embedding_gucs_for now always returns empty; the DDL-time value still
feeds build_hnsw_create_stmt (state.rs).

* feat(embed): always-live HNSW unless LEANKG_EMBED_COPY

pgvector maintains the HNSW index on INSERT, so a dropped-index +
CREATE INDEX cycle needs table ownership that DML-only roles lack.
should_use_incremental_hnsw_puts now returns true for any dirty>0 unless
LEANKG_EMBED_COPY forces the COPY bulk path. Drop the
(vectors/20).max(1000) threshold + LEANKG_EMBED_BULK_REINDEX_THRESHOLD.

* feat(pg): VALUES-upsert bulk path + SQL logging + per-project advisory lock

- import_relations routes per-model vector tables through upsert_values
  (INSERT ... VALUES ... ON CONFLICT DO UPDATE), skipping COPY FROM STDIN
  which deadlocks through the pgcat pooler (COPY reads as idle and the
  pooler's idle-timeout kills the socket mid-CopyIn). use_copy_path is
  exact-match on embedding_vectors only; embedding_vectors_* per-model
  and embedding_state* fall through to upsert_values.
- upsert_values: PK last-wins dedupe (no E21000), max_batch clamp, and
  $N::text::vector casts for vector columns.
- SQL logging: LEANKG_PG_SQL_LOG gate + log_pg_run_script / log_pg_import
  emitting translate phase, SQL, named params, rows, elapsed.
- index_advisory_lock now takes (env, path) — FNV-1a over env+salt+path so
  same-project indexes serialize, different projects run concurrently.
- test_pg_available probe so live-PG unit tests skip when PG is down.

* refactor(cli): use per-project index_advisory_lock(env, path)

Wire both index call sites (full + incremental) through the new
(env, path)-scoped advisory lock so same-project jobs serialize and
different projects run concurrently.

* feat(setup): monorepo workspace discovery + resolved leankg bin

- LEANKG_WORKSPACE_DIR scans a workspace for nested git repos (depth
  from LEANKG_WORKSPACE_MAX_DEPTH, default 3) and adds each as a
  mounted RepoSpec (url empty, no clone). Precedence: workspace →
  LEANKG_PROJECT_DIRS → LEANKG_REPOS.
- run_leankg_sub spawns the resolved leankg binary instead of a bare
  'leankg' command name, so the setup subprocess finds itself across
  renamed/installed distributions.

* feat(cli): resolve leankg-internal/leankg sibling binary for exec

Fall back across [leankg-internal, leankg] (and .exe variants) next to
the current executable, skipping a candidate equal to current_exe so
cargo-run doesn't recurse. Strict superset of the old single-name check.

* feat(indexer): LEANKG_PATH_REWRITE storage path mapping

Env LEANKG_PATH_REWRITE=FROM=TO rewrites the leading path prefix stored
in code_elements.file_path and physical-structure parent links, so an
index built under one mount root stays addressable under another. Applied
at the storage choke points (element extraction, index_file_sync, doc
sections, physical structure, rationale markers); on-disk reads/deletes
keep the real path. Doc qualified_names stay docs/... per be behavior.

* feat(pg): migration 005 drops qwen 2560-d HNSW + usearch_key DEFAULT

pgvector <0.9 caps vector dims at 2000, so the qwen3_emb_4b 2560-d HNSW
index can never build — dead weight on fresh DBs (002 now omits it) and
an applied-DB orphan. 005 is the idempotent upgrade path for already-
migrated databases: DROP INDEX IF EXISTS + ALTER COLUMN usearch_key
DROP DEFAULT (was DEFAULT 0, misleading for real usearch keys).

* feat(semantic): relevance guards — path-prefix corpus gate + confidence floor

Port be-knowledge-graph semantic-relevance-guards (a0a98279):
- path_prefix arg on semantic_search: corpus-scope gate hard-drops hits
  whose file_path lacks the requested prefix (monorepo disambiguation).
- SEMANTIC_MIN_RERANK=0.10 confidence floor: low-confidence reranks are
  flagged, not returned as authoritative.
- LEX_NO_CONFIRM_FACTOR=0.5 token-overlap: query/result token overlap
  scoring + direct_adjusted ranking; token_overlap in result JSON.
- corpus_suspicious flag on mcp_status inventory (files < 2500).
- runtime: LEANKG_EMBED_FAST now defaults OFF (quality/FP32), explicit
  fast path trims seq to 128; extract_first_comment in code blobs.

* test(embed): gate live-PG tests on test_pg_available probe

The 4 live-PG tests (mark_stale dedupe/E21000 + 2 HNSW queryability) used
FakeBackend via init_db or the broken from_env() guard that never skips.
Switch them to init_db_pg() behind test_pg_available() so plain cargo test
passes with no database running; the real E21000/HNSW paths still run when
leankg-pg-phase0 is up. build.rs RSS-cap test follows the LEANKG_EMBED_FAST
default flip (quality on by default).

* chore(embed): default embed profile = quality 512, fast is opt-in

Deploy defaults were pinned to the fast INT8 profile (LEANKG_EMBED_FAST=1,
bge-q, MAX_SEQ=128, MAX_BLOB_CHARS=500), so embedded vectors were 128-token
even though the runtime default is the full 512-token quality window. Remove
the pins so the default embed is FP32 / 512-token; the fast INT8/128 profile
stays available via explicit LEANKG_EMBED_FAST=1 + LEANKG_EMBED_MAX_SEQ=128
in perf/smoke scripts and run examples.

* fix(test): serialize LEANKG_IMPACT_MAX_AFFECTED env tests

impact_scan_options_default_caps_at_10k and impact_scan_options_respects_
env_override both read/mutate LEANKG_IMPACT_MAX_AFFECTED and ran in
parallel, so the default test intermittently read the other's leftover '5'
instead of 10_000 (CI failure: left 5, right 10000). Hold a module env lock
in both + explicitly clear the var in the default test.

* test(embed): lock env in embed_max_rss_mb tests

Both tests mutate LEANKG_EMBED_MAX_MB / LEANKG_EMBED_FAST without holding
the module env_lock, racing with the other locked tests in the same binary
(same flaky-env class as the traversal CI failure).

* fix(mcp): raise default tool concurrency to 100

Raise MCP tool semaphore default from num_cpus-1 to 100 permits so parallel
Cursor tool batches are less likely to hit -32603 concurrency-limit errors.
Wait up to 30s for a permit (queue instead of immediate reject). Keep
LEANKG_MCP_TOOL_CONCURRENCY env override. Update unit test to assert default
permits == 100.
Co-authored-by: leankg-release[bot] <noreply@github.com>
* feat(embeddings): add GraphRAG-style summary-primary embedding

Introduce `file_summary` module to synthesize per-file and per-module summary nodes with `contains` bridge edges. Add `--summary-primary` and `--summary-primary-cap` CLI flags to skip per-function embeddings for large files, reducing inference time on large codebases by relying on the file-summary node.

* feat(embeddings): add summary-only mode and module-to-file traversal edges

- Introduce `--summary-only` CLI flag and `BuildOptions::summary_only_enabled` to restrict vector generation strictly to file and module summary nodes.
- Synthesize `contains` bridge edges from module-summary nodes down to their member file-summary nodes so retrieval traversal no longer hits dead ends.
- Update PRD documentation and task tracker with the `FR-EMBED-SUMMARY` and `FR-EMBED-SUMMARY-ONLY` requirements.

* feat(embeddings): offsite dry-run + import pipeline

Add an isolated/offsite embedding workflow so the expensive inference step
can run on a separate host (e.g. a Colab T4 GPU):

  leankg embed --dry-run            -> .leankg/embed_export.jsonl
  python scripts/embed_batch.py ... -> import.jsonl
  leankg embed --import <file>      -> vectors in DB, state fresh

New module `src/embeddings/offsite.rs`:
- export_work_items: runs the same work-list collection as a real embed run
  (same Incremental->Full self-heal), writes NDJSON (meta line + one row per
  query). Non-mutating.
- import_vectors: upserts vectors via the exact upsert_pairs_to_db +
  state::upsert_fresh pair the live writer uses, so resume is identical.
  Guards on vec_dim mismatch, skips rows already fresh (resume), and by
  default rebuilds content_hash from the live graph to skip drifted/orphaned
  rows (--no-verify trusts the file).

NDJSON mapping contract: qualified_name is the authoritative join key (PK of
both embedding_vectors and embedding_state); content_hash is echoed unchanged
so embedding_state stamps correctly; `i` is a 0-based gap-detection index.

CLI (embed subcommand): --dry-run, --export-file, --import, --no-verify.
Dispatch branches before ONNX load / background spawn. BuildOptions assembly
factored into build_embed_options, shared by the real worker and the dry-run
path so the export faithfully represents the next real run.

scripts/embed_batch.py: sentence-transformers batch embedder. One input row
-> one output row, keyed by qualified_name; asserts gap-free input `i`;
resumes via --checkpoint; CUDA-aware.

Tests (offsite.rs, against FakeBackend): export gap-free + non-mutating,
import writes + stamps fresh, resume idempotency, drift/orphan skip,
vec_dim refusal, serde round-trips.

Supporting fixes:
- build.rs: expose collectors/WorkItem fields/upsert_pairs_to_db as
  pub(crate) so the offsite module can drive the existing engine.
- db/fake.rs: handle Cozo `vec([...])` vector literals in the write-source
  parser and treat `::hnsw create/drop` as no-ops, unblocking the embed
  write path in unit tests.
- retrieval/ontology_traversal.rs: fix stale `db::schema::init_db` import
  (moved to `db::backend`) that prevented the lib test suite from compiling.

Also rides on in-progress profile/summary embedding work (profile.rs +
provider/state/text_blob/indexer enhancements) already in the working tree.

* fix(embeddings): restore registry dim + feature-gate for no-feature build

* feat(db): rustls TLS for remote managed Postgres via LEANKG_PG_CA_CERT

pg_connect() picks NoTls for local dev PG (:5433) or a rustls connector
rooted at LEANKG_PG_CA_CERT for remote managed Postgres (Aiven/Neon).
Installs the ring crypto provider (rustls 0.23 requirement). Enables
direct (no-Docker) runs against Aiven PG from .env.

* fix(db): inject_search_path kept query params — remote TLS URLs broke

split_once('?') drops the '?', so the old code merged db name with the
query ('defaultdb&sslmode=requireoptions=...'), failing every remote URL
carrying a query string (e.g. ?sslmode=require). Re-add '?' before the
existing params and join the new options= with '&'.

* feat(db): support sslmode=verify-full/verify-ca for TLS PG URLs

tokio-postgres 0.7 only understands disable|prefer|require and rejects
unknown sslmode values at Config::parse time, so a managed-PG URL like
Rivestack's sslmode=verify-full failed before pg_connect could act.

Normalize the URL string before parsing (verify-* -> require), then:
- LEANKG_PG_CA_CERT set  -> root at that CA (Aiven private CA, unchanged)
- verify-* without a CA   -> root at the Mozilla store via webpki-roots
- otherwise               -> NoTls (local dev :5433, unchanged)

Chain + hostname verification still comes from the rustls root store +
server name, which is exactly what verify-full/verify-ca mean.

Verified live: embed --dry-run against rivesca.eu.db.rivestack.io over TLS.
+2 tests (url_wants_verified_tls_folds_verify_modes,
normalize_pg_url_for_parse_folds_verify_to_require).

---------

Co-authored-by: linhdmn <mnhatlinh.doan@gmail.com>
Co-authored-by: leankg-release[bot] <noreply@github.com>
…y (D4) (#239)

leankg index writes into the project's Postgres schema and has no embedded
fallback since v0.20 (D4), so the bare ubuntu-latest runner fails with
'connection refused'. Restore the pgvector service container that ci.yml
used before c3f28e0f, which fixes the red update-graph job that has been
failing on every main push since the PostgreSQL migration.
Co-authored-by: leankg-release[bot] <noreply@github.com>
Remove tools that overlap with more general replacements:
- query_file → search_code (same safe_discover path on mega-graphs)
- find_function → search_code with element_type=function
- get_callers → get_call_graph with depth=1
- search_annotations → manual filter on full graph load
- get_cluster_context → get_cluster_skill (same detect+load+filter)
- kg_concept_map → kg_context (same ontology query engine)
- get_graph_schema → low-value meta tool (get_architecture covers it)
- find_dead_code → low-value analysis tool
- session_recall → session-specific, rarely used
- kg_self_test → internal test tool
- mcp_embed → thin wrapper (call mcp_index + embed_control sequentially)

TDD: test_redundant_tools_removed verifies exact 80-tool count.

Also fixes Postgres schema isolation in create_schema_if_missing_sync
(vector extension search_path) and cleans dead code (hot_cache,
glob_match, glob import).
…e) (#242)

* docs: add 2027 roadmap, enterprise PRD, and central progress tracker

* refactor: purge CozoDB remnants from docs/ontology/tests and remove orphaned traceability code
Co-authored-by: leankg-release[bot] <noreply@github.com>
…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)
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.28.6 to 4.28.8.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](browserslist/browserslist@4.28.6...4.28.8)

---
updated-dependencies:
- dependency-name: browserslist
  dependency-version: 4.28.8
  dependency-type: indirect
...

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 4, 2026
@linhdmn
linhdmn force-pushed the dependabot/npm_and_yarn/ui-v2/browserslist-4.28.8 branch from 7cad348 to d6f77ce Compare September 14, 2026 05:06
@linhdmn
linhdmn force-pushed the main branch 2 times, most recently from 7f57e8f to 4ad0f18 Compare September 14, 2026 10:40
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