java-codebase-rag 0.9.1__tar.gz → 0.9.2__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.
- {java_codebase_rag-0.9.1/java_codebase_rag.egg-info → java_codebase_rag-0.9.2}/PKG-INFO +1 -1
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/jrag.py +20 -4
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2/java_codebase_rag.egg-info}/PKG-INFO +1 -1
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_ontology.py +4 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/pyproject.toml +1 -1
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/resolve_service.py +24 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_jrag_traversal_direct.py +133 -2
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_mcp_v2.py +29 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_resolve_service.py +47 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/LICENSE +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/README.md +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/ast_java.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/brownfield_events.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/build_ast_graph.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/chunk_heuristics.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/graph_enrich.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/graph_types.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/index_common.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/__init__.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/_fdlimit.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/_stdio.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/cli.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/cli_format.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/cli_progress.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/config.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/install_data/agents/explorer-rag-cli.md +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/install_data/agents/explorer-rag-enhanced.md +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/install_data/skills/explore-codebase/SKILL.md +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/install_data/skills/explore-codebase-cli/SKILL.md +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/installer.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/jrag_envelope.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/jrag_hints.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/jrag_render.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/lance_optimize.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/pipeline.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag/progress.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag.egg-info/SOURCES.txt +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag.egg-info/dependency_links.txt +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag.egg-info/entry_points.txt +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag.egg-info/requires.txt +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag.egg-info/top_level.txt +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_index_flow_lancedb.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_index_v1_common.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/ladybug_queries.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/mcp_hints.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/mcp_v2.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/path_filtering.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/pr_analysis.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/search_lancedb.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/server.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/setup.cfg +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_agent_skills_static.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_assign_endpoint_client_extraction.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_ast_graph_build.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_ast_java_calls.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_ast_java_capabilities.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_ast_java_thread_safety.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_bank_chat_brownfield_integration.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_brownfield_clients.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_brownfield_events.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_brownfield_overrides.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_brownfield_routes.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_call_edge_matching.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_call_edges_e2e.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_call_graph_receiver_resolution.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_call_graph_smoke_roundtrip.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_call_invariant.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_cli_progress_stdout_invariant.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_cli_quiet_parity.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_client_hint_recovery.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_client_node_extraction.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_client_role_rename.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_config.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_cross_service_resolution_flag.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_edge_navigation_doc.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_fd_limit.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_feign_not_exposer.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_graph_enrich.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_graph_only_boot.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_incremental_graph.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_install_data_sync.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_installer.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_installer_integration.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_installer_surface.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_java_codebase_rag_cli.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_jrag_auto_scope.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_jrag_envelope.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_jrag_listing.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_jrag_locate.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_jrag_orientation.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_jrag_render.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_jrag_status.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_jrag_token_budget.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_jrag_traversal_compose.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_ladybug_queries.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_lance_optimize.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_lancedb_e2e.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_mcp_hints.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_mcp_server_project_root.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_mcp_tools.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_mcp_v2_compose.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_meta_chain_core.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_microservice_scope.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_outgoing_call_extraction.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_packaging_metadata.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_path_filtering.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_pipeline.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_pr_analysis.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_progress.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_resolve_routes_messaging_layer_c.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_route_extraction.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_schema_consistency.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_search_lancedb.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_search_lancedb_capability.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_string_value_atoms.py +0 -0
- {java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_vectors_progress.py +0 -0
|
@@ -812,11 +812,27 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
812
812
|
"CALLS hops from the route handler. Intra-service is an INDEX-TIME data "
|
|
813
813
|
"property: CALLS edges are intra-codebase by construction, and the query "
|
|
814
814
|
"carries no microservice predicate, so the result reflects whatever the "
|
|
815
|
-
"fixture indexed (no query-time constraint). --
|
|
815
|
+
"fixture indexed (no query-time constraint). --depth clamped to 1..8."
|
|
816
816
|
),
|
|
817
817
|
)
|
|
818
|
-
flow.add_argument(
|
|
819
|
-
|
|
818
|
+
flow.add_argument(
|
|
819
|
+
"query",
|
|
820
|
+
help=(
|
|
821
|
+
"Route path (e.g. '/chat/assign') or Kafka topic name (e.g. "
|
|
822
|
+
"'banking.chat.compliance.review'). Resolved with hint_kind=route; "
|
|
823
|
+
"kafka_topic Routes match on topic."
|
|
824
|
+
),
|
|
825
|
+
)
|
|
826
|
+
# Primary flag is --depth (consistent with callers/callees/impact/decompose).
|
|
827
|
+
# --max-hops is kept as a hidden back-compat alias (same dest).
|
|
828
|
+
flow.add_argument(
|
|
829
|
+
"--depth", type=int, default=5, dest="depth",
|
|
830
|
+
help="Max CALLS hops (clamped 1..8, default 5).",
|
|
831
|
+
)
|
|
832
|
+
flow.add_argument(
|
|
833
|
+
"--max-hops", type=int, dest="depth",
|
|
834
|
+
default=argparse.SUPPRESS, help=argparse.SUPPRESS,
|
|
835
|
+
)
|
|
820
836
|
flow.set_defaults(handler=_cmd_flow)
|
|
821
837
|
|
|
822
838
|
# ---- Compose traversals + file inspection (PR-JRAG-3b) ----
|
|
@@ -3001,7 +3017,7 @@ def _cmd_flow(args: argparse.Namespace) -> int:
|
|
|
3001
3017
|
args, reason="trace_request_flow carries no microservice predicate; intra-codebase is an index-time data property"
|
|
3002
3018
|
)
|
|
3003
3019
|
|
|
3004
|
-
max_hops = max(1, min(8, getattr(args, "
|
|
3020
|
+
max_hops = max(1, min(8, getattr(args, "depth", 5)))
|
|
3005
3021
|
flow_data = graph.trace_request_flow(entry_route_id=node.id, max_hops=max_hops)
|
|
3006
3022
|
|
|
3007
3023
|
root_id = node.id
|
|
@@ -90,6 +90,8 @@ VALID_RESOLVE_REASONS: frozenset[str] = frozenset((
|
|
|
90
90
|
"short_name",
|
|
91
91
|
"route_template",
|
|
92
92
|
"route_method_path",
|
|
93
|
+
"route_topic",
|
|
94
|
+
"route_topic_prefix",
|
|
93
95
|
"client_target",
|
|
94
96
|
"client_target_path",
|
|
95
97
|
"client_name",
|
|
@@ -428,6 +430,8 @@ ResolveReason = Literal[
|
|
|
428
430
|
"short_name",
|
|
429
431
|
"route_template",
|
|
430
432
|
"route_method_path",
|
|
433
|
+
"route_topic",
|
|
434
|
+
"route_topic_prefix",
|
|
431
435
|
"client_target",
|
|
432
436
|
"client_target_path",
|
|
433
437
|
"client_name",
|
|
@@ -43,11 +43,13 @@ _RESOLVE_REASON_PRIORITY: dict[ResolveReason, int] = {
|
|
|
43
43
|
"producer_topic_prefix": 1,
|
|
44
44
|
"fqn_suffix": 2,
|
|
45
45
|
"route_template": 2,
|
|
46
|
+
"route_topic": 2,
|
|
46
47
|
"client_fqn": 2,
|
|
47
48
|
"short_name": 3,
|
|
48
49
|
"client_target": 3,
|
|
49
50
|
"client_name": 3,
|
|
50
51
|
"producer_topic": 3,
|
|
52
|
+
"route_topic_prefix": 3,
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
_SYMBOL_RESOLVE_RETURN = (
|
|
@@ -295,6 +297,28 @@ def _resolve_route_candidates(
|
|
|
295
297
|
path_val = str(row.get("path_template") or row.get("path") or "")
|
|
296
298
|
out.append((_node_ref_from_row("route", row), "route_template", len(path_val)))
|
|
297
299
|
|
|
300
|
+
# Kafka/topic routes carry their name in ``topic`` (``path``/``path_template``
|
|
301
|
+
# are empty), so path-based matching above cannot reach them. Match on
|
|
302
|
+
# ``r.topic`` the same way ``_resolve_producer_candidates`` matches
|
|
303
|
+
# ``p.topic`` — this lets ``flow``/``callers``/``overview`` resolve a
|
|
304
|
+
# ``kafka_topic`` Route by topic name. ``_drop_route_mirrors`` below then
|
|
305
|
+
# discards the no-EXPOSES producer phantom in favour of the server route.
|
|
306
|
+
rows = g._rows( # noqa: SLF001
|
|
307
|
+
f"MATCH (r:Route) WHERE r.topic = $topic{scope} RETURN {_ROUTE_RESOLVE_RETURN} LIMIT $lim",
|
|
308
|
+
{"topic": identifier, "lim": lim, **scope_params},
|
|
309
|
+
)
|
|
310
|
+
for row in rows:
|
|
311
|
+
out.append((_node_ref_from_row("route", row), "route_topic", len(identifier)))
|
|
312
|
+
|
|
313
|
+
if not identifier.startswith("/"):
|
|
314
|
+
rows = g._rows( # noqa: SLF001
|
|
315
|
+
f"MATCH (r:Route) WHERE r.topic STARTS WITH $topic{scope} "
|
|
316
|
+
f"RETURN {_ROUTE_RESOLVE_RETURN} LIMIT $lim",
|
|
317
|
+
{"topic": identifier, "lim": lim, **scope_params},
|
|
318
|
+
)
|
|
319
|
+
for row in rows:
|
|
320
|
+
out.append((_node_ref_from_row("route", row), "route_topic_prefix", len(identifier)))
|
|
321
|
+
|
|
298
322
|
return _drop_route_mirrors(g, out)
|
|
299
323
|
|
|
300
324
|
|
|
@@ -22,8 +22,11 @@ Tests (bank-chat fixture):
|
|
|
22
22
|
13. test_impact_service_post_filter_emits_warning
|
|
23
23
|
14. test_decompose_renders_role_waterfall
|
|
24
24
|
15. test_flow_outbound_intra_service_on_fixture
|
|
25
|
-
16.
|
|
26
|
-
17.
|
|
25
|
+
16. test_flow_follows_kafka_topic_on_fixture
|
|
26
|
+
17. test_flow_depth_flag_and_max_hops_alias
|
|
27
|
+
18. test_callers_topic_disambiguates_with_kind
|
|
28
|
+
19. test_traversal_resolve_ambiguous_stops
|
|
29
|
+
20. test_traversal_rejects_offset
|
|
27
30
|
"""
|
|
28
31
|
from __future__ import annotations
|
|
29
32
|
|
|
@@ -620,6 +623,134 @@ def test_flow_outbound_intra_service_on_fixture(
|
|
|
620
623
|
)
|
|
621
624
|
|
|
622
625
|
|
|
626
|
+
def test_flow_follows_kafka_topic_on_fixture(
|
|
627
|
+
corpus_root: Path, ladybug_db_path: Path
|
|
628
|
+
) -> None:
|
|
629
|
+
"""flow resolves a Kafka topic to its Route AND follows it (async edges).
|
|
630
|
+
|
|
631
|
+
Regression: ``_resolve_route_candidates`` matched only on path, so a
|
|
632
|
+
``kafka_topic`` Route (name in ``topic``, ``path=''``) was unresolvable and
|
|
633
|
+
``jrag flow <topic>`` returned 'none'. Resolution alone is not enough — this
|
|
634
|
+
test also asserts the follow graph is non-empty and carries an
|
|
635
|
+
``ASYNC_CALLS`` edge, proving the kafka-specific inbound arm (topic-matched
|
|
636
|
+
Producer) actually fires once the route resolves.
|
|
637
|
+
|
|
638
|
+
``banking.chat.incoming`` is the canonical dual-sided topic: produced by
|
|
639
|
+
``FollowUpKafkaPublisher`` (inbound ``ASYNC_CALLS``) and consumed by
|
|
640
|
+
``ChatKafkaListener`` (outbound ``CALLS`` to ``orchestrationService.handle``).
|
|
641
|
+
"""
|
|
642
|
+
env = _env_for(corpus_root, ladybug_db_path)
|
|
643
|
+
proc = _run_jrag(
|
|
644
|
+
["flow", "banking.chat.incoming", "--format", "json"], env=env
|
|
645
|
+
)
|
|
646
|
+
assert proc.returncode == 0, (
|
|
647
|
+
f"flow on kafka topic failed: rc={proc.returncode}\nstdout={proc.stdout}\nstderr={proc.stderr}"
|
|
648
|
+
)
|
|
649
|
+
payload = json.loads(proc.stdout)
|
|
650
|
+
assert payload["status"] == "ok", f"expected ok, got {payload}"
|
|
651
|
+
assert payload.get("root"), "expected root id (the kafka_topic Route)"
|
|
652
|
+
root_node = payload.get("nodes", {}).get(payload["root"], {})
|
|
653
|
+
assert root_node.get("kind") == "route", f"expected route root, got {root_node}"
|
|
654
|
+
# The follow graph must be non-empty AND carry a kafka async inbound edge —
|
|
655
|
+
# this is the part that proves "flow follows kafka", not just "resolves it".
|
|
656
|
+
edges = payload.get("edges", [])
|
|
657
|
+
assert edges, f"expected non-empty follow graph for a kafka topic, got edges={edges}"
|
|
658
|
+
assert any(e.get("edge_type") == "ASYNC_CALLS" for e in edges), (
|
|
659
|
+
f"expected an ASYNC_CALLS (topic-matched Producer) inbound edge, got {edges}"
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def test_flow_depth_flag_and_max_hops_alias(
|
|
664
|
+
corpus_root: Path, ladybug_db_path: Path
|
|
665
|
+
) -> None:
|
|
666
|
+
"""flow uses --depth (consistent with callers/callees/impact/decompose).
|
|
667
|
+
|
|
668
|
+
--max-hops remains as a hidden back-compat alias (same dest). Both must be
|
|
669
|
+
accepted, produce the same traversal, and --depth must actually change the
|
|
670
|
+
traversal depth (more hops => strictly more CALLS edges).
|
|
671
|
+
"""
|
|
672
|
+
env = _env_for(corpus_root, ladybug_db_path)
|
|
673
|
+
|
|
674
|
+
# --depth is the primary flag now.
|
|
675
|
+
proc_depth = _run_jrag(
|
|
676
|
+
["flow", "/chat/assign", "--depth", "2", "--format", "json"], env=env
|
|
677
|
+
)
|
|
678
|
+
assert proc_depth.returncode == 0, (
|
|
679
|
+
f"flow --depth failed: rc={proc_depth.returncode}\nstdout={proc_depth.stdout}"
|
|
680
|
+
)
|
|
681
|
+
payload_depth = json.loads(proc_depth.stdout)
|
|
682
|
+
assert payload_depth["status"] == "ok", f"expected ok, got {payload_depth}"
|
|
683
|
+
|
|
684
|
+
# --max-hops is the hidden alias; must still be accepted (back-compat).
|
|
685
|
+
proc_alias = _run_jrag(
|
|
686
|
+
["flow", "/chat/assign", "--max-hops", "2", "--format", "json"], env=env
|
|
687
|
+
)
|
|
688
|
+
assert proc_alias.returncode == 0, (
|
|
689
|
+
f"flow --max-hops alias failed: rc={proc_alias.returncode}\nstdout={proc_alias.stdout}"
|
|
690
|
+
)
|
|
691
|
+
payload_alias = json.loads(proc_alias.stdout)
|
|
692
|
+
assert payload_alias["status"] == "ok", f"expected ok, got {payload_alias}"
|
|
693
|
+
# Same dest -> same traversal (depth 2 in both cases).
|
|
694
|
+
assert payload_alias.get("root") == payload_depth.get("root"), (
|
|
695
|
+
"expected identical root id for --depth and --max-hops"
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
# --depth must actually affect the traversal: depth 1 yields strictly fewer
|
|
699
|
+
# outbound CALLS edges than depth 5 on this fixture (~3 vs ~20).
|
|
700
|
+
proc_shallow = _run_jrag(
|
|
701
|
+
["flow", "/chat/assign", "--depth", "1", "--format", "json"], env=env
|
|
702
|
+
)
|
|
703
|
+
proc_deep = _run_jrag(
|
|
704
|
+
["flow", "/chat/assign", "--depth", "5", "--format", "json"], env=env
|
|
705
|
+
)
|
|
706
|
+
shallow = json.loads(proc_shallow.stdout)
|
|
707
|
+
deep = json.loads(proc_deep.stdout)
|
|
708
|
+
assert len(shallow.get("edges", [])) < len(deep.get("edges", [])), (
|
|
709
|
+
f"--depth should change traversal size; depth=1 -> {len(shallow.get('edges', []))}, "
|
|
710
|
+
f"depth=5 -> {len(deep.get('edges', []))}"
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def test_callers_topic_disambiguates_with_kind(
|
|
715
|
+
corpus_root: Path, ladybug_db_path: Path
|
|
716
|
+
) -> None:
|
|
717
|
+
"""A dual-sided topic resolves to BOTH a Producer and a Route -> ambiguous
|
|
718
|
+
without --kind; --kind route collapses to one and runs find_route_callers.
|
|
719
|
+
|
|
720
|
+
Behavior note (from enabling r.topic resolution): ``callers <topic>`` with no
|
|
721
|
+
--kind now searches all kinds, and a topic with both a Producer and a server
|
|
722
|
+
Route is genuinely ambiguous (previously only the Producer matched, which
|
|
723
|
+
then errored because callers only accepts Symbol/Route roots — jrag.py:2371).
|
|
724
|
+
``--kind route`` is the disambiguation that yields a working callers run.
|
|
725
|
+
(--kind producer resolves to the Producer but callers rejects a Producer
|
|
726
|
+
root; use the ``producers`` command for the producer view.)
|
|
727
|
+
"""
|
|
728
|
+
env = _env_for(corpus_root, ladybug_db_path)
|
|
729
|
+
|
|
730
|
+
# No --kind: Producer + Route both match -> ambiguous (no traversal).
|
|
731
|
+
proc_both = _run_jrag(
|
|
732
|
+
["callers", "banking.chat.incoming", "--format", "json"], env=env
|
|
733
|
+
)
|
|
734
|
+
payload_both = json.loads(proc_both.stdout)
|
|
735
|
+
assert payload_both["status"] == "ambiguous", (
|
|
736
|
+
f"expected ambiguous for dual-sided topic without --kind, got {payload_both.get('status')}"
|
|
737
|
+
)
|
|
738
|
+
assert len(payload_both.get("candidates", [])) >= 2, (
|
|
739
|
+
f"expected >=2 candidates (producer + route), got {payload_both.get('candidates')}"
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
# --kind route: resolves to the single server Route and runs find_route_callers.
|
|
743
|
+
proc_route = _run_jrag(
|
|
744
|
+
["callers", "banking.chat.incoming", "--kind", "route", "--format", "json"],
|
|
745
|
+
env=env,
|
|
746
|
+
)
|
|
747
|
+
payload_route = json.loads(proc_route.stdout)
|
|
748
|
+
assert payload_route["status"] == "ok", (
|
|
749
|
+
f"expected --kind route to resolve and run, got {payload_route.get('status')}"
|
|
750
|
+
)
|
|
751
|
+
assert payload_route.get("root"), "expected a resolved root for --kind route"
|
|
752
|
+
|
|
753
|
+
|
|
623
754
|
# ----- Test 16: traversal resolve-ambiguous stops (no auto-pick) -----
|
|
624
755
|
|
|
625
756
|
|
|
@@ -1364,6 +1364,27 @@ def test_resolve_every_reason_in_closed_set_appears() -> None:
|
|
|
1364
1364
|
"end_line": 1,
|
|
1365
1365
|
"resolved": True,
|
|
1366
1366
|
}
|
|
1367
|
+
# kafka_topic Route: name lives in ``topic`` (path empty) — exercises the
|
|
1368
|
+
# r.topic resolve branch (route_topic / route_topic_prefix reasons).
|
|
1369
|
+
kafka_route_row = {
|
|
1370
|
+
"id": "route:reasontopic000",
|
|
1371
|
+
"kind": "kafka_topic",
|
|
1372
|
+
"framework": "kafka",
|
|
1373
|
+
"method": "",
|
|
1374
|
+
"path": "",
|
|
1375
|
+
"path_template": "",
|
|
1376
|
+
"path_regex": "",
|
|
1377
|
+
"topic": "reason.created",
|
|
1378
|
+
"broker": "",
|
|
1379
|
+
"feign_name": "",
|
|
1380
|
+
"feign_url": "",
|
|
1381
|
+
"microservice": "svc",
|
|
1382
|
+
"module": "mod",
|
|
1383
|
+
"filename": "K.java",
|
|
1384
|
+
"start_line": 1,
|
|
1385
|
+
"end_line": 1,
|
|
1386
|
+
"resolved": True,
|
|
1387
|
+
}
|
|
1367
1388
|
client_row = {
|
|
1368
1389
|
"id": "client:reason",
|
|
1369
1390
|
"client_kind": "feign_method",
|
|
@@ -1415,6 +1436,10 @@ def test_resolve_every_reason_in_closed_set_appears() -> None:
|
|
|
1415
1436
|
return [route_row]
|
|
1416
1437
|
if "r.path = $path OR r.path_template = $path" in query:
|
|
1417
1438
|
return [route_row]
|
|
1439
|
+
if "r.topic STARTS WITH $topic" in query:
|
|
1440
|
+
return [kafka_route_row]
|
|
1441
|
+
if "WHERE r.topic = $topic" in query:
|
|
1442
|
+
return [kafka_route_row]
|
|
1418
1443
|
if "WHERE c.id = $id" in query:
|
|
1419
1444
|
return [client_row]
|
|
1420
1445
|
if "STARTS WITH $path" in query:
|
|
@@ -1443,6 +1468,10 @@ def test_resolve_every_reason_in_closed_set_appears() -> None:
|
|
|
1443
1468
|
seen.add(reason)
|
|
1444
1469
|
for _node, reason, _spec in _resolve_route_candidates(g, "/reason"):
|
|
1445
1470
|
seen.add(reason)
|
|
1471
|
+
# Topic resolution: exact ``r.topic`` (route_topic) + prefix
|
|
1472
|
+
# ``r.topic STARTS WITH`` (route_topic_prefix, since no leading '/').
|
|
1473
|
+
for _node, reason, _spec in _resolve_route_candidates(g, "reason.created"):
|
|
1474
|
+
seen.add(reason)
|
|
1446
1475
|
for _node, reason, _spec in _resolve_client_candidates(g, "client:reason"):
|
|
1447
1476
|
seen.add(reason)
|
|
1448
1477
|
for _node, reason, _spec in _resolve_client_candidates(g, "reasonsvc"):
|
|
@@ -146,6 +146,53 @@ def test_resolve_service_producer_topic_prefix(ladybug_db_path: Path) -> None:
|
|
|
146
146
|
assert result.message is not None
|
|
147
147
|
|
|
148
148
|
|
|
149
|
+
def test_resolve_service_route_kafka_topic(ladybug_db_path: Path) -> None:
|
|
150
|
+
"""A Kafka topic name resolves to its Route via ``r.topic``.
|
|
151
|
+
|
|
152
|
+
Regression: ``_resolve_route_candidates`` matched only on
|
|
153
|
+
``path``/``path_template``, so ``kafka_topic`` Routes (which carry their
|
|
154
|
+
name in ``topic`` with ``path=''``) were unresolvable — ``jrag flow
|
|
155
|
+
<topic>``/``callers <topic>``/``overview <topic>`` could not follow kafka
|
|
156
|
+
even when the route + EXPOSES edge existed. ``banking.chat.compliance.review``
|
|
157
|
+
is consumed by ``ComplianceReviewListener`` (consumer-only, no producer
|
|
158
|
+
phantom), so it resolves to exactly one Route.
|
|
159
|
+
"""
|
|
160
|
+
g = LadybugGraph.get(str(ladybug_db_path))
|
|
161
|
+
result = resolve_v2("banking.chat.compliance.review", hint_kind="route", graph=g)
|
|
162
|
+
|
|
163
|
+
assert isinstance(result, ResolveOutput)
|
|
164
|
+
assert result.success is True
|
|
165
|
+
assert result.resolved_identifier == "banking.chat.compliance.review"
|
|
166
|
+
assert result.status == "one", (
|
|
167
|
+
f"topic should resolve to one Route, got status={result.status!r}: {result.message}"
|
|
168
|
+
)
|
|
169
|
+
assert result.node is not None
|
|
170
|
+
assert result.node.kind == "route", f"expected a Route node, got {result.node}"
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def test_resolve_service_route_kafka_topic_drops_producer_mirror(ladybug_db_path: Path) -> None:
|
|
174
|
+
"""A topic with both a producer (phantom, no EXPOSES) and a consumer
|
|
175
|
+
(server Route, EXPOSES) resolves to the single server Route.
|
|
176
|
+
|
|
177
|
+
``banking.chat.incoming`` is produced by ``FollowUpKafkaPublisher`` (phantom
|
|
178
|
+
kafka_topic Route, ``microservice=''``, no EXPOSES) and consumed by
|
|
179
|
+
``ChatKafkaListener`` (server Route with EXPOSES). Both carry the same topic
|
|
180
|
+
so topic-matching surfaces both; ``_drop_route_mirrors`` must collapse them
|
|
181
|
+
to the one exposed server route (status ``one``), not report ``many``.
|
|
182
|
+
"""
|
|
183
|
+
g = LadybugGraph.get(str(ladybug_db_path))
|
|
184
|
+
result = resolve_v2("banking.chat.incoming", hint_kind="route", graph=g)
|
|
185
|
+
|
|
186
|
+
assert isinstance(result, ResolveOutput)
|
|
187
|
+
assert result.success is True
|
|
188
|
+
assert result.status == "one", (
|
|
189
|
+
f"producer+consumer topic should collapse to one server Route via mirror-drop, "
|
|
190
|
+
f"got status={result.status!r}: {result.message}"
|
|
191
|
+
)
|
|
192
|
+
assert result.node is not None
|
|
193
|
+
assert result.node.kind == "route", f"expected a Route node, got {result.node}"
|
|
194
|
+
|
|
195
|
+
|
|
149
196
|
def test_resolve_service_hint_kind_filters(ladybug_db_path: Path) -> None:
|
|
150
197
|
"""hint_kind narrows the search space (route hint won't match symbol-only ids)."""
|
|
151
198
|
g = LadybugGraph.get(str(ladybug_db_path))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/java_codebase_rag.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_assign_endpoint_client_extraction.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_bank_chat_brownfield_integration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_call_graph_receiver_resolution.py
RENAMED
|
File without changes
|
{java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_call_graph_smoke_roundtrip.py
RENAMED
|
File without changes
|
|
File without changes
|
{java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_cli_progress_stdout_invariant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_cross_service_resolution_flag.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_codebase_rag-0.9.1 → java_codebase_rag-0.9.2}/tests/test_resolve_routes_messaging_layer_c.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|