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
Open
dependabot[bot] wants to merge 529 commits into
dependabot[bot] wants to merge 529 commits into
Conversation
…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.
* 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>
…MA-01..06), FR-SM-04/05 re-grounded (#261)
* 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>
linhdmn
force-pushed
the
dependabot/npm_and_yarn/ui-v2/browserslist-4.28.8
branch
from
September 14, 2026 05:06
7cad348 to
d6f77ce
Compare
linhdmn
force-pushed
the
main
branch
2 times, most recently
from
September 14, 2026 10:40
7f57e8f to
4ad0f18
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps browserslist from 4.28.6 to 4.28.8.
Release notes
Sourced from browserslist's releases.
Changelog
Sourced from browserslist's changelog.
Commits
f2f2e6cRelease 4.28.8 versiond0787c8Update dependenciesfcf8fa9Merge pull request #939 from Jaybhade/fix/baseline-kaios-without-downstream57ecd64fix: support "including kaios" without downstream093a0f6Update EM bannerb637868Release 4.28.7 version313f465Update dependenciesc935c5aFix regexp performanced7e9e65Rewrite structure parsing to make it always fastec4a55eFix import orderDependabot 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 rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill 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 versionwill 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 dependencywill 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.