java-codebase-rag 0.5.3__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. {java_codebase_rag-0.5.3/java_codebase_rag.egg-info → java_codebase_rag-0.6.0}/PKG-INFO +5 -5
  2. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/README.md +2 -2
  3. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/ast_java.py +1 -1
  4. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/build_ast_graph.py +142 -90
  5. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/graph_enrich.py +3 -3
  6. java_codebase_rag-0.6.0/java_codebase_rag/_fdlimit.py +48 -0
  7. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag/cli.py +31 -28
  8. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag/config.py +28 -8
  9. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag/installer.py +99 -10
  10. java_codebase_rag-0.6.0/java_codebase_rag/lance_optimize.py +148 -0
  11. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag/pipeline.py +63 -9
  12. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0/java_codebase_rag.egg-info}/PKG-INFO +5 -5
  13. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag.egg-info/SOURCES.txt +6 -2
  14. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag.egg-info/requires.txt +1 -1
  15. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag.egg-info/top_level.txt +1 -1
  16. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_index_flow_lancedb.py +22 -4
  17. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_ontology.py +1 -1
  18. java_codebase_rag-0.5.3/kuzu_queries.py → java_codebase_rag-0.6.0/ladybug_queries.py +62 -56
  19. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/mcp_v2.py +16 -16
  20. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/pr_analysis.py +1 -1
  21. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/pyproject.toml +4 -4
  22. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/search_lancedb.py +8 -8
  23. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/server.py +47 -17
  24. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_assign_endpoint_client_extraction.py +6 -6
  25. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_ast_graph_build.py +68 -51
  26. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_bank_chat_brownfield_integration.py +16 -16
  27. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_brownfield_clients.py +12 -12
  28. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_brownfield_routes.py +14 -14
  29. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_call_edges_e2e.py +28 -28
  30. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_call_graph_receiver_resolution.py +11 -11
  31. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_call_graph_smoke_roundtrip.py +49 -49
  32. java_codebase_rag-0.6.0/tests/test_call_invariant.py +34 -0
  33. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_cli_quiet_parity.py +4 -4
  34. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_client_hint_recovery.py +7 -7
  35. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_client_node_extraction.py +6 -6
  36. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_client_role_rename.py +18 -18
  37. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_config.py +20 -4
  38. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_cross_service_resolution_flag.py +19 -19
  39. java_codebase_rag-0.6.0/tests/test_fd_limit.py +78 -0
  40. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_feign_not_exposer.py +14 -14
  41. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_incremental_graph.py +279 -78
  42. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_installer.py +91 -0
  43. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_java_codebase_rag_cli.py +29 -29
  44. java_codebase_rag-0.5.3/tests/test_kuzu_queries.py → java_codebase_rag-0.6.0/tests/test_ladybug_queries.py +103 -103
  45. java_codebase_rag-0.6.0/tests/test_lance_optimize.py +165 -0
  46. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_lancedb_e2e.py +23 -13
  47. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_mcp_hints.py +155 -155
  48. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_mcp_tools.py +17 -0
  49. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_mcp_v2.py +274 -274
  50. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_mcp_v2_compose.py +151 -151
  51. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_pr_analysis.py +24 -24
  52. java_codebase_rag-0.5.3/tests/test_call_invariant.py +0 -34
  53. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/LICENSE +0 -0
  54. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/brownfield_events.py +0 -0
  55. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/chunk_heuristics.py +0 -0
  56. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/index_common.py +0 -0
  57. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag/__init__.py +0 -0
  58. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag/cli_format.py +0 -0
  59. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag/cli_progress.py +0 -0
  60. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag/install_data/agents/explorer-rag-enhanced.md +0 -0
  61. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag/install_data/skills/explore-codebase/SKILL.md +0 -0
  62. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag.egg-info/dependency_links.txt +0 -0
  63. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_codebase_rag.egg-info/entry_points.txt +0 -0
  64. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/java_index_v1_common.py +0 -0
  65. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/mcp_hints.py +0 -0
  66. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/path_filtering.py +0 -0
  67. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/setup.cfg +0 -0
  68. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_agent_skills_static.py +0 -0
  69. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_ast_java_calls.py +0 -0
  70. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_ast_java_capabilities.py +0 -0
  71. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_brownfield_events.py +0 -0
  72. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_brownfield_overrides.py +0 -0
  73. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_call_edge_matching.py +0 -0
  74. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_cli_progress_stdout_invariant.py +0 -0
  75. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_edge_navigation_doc.py +0 -0
  76. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_graph_enrich.py +0 -0
  77. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_installer_integration.py +0 -0
  78. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_mcp_server_project_root.py +0 -0
  79. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_meta_chain_core.py +0 -0
  80. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_microservice_scope.py +0 -0
  81. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_outgoing_call_extraction.py +0 -0
  82. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_packaging_metadata.py +0 -0
  83. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_path_filtering.py +0 -0
  84. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_resolve_routes_messaging_layer_c.py +0 -0
  85. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_route_extraction.py +0 -0
  86. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_schema_consistency.py +0 -0
  87. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_search_lancedb.py +0 -0
  88. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_search_lancedb_capability.py +0 -0
  89. {java_codebase_rag-0.5.3 → java_codebase_rag-0.6.0}/tests/test_string_value_atoms.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: java-codebase-rag
3
- Version: 0.5.3
3
+ Version: 0.6.0
4
4
  Summary: MCP server for semantic + structural search over Java codebases
5
5
  Author: HumanBean17
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/HumanBean17/java-codebase-rag
8
8
  Project-URL: Repository, https://github.com/HumanBean17/java-codebase-rag
9
9
  Project-URL: Issues, https://github.com/HumanBean17/java-codebase-rag/issues
10
- Keywords: mcp,java,rag,code-search,graph,lancedb,kuzu
10
+ Keywords: mcp,java,rag,code-search,graph,lancedb,ladybug
11
11
  Classifier: Development Status :: 3 - Alpha
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Programming Language :: Python :: 3
@@ -19,7 +19,7 @@ Requires-Python: >=3.11
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
21
  Requires-Dist: cocoindex[lancedb]<2,>=1.0.0a43
22
- Requires-Dist: kuzu<0.12,>=0.11.3
22
+ Requires-Dist: ladybug<0.18,>=0.17.1
23
23
  Requires-Dist: lancedb<0.31,>=0.25.3
24
24
  Requires-Dist: mcp<2,>=1.27.0
25
25
  Requires-Dist: numpy<2.5,>=1.26.4
@@ -103,7 +103,7 @@ java-codebase-rag install
103
103
  java-codebase-rag install --non-interactive --agent claude-code
104
104
  ```
105
105
 
106
- After `pip install --upgrade java-codebase-rag`, run `java-codebase-rag update` to refresh shipped artifacts.
106
+ After `pip install --upgrade java-codebase-rag`, run `java-codebase-rag update` to refresh shipped artifacts and catch up the index (Lance + graph).
107
107
 
108
108
  ### Manual registration
109
109
 
@@ -235,7 +235,7 @@ Run `java-codebase-rag --help` to list grouped subcommands. Operator playbook wi
235
235
  | Group | Subcommand | What it does |
236
236
  |---|---|---|
237
237
  | Setup | `install` | Interactive setup wizard: config, MCP registration, skill/agent deployment, indexing. |
238
- | Setup | `update` | Refresh shipped artifacts (skill, agent, MCP entry) after pip upgrade. |
238
+ | Setup | `update` | Refresh shipped artifacts (skill, agent, MCP entry) + incremental Lance/graph catch-up after pip upgrade. |
239
239
  | Lifecycle | `init` | First-time index. Refuses if artifacts already exist. |
240
240
  | Lifecycle | `increment` | CocoIndex catch-up + incremental Kuzu update. `--vectors-only` for Lance only. |
241
241
  | Lifecycle | `reprocess` | Full Lance + Kuzu rebuild. `--vectors-only` / `--graph-only` for a single phase. |
@@ -63,7 +63,7 @@ java-codebase-rag install
63
63
  java-codebase-rag install --non-interactive --agent claude-code
64
64
  ```
65
65
 
66
- After `pip install --upgrade java-codebase-rag`, run `java-codebase-rag update` to refresh shipped artifacts.
66
+ After `pip install --upgrade java-codebase-rag`, run `java-codebase-rag update` to refresh shipped artifacts and catch up the index (Lance + graph).
67
67
 
68
68
  ### Manual registration
69
69
 
@@ -195,7 +195,7 @@ Run `java-codebase-rag --help` to list grouped subcommands. Operator playbook wi
195
195
  | Group | Subcommand | What it does |
196
196
  |---|---|---|
197
197
  | Setup | `install` | Interactive setup wizard: config, MCP registration, skill/agent deployment, indexing. |
198
- | Setup | `update` | Refresh shipped artifacts (skill, agent, MCP entry) after pip upgrade. |
198
+ | Setup | `update` | Refresh shipped artifacts (skill, agent, MCP entry) + incremental Lance/graph catch-up after pip upgrade. |
199
199
  | Lifecycle | `init` | First-time index. Refuses if artifacts already exist. |
200
200
  | Lifecycle | `increment` | CocoIndex catch-up + incremental Kuzu update. `--vectors-only` for Lance only. |
201
201
  | Lifecycle | `reprocess` | Full Lance + Kuzu rebuild. `--vectors-only` / `--graph-only` for a single phase. |
@@ -325,7 +325,7 @@ class RouteDecl:
325
325
  filename: str
326
326
  start_line: int
327
327
  end_line: int
328
- # brownfield / B2a composition (graph_enrich.resolve_routes_for_method); not a Kuzu column.
328
+ # brownfield / B2a composition (graph_enrich.resolve_routes_for_method); not a graph column.
329
329
  route_source_layer: str = "builtin"
330
330
 
331
331
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Four-pass AST-derived Knowledge Base builder (Kuzu).
2
+ """Four-pass AST-derived Knowledge Base builder (LadybugDB).
3
3
 
4
4
  Walks a Java source tree with `tree_sitter_java`, writes a deterministic graph of:
5
5
  Symbol nodes: package, file, class, interface, enum, record, annotation, method, constructor
@@ -13,14 +13,14 @@ Pass 3 resolves static call sites into confidence-scored CALLS edges and DECLARE
13
13
  Pass 4 emits Route rows plus Symbol→Route EXPOSES edges from literal annotation metadata.
14
14
 
15
15
  Usage:
16
- build_ast_graph.py --source-root <repo> [--kuzu-path <path>] [--verbose]
16
+ build_ast_graph.py --source-root <repo> [--ladybug-path <path>] [--verbose]
17
17
 
18
- Default Kuzu database path resolution order:
19
- --kuzu-path CLI arg (path passed to kuzu.Database(...))
20
- JAVA_CODEBASE_RAG_INDEX_DIR/code_graph.kuzu (if set and local)
21
- ./.java-codebase-rag/code_graph.kuzu under cwd
18
+ Default LadybugDB database path resolution order:
19
+ --ladybug-path CLI arg (path passed to ladybug.Database(...))
20
+ JAVA_CODEBASE_RAG_INDEX_DIR/code_graph.lbug (if set and local)
21
+ ./.java-codebase-rag/code_graph.lbug under cwd
22
22
 
23
- The Kuzu DB is dropped and rebuilt on every run (Phase 1 is a full rebuild).
23
+ The LadybugDB DB is dropped and rebuilt on every run (Phase 1 is a full rebuild).
24
24
  """
25
25
  from __future__ import annotations
26
26
 
@@ -37,7 +37,7 @@ from collections import defaultdict
37
37
  from dataclasses import asdict, dataclass, field, replace
38
38
  from pathlib import Path
39
39
 
40
- import kuzu
40
+ import ladybug
41
41
 
42
42
  from ast_java import (
43
43
  ONTOLOGY_VERSION,
@@ -76,7 +76,7 @@ _PASS3_START = "[graph] pass 3 · call resolution (outgoing calls per site)"
76
76
  _PASS4_START = "[graph] pass 4 · route and EXPOSES extraction"
77
77
  _PASS5_START = "[graph] pass 5 · imperative HTTP_CALLS / ASYNC_CALLS edges"
78
78
  _PASS6_START = "[graph] pass 6 · cross-service call-edge matching"
79
- _WRITE_START = "[graph] writing · Kuzu graph to disk"
79
+ _WRITE_START = "[graph] writing · LadybugDB graph to disk"
80
80
 
81
81
 
82
82
  def _verbose_stderr_line(content: str) -> None:
@@ -230,7 +230,7 @@ class RouteRow:
230
230
  start_line: int
231
231
  end_line: int
232
232
  resolved: bool
233
- # B2a brownfield composition (PR-A3); not persisted on Kuzu `Route` nodes.
233
+ # B2a brownfield composition (PR-A3); not persisted on LadybugDB `Route` nodes.
234
234
  source_layer: str = "builtin"
235
235
 
236
236
 
@@ -499,8 +499,8 @@ def _hash_file(abs_path: Path) -> str:
499
499
  # ---------- incremental rebuild helpers ----------
500
500
 
501
501
 
502
- def _load_existing_types(conn: kuzu.Connection, tables: GraphTables, exclude_files: set[str] | None = None) -> None:
503
- """Load type entries from existing Kuzu graph into tables for cross-file resolution.
502
+ def _load_existing_types(conn: ladybug.Connection, tables: GraphTables, exclude_files: set[str] | None = None) -> None:
503
+ """Load type entries from existing LadybugDB graph into tables for cross-file resolution.
504
504
 
505
505
  When exclude_files is provided, only load types from files NOT in the set.
506
506
  """
@@ -543,8 +543,8 @@ def _load_existing_types(conn: kuzu.Connection, tables: GraphTables, exclude_fil
543
543
  tables.by_package.setdefault(package, []).append(entry)
544
544
 
545
545
 
546
- def _load_existing_members(conn: kuzu.Connection, tables: GraphTables, exclude_files: set[str] | None = None) -> None:
547
- """Load member entries from existing Kuzu graph into tables.members.
546
+ def _load_existing_members(conn: ladybug.Connection, tables: GraphTables, exclude_files: set[str] | None = None) -> None:
547
+ """Load member entries from existing LadybugDB graph into tables.members.
548
548
 
549
549
  When exclude_files is provided, only load members from files NOT in the set.
550
550
  """
@@ -588,15 +588,25 @@ def _load_existing_members(conn: kuzu.Connection, tables: GraphTables, exclude_f
588
588
  ))
589
589
 
590
590
 
591
- def _find_dependents(conn: kuzu.Connection, changed_node_ids: set[str]) -> set[str]:
591
+ # Every Symbol->Symbol REL TABLE type in the graph schema. A Symbol node can
592
+ # only have an INCOMING edge of one of these types, so `_find_dependents` MUST
593
+ # walk all of them: that completeness is what makes the changed-node DETACH
594
+ # DELETE in `_delete_file_scope` Phase 3 safe (every real caller of a changed
595
+ # node is pulled into scope, so Phase 1 removes the edge before the node delete).
596
+ # If you add a new Symbol->Symbol edge type to the schema, add it here too —
597
+ # otherwise changed-node deletion would silently drop its surviving edges.
598
+ _SYMBOL_TO_SYMBOL_EDGE_TYPES = (
599
+ "EXTENDS", "IMPLEMENTS", "INJECTS", "CALLS", "DECLARES", "OVERRIDES",
600
+ )
601
+
602
+
603
+ def _find_dependents(conn: ladybug.Connection, changed_node_ids: set[str]) -> set[str]:
592
604
  """Find files whose nodes have edges pointing into changed nodes. Returns set of filenames."""
593
605
  dependent_files: set[str] = set()
594
606
 
595
- # Query each Symbol-to-Symbol edge table for incoming edges
596
- edge_types = ["EXTENDS", "IMPLEMENTS", "INJECTS", "CALLS", "DECLARES", "OVERRIDES"]
597
607
  params = {"changed_ids": list(changed_node_ids)}
598
608
 
599
- for edge_type in edge_types:
609
+ for edge_type in _SYMBOL_TO_SYMBOL_EDGE_TYPES:
600
610
  query = f"""
601
611
  MATCH (src:Symbol)-[e:{edge_type}]->(dst:Symbol)
602
612
  WHERE dst.id IN $changed_ids
@@ -612,23 +622,52 @@ def _find_dependents(conn: kuzu.Connection, changed_node_ids: set[str]) -> set[s
612
622
  return dependent_files
613
623
 
614
624
 
615
- def _delete_file_scope(conn: kuzu.Connection, filenames: set[str]) -> None:
616
- """Delete all nodes and edges originating from the given files.
617
-
618
- Skip phantom nodes (filename=""). Deletes ALL edge types in Phase 1,
619
- then nodes in subsequent phases. Route/Client/Producer nodes use
620
- DETACH DELETE as a safety net for any edges missed in Phase 1.
621
-
622
- Edges are deleted in batch across all filenames first to avoid Kuzu
623
- "has connected edges" errors when edges from one file point to nodes
624
- in another file within the same scope.
625
+ def _delete_file_scope(
626
+ conn: ladybug.Connection,
627
+ changed_files: set[str],
628
+ dependent_files: set[str],
629
+ ) -> None:
630
+ """Delete nodes and edges for a scope split into changed vs dependent files.
631
+
632
+ ``changed_files`` are files whose content actually changed (added/modified/
633
+ removed): their Symbol nodes are deleted (and re-created by ``_scoped_write``).
634
+ ``dependent_files`` are files pulled in only to re-resolve their OUTGOING
635
+ edges against the changed nodes; their node definitions did not change, so
636
+ their nodes are deliberately PRESERVED (they re-MERGE in place on the same
637
+ deterministic ``symbol_id``). Skipping phantom nodes (filename="").
638
+
639
+ Why dependents are preserved (issue #305): the orchestrator computes
640
+ dependents from the *changed* nodes only, so a dependent file's node can
641
+ have an incoming CALLS edge from an out-of-scope caller. The ``source_file``
642
+ on every Symbol->Symbol edge is the CALLER's file (pinned by
643
+ ``test_source_file_value_matches_symbol_filename``), so Phase 1 below only
644
+ deletes edges ORIGINATING in scope; incoming edges from out-of-scope callers
645
+ survive. If we then tried to DELETE the dependent node, LadybugDB rejects it
646
+ ("Node ... has connected edges in table CALLS in the bwd direction, ...
647
+ Please delete the edges first or try DETACH DELETE") and the rebuild falls
648
+ back to a full rebuild. A naive fix (DETACH DELETE on dependents, or an
649
+ extra incoming-edge pass) would silence the crash but permanently drop those
650
+ out-of-scope edges, corrupting the graph. Preserving dependent nodes keeps
651
+ both the nodes and their incoming edges intact.
652
+
653
+ Phase 1 deletes ALL edge types across the whole scope (changed + dependent)
654
+ first to avoid LadybugDB "has connected edges" errors when edges from one
655
+ file point to nodes in another file within the same scope. Route/Client/
656
+ Producer nodes use DETACH DELETE as a safety net for any edges missed in
657
+ Phase 1.
625
658
  """
626
- filename_list = list(filenames)
627
-
628
- # Phase 1: Delete ALL edges from ALL scope files at once.
629
- # This avoids ordering issues where file A has an edge from file B
630
- # pointing into it; if we delete A's nodes before B's edges, Kuzu
631
- # raises "has connected edges" errors.
659
+ scope_files = changed_files | dependent_files
660
+ scope_list = list(scope_files)
661
+ changed_list = list(changed_files)
662
+
663
+ # Phase 1: Delete ALL edges ORIGINATING from any scope file (changed +
664
+ # dependent). Because `source_file` is the caller's file, this deletes edges
665
+ # whose source is in scope (including dependents' outgoing edges to changed
666
+ # nodes) while intentionally leaving incoming edges from out-of-scope callers
667
+ # intact — those must survive so the dependent nodes below can be preserved.
668
+ # This list is a superset of `_SYMBOL_TO_SYMBOL_EDGE_TYPES` (it also covers
669
+ # Symbol->Route/Client/Producer/UCS and Client/Producer->Route edges); keep
670
+ # both lists in sync with the schema.
632
671
  edge_tables = [
633
672
  "EXTENDS", "IMPLEMENTS", "INJECTS", "CALLS", "DECLARES", "OVERRIDES",
634
673
  "UNRESOLVED_AT", "EXPOSES", "DECLARES_CLIENT", "DECLARES_PRODUCER",
@@ -640,7 +679,7 @@ def _delete_file_scope(conn: kuzu.Connection, filenames: set[str]) -> None:
640
679
  WHERE e.source_file IN $filenames
641
680
  DELETE e
642
681
  """
643
- conn.execute(query, {"filenames": filename_list})
682
+ conn.execute(query, {"filenames": scope_list})
644
683
 
645
684
  # Phase 2: Collect all Symbol node IDs for UnresolvedCallSite cleanup.
646
685
  symbol_ids: list[str] = []
@@ -649,12 +688,15 @@ def _delete_file_scope(conn: kuzu.Connection, filenames: set[str]) -> None:
649
688
  WHERE s.filename IN $filenames
650
689
  RETURN s.id
651
690
  """
652
- result = conn.execute(symbol_ids_query, {"filenames": filename_list})
691
+ result = conn.execute(symbol_ids_query, {"filenames": scope_list})
653
692
  while result.has_next():
654
693
  row = result.get_next()
655
694
  symbol_ids.append(row[0])
656
695
 
657
- # Delete UnresolvedCallSite nodes whose caller_id is in the collected set
696
+ # Delete UnresolvedCallSite nodes whose caller_id is in the collected set.
697
+ # These are children of scope symbols (including preserved dependents);
698
+ # deleting them is safe because every scope file — dependents included — is
699
+ # reprocessed and re-emits its UnresolvedCallSite nodes in `_scoped_write`.
658
700
  if symbol_ids:
659
701
  unresolved_query = """
660
702
  MATCH (u:UnresolvedCallSite)
@@ -663,27 +705,37 @@ def _delete_file_scope(conn: kuzu.Connection, filenames: set[str]) -> None:
663
705
  """
664
706
  conn.execute(unresolved_query, {"symbol_ids": symbol_ids})
665
707
 
666
- # Phase 3: Delete Symbol nodes.
708
+ # Phase 3: Delete Symbol nodes ONLY for changed files (not dependents).
709
+ # Dependent-file nodes are deliberately PRESERVED so their incoming edges
710
+ # from out-of-scope callers survive; the dependents are re-MERGEd in place
711
+ # by `_scoped_write` on the same deterministic node id. A changed node's
712
+ # real incoming edges all come from dependent files (callers pulled into
713
+ # scope by `_find_dependents`, which walks every type in
714
+ # `_SYMBOL_TO_SYMBOL_EDGE_TYPES`), so Phase 1 already removed them and the
715
+ # dependents re-emit them when reprocessed. DETACH DELETE is only a safety
716
+ # net for the rare surviving edge whose source was NOT pulled into scope
717
+ # (e.g. a phantom caller with filename="", which `_find_dependents` skips);
718
+ # such an edge is stale once the node is recreated, so dropping it is fine.
667
719
  delete_symbols_query = """
668
720
  MATCH (s:Symbol)
669
721
  WHERE s.filename IN $filenames
670
- DELETE s
722
+ DETACH DELETE s
671
723
  """
672
- conn.execute(delete_symbols_query, {"filenames": filename_list})
724
+ conn.execute(delete_symbols_query, {"filenames": changed_list})
673
725
 
674
726
  # Phase 4: Delete Route, Client, Producer nodes.
675
727
  # Use DETACH DELETE as a safety net in case any edges were missed in Phase 1.
676
728
  for label in ["Route", "Client", "Producer"]:
677
729
  conn.execute(
678
730
  f"MATCH (n:{label}) WHERE n.filename IN $filenames DETACH DELETE n",
679
- {"filenames": filename_list},
731
+ {"filenames": scope_list},
680
732
  )
681
733
 
682
734
 
683
- def _scoped_write(conn: kuzu.Connection, tables: GraphTables, *, project_root: Path, meta_chain: dict[str, frozenset[str]] | None) -> None:
684
- """Write nodes and edges to existing Kuzu database without drop/create schema.
735
+ def _scoped_write(conn: ladybug.Connection, tables: GraphTables, *, project_root: Path, meta_chain: dict[str, frozenset[str]] | None) -> None:
736
+ """Write nodes and edges to existing LadybugDB database without drop/create schema.
685
737
 
686
- Like write_kuzu() but without _drop_all()/_create_schema(). The caller is
738
+ Like write_ladybug() but without _drop_all()/_create_schema(). The caller is
687
739
  responsible for calling _populate_declares_rows() and _populate_overrides_rows()
688
740
  before invoking this function.
689
741
 
@@ -715,13 +767,13 @@ def _scoped_write(conn: kuzu.Connection, tables: GraphTables, *, project_root: P
715
767
 
716
768
 
717
769
  def _write_nodes_merge(
718
- conn: kuzu.Connection,
770
+ conn: ladybug.Connection,
719
771
  tables: GraphTables,
720
772
  *,
721
773
  project_root: Path,
722
774
  meta_chain: dict[str, frozenset[str]] | None,
723
775
  ) -> None:
724
- """Write nodes to existing Kuzu database using MERGE to handle existing nodes."""
776
+ """Write nodes to existing LadybugDB database using MERGE to handle existing nodes."""
725
777
  _write_nodes_impl(conn, tables, project_root=project_root, meta_chain=meta_chain, symbol_query=_MERGE_SYMBOL)
726
778
 
727
779
 
@@ -2664,7 +2716,7 @@ def pass6_match_edges(
2664
2716
  )
2665
2717
 
2666
2718
 
2667
- # ---------- Kuzu write ----------
2719
+ # ---------- LadybugDB write ----------
2668
2720
 
2669
2721
 
2670
2722
  _SCHEMA_NODE = (
@@ -2685,7 +2737,7 @@ _SCHEMA_META = (
2685
2737
  "ontology_version INT64, built_at INT64, source_root STRING, "
2686
2738
  "counts_json STRING, parse_errors INT64, "
2687
2739
  "routes_total INT64, exposes_total INT64, "
2688
- # JSON map {framework: count}; STRING avoids Kuzu Python MAP↔STRUCT binder mismatch.
2740
+ # JSON map {framework: count}; STRING avoids LadybugDB Python MAP↔STRUCT binder mismatch.
2689
2741
  "routes_by_framework STRING, "
2690
2742
  "routes_resolved_pct DOUBLE, "
2691
2743
  "routes_from_brownfield_pct DOUBLE, "
@@ -2798,7 +2850,7 @@ _SCHEMA_ASYNC_CALLS = (
2798
2850
  )
2799
2851
 
2800
2852
 
2801
- def _drop_all(conn: kuzu.Connection) -> None:
2853
+ def _drop_all(conn: ladybug.Connection) -> None:
2802
2854
  for stmt in (
2803
2855
  "DROP TABLE IF EXISTS DECLARES_CLIENT",
2804
2856
  "DROP TABLE IF EXISTS DECLARES_PRODUCER",
@@ -2825,7 +2877,7 @@ def _drop_all(conn: kuzu.Connection) -> None:
2825
2877
  pass
2826
2878
 
2827
2879
 
2828
- def _create_schema(conn: kuzu.Connection) -> None:
2880
+ def _create_schema(conn: ladybug.Connection) -> None:
2829
2881
  for stmt in (
2830
2882
  _SCHEMA_NODE,
2831
2883
  _SCHEMA_UNRESOLVED_CALL_SITE,
@@ -2885,7 +2937,7 @@ _MERGE_SYMBOL = (
2885
2937
 
2886
2938
 
2887
2939
  def _write_nodes_impl(
2888
- conn: kuzu.Connection,
2940
+ conn: ladybug.Connection,
2889
2941
  tables: GraphTables,
2890
2942
  *,
2891
2943
  project_root: Path,
@@ -2952,7 +3004,7 @@ def _write_nodes_impl(
2952
3004
 
2953
3005
 
2954
3006
  def _write_nodes(
2955
- conn: kuzu.Connection,
3007
+ conn: ladybug.Connection,
2956
3008
  tables: GraphTables,
2957
3009
  *,
2958
3010
  project_root: Path,
@@ -3064,7 +3116,7 @@ def _direct_supertype_ids(tables: GraphTables, type_id: str) -> list[str]:
3064
3116
  def _populate_overrides_rows(tables: GraphTables) -> None:
3065
3117
  """Materialize (subtype_method)-[:OVERRIDES]->(supertype_method) for one supertype hop.
3066
3118
 
3067
- Matches ``KuzuGraph.override_axis_rollup_for`` (direct ``IMPLEMENTS`` / ``EXTENDS``
3119
+ Matches ``LadybugDBGraph.override_axis_rollup_for`` (direct ``IMPLEMENTS`` / ``EXTENDS``
3068
3120
  only, same ``signature``, distinct method ids, non-static instance methods).
3069
3121
  """
3070
3122
  by_declaring_type: dict[str, list[MemberEntry]] = defaultdict(list)
@@ -3099,7 +3151,7 @@ def _build_file_by_node_id(tables: GraphTables) -> dict[str, str]:
3099
3151
  return lookup
3100
3152
 
3101
3153
 
3102
- def _write_edges(conn: kuzu.Connection, tables: GraphTables, _file_by_node_id: dict[str, str] | None = None) -> None:
3154
+ def _write_edges(conn: ladybug.Connection, tables: GraphTables, _file_by_node_id: dict[str, str] | None = None) -> None:
3103
3155
  # Build node_id -> file_path lookup for source_file resolution.
3104
3156
  if _file_by_node_id is None:
3105
3157
  _file_by_node_id = _build_file_by_node_id(tables)
@@ -3193,7 +3245,7 @@ def _write_edges(conn: kuzu.Connection, tables: GraphTables, _file_by_node_id: d
3193
3245
  })
3194
3246
 
3195
3247
 
3196
- def _write_routes_and_exposes(conn: kuzu.Connection, tables: GraphTables, _file_by_node_id: dict[str, str] | None = None) -> None:
3248
+ def _write_routes_and_exposes(conn: ladybug.Connection, tables: GraphTables, _file_by_node_id: dict[str, str] | None = None) -> None:
3197
3249
  # Build node_id -> file_path lookup for source_file resolution (for Symbol sources).
3198
3250
  if _file_by_node_id is None:
3199
3251
  _file_by_node_id = _build_file_by_node_id(tables)
@@ -3276,7 +3328,7 @@ def _write_routes_and_exposes(conn: kuzu.Connection, tables: GraphTables, _file_
3276
3328
  })
3277
3329
 
3278
3330
 
3279
- def _write_meta(conn: kuzu.Connection, tables: GraphTables, source_root: Path) -> None:
3331
+ def _write_meta(conn: ladybug.Connection, tables: GraphTables, source_root: Path) -> None:
3280
3332
  seen_calls: set[tuple[str, str, int, int]] = set()
3281
3333
  calls_unique = 0
3282
3334
  for row in tables.calls_rows:
@@ -3392,12 +3444,12 @@ def _write_meta(conn: kuzu.Connection, tables: GraphTables, source_root: Path) -
3392
3444
 
3393
3445
  def incremental_rebuild(
3394
3446
  source_root: Path,
3395
- kuzu_path: Path,
3447
+ ladybug_path: Path,
3396
3448
  *,
3397
3449
  verbose: bool,
3398
3450
  expansion_cap: int = 50,
3399
3451
  ) -> IncrementalResult:
3400
- """Incrementally rebuild the Kuzu graph, processing only changed files and their dependents.
3452
+ """Incrementally rebuild the LadybugDB graph, processing only changed files and their dependents.
3401
3453
 
3402
3454
  Returns IncrementalResult with statistics about the rebuild.
3403
3455
  Falls back to full rebuild if:
@@ -3409,7 +3461,7 @@ def incremental_rebuild(
3409
3461
  t_start = time.time()
3410
3462
 
3411
3463
  # Step 1: Load existing graph and detect changes
3412
- if not kuzu_path.exists():
3464
+ if not ladybug_path.exists():
3413
3465
  if verbose:
3414
3466
  _verbose_stderr_line("[increment] no existing graph; falling back to full rebuild")
3415
3467
  # Fall back to full rebuild
@@ -3420,7 +3472,7 @@ def incremental_rebuild(
3420
3472
  pass4_routes(tables, asts, source_root=source_root, verbose=verbose)
3421
3473
  pass5_imperative_edges(tables, asts, source_root=source_root, verbose=verbose)
3422
3474
  pass6_match_edges(tables, verbose=verbose)
3423
- write_kuzu(kuzu_path, tables, source_root=source_root, verbose=verbose)
3475
+ write_ladybug(ladybug_path, tables, source_root=source_root, verbose=verbose)
3424
3476
 
3425
3477
  return IncrementalResult(
3426
3478
  mode="full_fallback",
@@ -3431,8 +3483,8 @@ def incremental_rebuild(
3431
3483
  elapsed_sec=time.time() - t_start,
3432
3484
  )
3433
3485
 
3434
- db = kuzu.Database(str(kuzu_path))
3435
- conn = kuzu.Connection(db)
3486
+ db = ladybug.Database(str(ladybug_path))
3487
+ conn = ladybug.Connection(db)
3436
3488
 
3437
3489
  # Check ontology version
3438
3490
  try:
@@ -3445,7 +3497,7 @@ def incremental_rebuild(
3445
3497
  _verbose_stderr_line(f"[increment] ontology version {version} < 17; falling back to full rebuild")
3446
3498
  conn.close()
3447
3499
  del conn, db
3448
- return _fallback_to_full(source_root, kuzu_path, verbose, t_start)
3500
+ return _fallback_to_full(source_root, ladybug_path, verbose, t_start)
3449
3501
  except Exception as e:
3450
3502
  if verbose:
3451
3503
  _verbose_stderr_line(f"[increment] failed to read ontology version: {e}; falling back to full rebuild")
@@ -3454,9 +3506,9 @@ def incremental_rebuild(
3454
3506
  except Exception:
3455
3507
  pass
3456
3508
  del conn, db
3457
- return _fallback_to_full(source_root, kuzu_path, verbose, t_start)
3509
+ return _fallback_to_full(source_root, ladybug_path, verbose, t_start)
3458
3510
 
3459
- index_dir = kuzu_path.parent
3511
+ index_dir = ladybug_path.parent
3460
3512
  tracker = FileHashTracker(index_dir)
3461
3513
  tracker.load()
3462
3514
 
@@ -3488,7 +3540,7 @@ def incremental_rebuild(
3488
3540
  _verbose_stderr_line("[increment] crash marker exists; falling back to full rebuild")
3489
3541
  conn.close()
3490
3542
  crash_marker_path.unlink(missing_ok=True)
3491
- return _fallback_to_full(source_root, kuzu_path, verbose, t_start)
3543
+ return _fallback_to_full(source_root, ladybug_path, verbose, t_start)
3492
3544
 
3493
3545
  # Write crash marker
3494
3546
  crash_marker_path.write_text("", encoding="utf-8")
@@ -3516,7 +3568,7 @@ def incremental_rebuild(
3516
3568
  _verbose_stderr_line(f"[increment] dependent expansion cap ({expansion_cap}) exceeded ({len(scope_files)} files); falling back to full rebuild")
3517
3569
  conn.close()
3518
3570
  crash_marker_path.unlink(missing_ok=True)
3519
- return _fallback_to_full(source_root, kuzu_path, verbose, t_start)
3571
+ return _fallback_to_full(source_root, ladybug_path, verbose, t_start)
3520
3572
 
3521
3573
  if verbose:
3522
3574
  _verbose_stderr_line(f"[increment] processing {len(scope_files)} files ({len(changed_files)} changed + {len(dependent_files)} dependents)")
@@ -3524,7 +3576,7 @@ def incremental_rebuild(
3524
3576
  # Step 4: Scoped deletion
3525
3577
  if verbose:
3526
3578
  _verbose_stderr_line("[increment] deleting outdated nodes and edges")
3527
- _delete_file_scope(conn, scope_files)
3579
+ _delete_file_scope(conn, changed_files, dependent_files)
3528
3580
 
3529
3581
  # Force deletion to be applied by running a dummy query
3530
3582
  conn.execute("MATCH (s:Symbol) RETURN count(*)")
@@ -3612,12 +3664,12 @@ def incremental_rebuild(
3612
3664
  _verbose_stderr_line(f"[increment] error during incremental rebuild: {e}; falling back to full rebuild")
3613
3665
  conn.close()
3614
3666
  crash_marker_path.unlink(missing_ok=True)
3615
- return _fallback_to_full(source_root, kuzu_path, verbose, t_start)
3667
+ return _fallback_to_full(source_root, ladybug_path, verbose, t_start)
3616
3668
 
3617
3669
 
3618
- def _init_hash_tracker(source_root: Path, kuzu_path: Path) -> int:
3670
+ def _init_hash_tracker(source_root: Path, ladybug_path: Path) -> int:
3619
3671
  """Initialize hash tracker for all Java files. Returns number of files hashed."""
3620
- index_dir = kuzu_path.parent
3672
+ index_dir = ladybug_path.parent
3621
3673
  tracker = FileHashTracker(index_dir)
3622
3674
  tracker.load()
3623
3675
  ignore = LayeredIgnore(source_root)
@@ -3635,7 +3687,7 @@ def _init_hash_tracker(source_root: Path, kuzu_path: Path) -> int:
3635
3687
  return len(all_files)
3636
3688
 
3637
3689
 
3638
- def _fallback_to_full(source_root: Path, kuzu_path: Path, verbose: bool, t_start: float) -> IncrementalResult:
3690
+ def _fallback_to_full(source_root: Path, ladybug_path: Path, verbose: bool, t_start: float) -> IncrementalResult:
3639
3691
  """Fallback to full rebuild."""
3640
3692
  tables = GraphTables()
3641
3693
  asts = pass1_parse(source_root, tables, verbose=verbose)
@@ -3644,7 +3696,7 @@ def _fallback_to_full(source_root: Path, kuzu_path: Path, verbose: bool, t_start
3644
3696
  pass4_routes(tables, asts, source_root=source_root, verbose=verbose)
3645
3697
  pass5_imperative_edges(tables, asts, source_root=source_root, verbose=verbose)
3646
3698
  pass6_match_edges(tables, verbose=verbose)
3647
- write_kuzu(kuzu_path, tables, source_root=source_root, verbose=verbose)
3699
+ write_ladybug(ladybug_path, tables, source_root=source_root, verbose=verbose)
3648
3700
 
3649
3701
  return IncrementalResult(
3650
3702
  mode="full_fallback",
@@ -3656,12 +3708,12 @@ def _fallback_to_full(source_root: Path, kuzu_path: Path, verbose: bool, t_start
3656
3708
  )
3657
3709
 
3658
3710
 
3659
- def _write_clients_producers_and_calls(conn: kuzu.Connection, tables: GraphTables) -> None:
3660
- """Write Route, Client, Producer, and cross-service edges to Kuzu.
3711
+ def _write_clients_producers_and_calls(conn: ladybug.Connection, tables: GraphTables) -> None:
3712
+ """Write Route, Client, Producer, and cross-service edges to LadybugDB.
3661
3713
 
3662
3714
  Used by the incremental rebuild's global pass 5-6 step. Writes phantom
3663
3715
  Route nodes (created by pass5 for cross-service calls) that wouldn't
3664
- otherwise exist in Kuzu.
3716
+ otherwise exist in LadybugDB.
3665
3717
  """
3666
3718
  # Write phantom routes that don't already exist (pass5 creates these for cross-service calls)
3667
3719
  for row in tables.routes_rows:
@@ -3739,7 +3791,7 @@ def _write_clients_producers_and_calls(conn: kuzu.Connection, tables: GraphTable
3739
3791
  })
3740
3792
 
3741
3793
 
3742
- def write_kuzu(
3794
+ def write_ladybug(
3743
3795
  db_path: Path,
3744
3796
  tables: GraphTables,
3745
3797
  *,
@@ -3755,8 +3807,8 @@ def write_kuzu(
3755
3807
  _verbose_stderr_line(_WRITE_START)
3756
3808
  with _VerbosePassHeartbeats("[graph] writing", verbose=verbose):
3757
3809
  db_path.parent.mkdir(parents=True, exist_ok=True)
3758
- db = kuzu.Database(str(db_path))
3759
- conn = kuzu.Connection(db)
3810
+ db = ladybug.Database(str(db_path))
3811
+ conn = ladybug.Connection(db)
3760
3812
  _drop_all(conn)
3761
3813
  _create_schema(conn)
3762
3814
  t0 = time.time()
@@ -3787,22 +3839,22 @@ def write_kuzu(
3787
3839
  # ---------- CLI ----------
3788
3840
 
3789
3841
 
3790
- def _default_kuzu_path() -> Path:
3842
+ def _default_ladybug_path() -> Path:
3791
3843
  idx = os.environ.get("JAVA_CODEBASE_RAG_INDEX_DIR", "").strip()
3792
3844
  if idx and not idx.startswith(("s3://", "gs://", "az://")):
3793
- return Path(os.path.expanduser(idx.rstrip("/"))) / "code_graph.kuzu"
3794
- return Path.cwd() / ".java-codebase-rag" / "code_graph.kuzu"
3845
+ return Path(os.path.expanduser(idx.rstrip("/"))) / "code_graph.lbug"
3846
+ return Path.cwd() / ".java-codebase-rag" / "code_graph.lbug"
3795
3847
 
3796
3848
 
3797
3849
  def main() -> int:
3798
- parser = argparse.ArgumentParser(description="Build an AST-derived Kuzu graph for Java sources.")
3850
+ parser = argparse.ArgumentParser(description="Build an AST-derived LadybugDB graph for Java sources.")
3799
3851
  parser.add_argument("--source-root", default=None, help="Repository / monorepo root to scan for .java (defaults to current working directory)")
3800
3852
  parser.add_argument(
3801
- "--kuzu-path",
3853
+ "--ladybug-path",
3802
3854
  default=None,
3803
3855
  help=(
3804
- "Kuzu database path (file/dir as used by kuzu.Database; "
3805
- "default: $JAVA_CODEBASE_RAG_INDEX_DIR/code_graph.kuzu or ./.java-codebase-rag/code_graph.kuzu)"
3856
+ "LadybugDB database path (file/dir as used by ladybug.Database; "
3857
+ "default: $JAVA_CODEBASE_RAG_INDEX_DIR/code_graph.lbug or ./.java-codebase-rag/code_graph.lbug)"
3806
3858
  ),
3807
3859
  )
3808
3860
  parser.add_argument("--verbose", action="store_true")
@@ -3814,10 +3866,10 @@ def main() -> int:
3814
3866
  print(f"source-root not a directory: {root}", file=sys.stderr)
3815
3867
  return 2
3816
3868
 
3817
- kuzu_path = Path(args.kuzu_path).expanduser() if args.kuzu_path else _default_kuzu_path()
3869
+ ladybug_path = Path(args.ladybug_path).expanduser() if args.ladybug_path else _default_ladybug_path()
3818
3870
 
3819
3871
  if args.incremental:
3820
- result = incremental_rebuild(root, kuzu_path, verbose=args.verbose)
3872
+ result = incremental_rebuild(root, ladybug_path, verbose=args.verbose)
3821
3873
  print(json.dumps({
3822
3874
  "mode": result.mode,
3823
3875
  "files_changed": result.files_changed,
@@ -3837,9 +3889,9 @@ def main() -> int:
3837
3889
  pass4_routes(tables, asts, source_root=root, verbose=args.verbose)
3838
3890
  pass5_imperative_edges(tables, asts, source_root=root, verbose=args.verbose)
3839
3891
  pass6_match_edges(tables, verbose=args.verbose)
3840
- write_kuzu(kuzu_path, tables, source_root=root, verbose=args.verbose)
3892
+ write_ladybug(ladybug_path, tables, source_root=root, verbose=args.verbose)
3841
3893
  if args.verbose:
3842
- _verbose_stderr_line(f"[graph] done · kuzu at {kuzu_path}")
3894
+ _verbose_stderr_line(f"[graph] done · ladybug at {ladybug_path}")
3843
3895
  return 0
3844
3896
 
3845
3897
 
@@ -334,7 +334,7 @@ def collect_annotation_meta_chain(
334
334
  ) -> dict[str, frozenset[str]]:
335
335
  """Map annotation simple name → built-in simple names reachable via meta-annotations.
336
336
 
337
- Single source of truth for Layer A: both the Kuzu writer and Lance chunk
337
+ Single source of truth for Layer A: both the LadybugDB writer and Lance chunk
338
338
  enrichment must use this; they must not derive `meta_chain` from separate
339
339
  filesystem walks. See ``PLAN-BROWNFIELD-ROLE-OVERRIDES`` §
340
340
  *Single source of truth (REQUIRED — read before implementation)*.
@@ -350,7 +350,7 @@ def annotation_meta_decls_from_graph_tables(
350
350
  """From `build_ast_graph.GraphTables.types`, map @interface simple name -> meta anns.
351
351
 
352
352
  Used for diagnostics; Layer A in production uses `collect_annotation_meta_chain`
353
- (disk) so Kuzu and Lance share one index.
353
+ (disk) so LadybugDB and Lance share one index.
354
354
  """
355
355
  decls: dict[str, tuple[str, ...]] = {}
356
356
  first_fqn: dict[str, str] = {}
@@ -1702,7 +1702,7 @@ def enrich_chunk(
1702
1702
 
1703
1703
 
1704
1704
  def symbol_id(kind: str, fqn: str, file_path: str = "", start_byte: int = 0) -> str:
1705
- """Deterministic SHA1-based id for Kuzu Symbol nodes."""
1705
+ """Deterministic SHA1-based id for LadybugDB Symbol nodes."""
1706
1706
  key = f"{kind}|{fqn}|{file_path}|{start_byte}".encode("utf-8")
1707
1707
  return hashlib.sha1(key).hexdigest()
1708
1708