java-codebase-rag 0.12.0__py3-none-any.whl → 0.12.2__py3-none-any.whl
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.12.2.dist-info/METADATA +35 -0
- java_codebase_rag-0.12.2.dist-info/RECORD +4 -0
- {java_codebase_rag-0.12.0.dist-info → java_codebase_rag-0.12.2.dist-info}/WHEEL +1 -1
- java_codebase_rag/_deprecation.py +0 -103
- java_codebase_rag/_fdlimit.py +0 -56
- java_codebase_rag/_stdio.py +0 -32
- java_codebase_rag/_version.py +0 -35
- java_codebase_rag/absence/__init__.py +0 -0
- java_codebase_rag/absence/absence_diagnosis.py +0 -700
- java_codebase_rag/absence/absence_types.py +0 -124
- java_codebase_rag/absence/absence_vocab.py +0 -460
- java_codebase_rag/analysis/__init__.py +0 -0
- java_codebase_rag/analysis/pr_analysis.py +0 -563
- java_codebase_rag/analysis/resolve_service.py +0 -740
- java_codebase_rag/ast/__init__.py +0 -0
- java_codebase_rag/ast/ast_java.py +0 -2847
- java_codebase_rag/ast/ast_kotlin.py +0 -1794
- java_codebase_rag/ast/brownfield_events.py +0 -58
- java_codebase_rag/ast/chunk_heuristics.py +0 -83
- java_codebase_rag/ast/language.py +0 -117
- java_codebase_rag/cli.py +0 -1215
- java_codebase_rag/cli_dispatch.py +0 -251
- java_codebase_rag/cli_format.py +0 -85
- java_codebase_rag/cli_progress.py +0 -94
- java_codebase_rag/config.py +0 -833
- java_codebase_rag/eval/__init__.py +0 -1
- java_codebase_rag/eval/ground_truth.py +0 -100
- java_codebase_rag/eval/metrics.py +0 -107
- java_codebase_rag/eval/runner.py +0 -556
- java_codebase_rag/graph/__init__.py +0 -0
- java_codebase_rag/graph/build_ast_graph.py +0 -4593
- java_codebase_rag/graph/graph_enrich.py +0 -1940
- java_codebase_rag/graph/graph_types.py +0 -224
- java_codebase_rag/graph/java_ontology.py +0 -465
- java_codebase_rag/graph/ladybug_queries.py +0 -2213
- java_codebase_rag/graph/path_filtering.py +0 -509
- java_codebase_rag/index/__init__.py +0 -0
- java_codebase_rag/index/java_index_flow_lancedb.py +0 -879
- java_codebase_rag/index/java_index_v1_common.py +0 -33
- java_codebase_rag/install_data/__init__.py +0 -0
- java_codebase_rag/install_data/agents/explorer-rag-cli.md +0 -110
- java_codebase_rag/install_data/agents/explorer-rag-enhanced.md +0 -152
- java_codebase_rag/install_data/skills/explore-codebase/SKILL.md +0 -165
- java_codebase_rag/install_data/skills/explore-codebase-cli/SKILL.md +0 -107
- java_codebase_rag/installer.py +0 -2188
- java_codebase_rag/jrag.py +0 -4545
- java_codebase_rag/jrag_envelope.py +0 -1107
- java_codebase_rag/jrag_hints.py +0 -204
- java_codebase_rag/jrag_render.py +0 -926
- java_codebase_rag/lance_optimize.py +0 -264
- java_codebase_rag/mcp/__init__.py +0 -0
- java_codebase_rag/mcp/mcp_hints.py +0 -932
- java_codebase_rag/mcp/mcp_v2.py +0 -1916
- java_codebase_rag/mcp/server.py +0 -886
- java_codebase_rag/pipeline.py +0 -531
- java_codebase_rag/progress.py +0 -570
- java_codebase_rag/read_payloads.py +0 -781
- java_codebase_rag/search/__init__.py +0 -0
- java_codebase_rag/search/index_common.py +0 -10
- java_codebase_rag/search/search_lancedb.py +0 -1296
- java_codebase_rag/search/search_lexical.py +0 -449
- java_codebase_rag/search/search_scoring.py +0 -537
- java_codebase_rag/watch/__init__.py +0 -0
- java_codebase_rag/watch/client.py +0 -230
- java_codebase_rag/watch/daemon.py +0 -396
- java_codebase_rag/watch/lock.py +0 -201
- java_codebase_rag/watch/paths.py +0 -76
- java_codebase_rag/watch/protocol.py +0 -122
- java_codebase_rag/watch/server.py +0 -273
- java_codebase_rag/watch/warm.py +0 -105
- java_codebase_rag/watch/watcher.py +0 -394
- java_codebase_rag-0.12.0.dist-info/METADATA +0 -340
- java_codebase_rag-0.12.0.dist-info/RECORD +0 -75
- java_codebase_rag-0.12.0.dist-info/entry_points.txt +0 -5
- java_codebase_rag-0.12.0.dist-info/licenses/LICENSE +0 -21
- java_codebase_rag-0.12.0.dist-info/top_level.txt +0 -1
- /java_codebase_rag/__init__.py → /java_codebase_rag-0.12.2.dist-info/top_level.txt +0 -0
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
"""Shared graph types and helpers used by mcp_v2 and resolve_service.
|
|
2
|
-
|
|
3
|
-
This is the neutral, acyclic shared module. It must NOT import from
|
|
4
|
-
``mcp_v2`` or ``resolve_service`` — both of those import FROM here.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from typing import Any, Literal
|
|
10
|
-
|
|
11
|
-
from pydantic import BaseModel
|
|
12
|
-
|
|
13
|
-
from java_codebase_rag.graph.ladybug_queries import LadybugGraph
|
|
14
|
-
from java_codebase_rag.mcp.mcp_hints import generate_hints
|
|
15
|
-
|
|
16
|
-
__all__ = [
|
|
17
|
-
"NodeRef",
|
|
18
|
-
"StructuredHint",
|
|
19
|
-
"set_hints_enabled",
|
|
20
|
-
"_hints_or_skip",
|
|
21
|
-
"_node_kind_from_id",
|
|
22
|
-
"_resolve_node_kind",
|
|
23
|
-
"_node_ref_from_row",
|
|
24
|
-
"_to_structured_hints",
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class NodeRef(BaseModel):
|
|
29
|
-
id: str
|
|
30
|
-
kind: Literal["symbol", "route", "client", "producer", "unresolved_call_site"]
|
|
31
|
-
fqn: str
|
|
32
|
-
name: str | None = None
|
|
33
|
-
symbol_kind: str | None = None
|
|
34
|
-
microservice: str | None = None
|
|
35
|
-
module: str | None = None
|
|
36
|
-
role: str | None = None
|
|
37
|
-
generated: bool | None = None
|
|
38
|
-
generated_by: str | None = None
|
|
39
|
-
# Identity-adjacent fields consumed by ``jrag_envelope.node_key`` (keying)
|
|
40
|
-
# and ``project_node`` allow-lists (rendering) so find filter-mode nodes
|
|
41
|
-
# carry the same rich shape as the dedicated listings (http-routes /
|
|
42
|
-
# http-clients / producers). All optional with default None: existing
|
|
43
|
-
# NodeRef constructors are unaffected and ``_drop_empty`` strips unset
|
|
44
|
-
# fields at the JSON boundary.
|
|
45
|
-
method: str | None = None
|
|
46
|
-
path: str | None = None
|
|
47
|
-
framework: str | None = None
|
|
48
|
-
member_fqn: str | None = None
|
|
49
|
-
target_service: str | None = None
|
|
50
|
-
client_kind: str | None = None
|
|
51
|
-
topic: str | None = None
|
|
52
|
-
broker: str | None = None
|
|
53
|
-
producer_kind: str | None = None
|
|
54
|
-
filename: str | None = None
|
|
55
|
-
start_line: int | None = None
|
|
56
|
-
resolved: bool | None = None
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class StructuredHint(BaseModel):
|
|
60
|
-
label: str = ""
|
|
61
|
-
tool: Literal["search", "find", "describe", "neighbors", "resolve"]
|
|
62
|
-
args: dict[str, Any]
|
|
63
|
-
actionable: bool = True
|
|
64
|
-
reason: str = ""
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
# Module-level flag set by server.py at startup from resolved config.
|
|
68
|
-
# Single source of truth — both mcp_v2 and resolve_service read this via
|
|
69
|
-
# _hints_or_skip, and server.py sets it via set_hints_enabled (re-exported
|
|
70
|
-
# by mcp_v2 for back-comat).
|
|
71
|
-
_hints_enabled: bool = True
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def set_hints_enabled(enabled: bool) -> None:
|
|
75
|
-
global _hints_enabled
|
|
76
|
-
_hints_enabled = enabled
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def _hints_or_skip(tool: str, payload: dict) -> tuple[list, list]:
|
|
80
|
-
return generate_hints(tool, payload) if _hints_enabled else ([], [])
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def _node_kind_from_id(
|
|
84
|
-
id_str: str,
|
|
85
|
-
) -> Literal["symbol", "route", "client", "producer", "unresolved_call_site"]:
|
|
86
|
-
if id_str.startswith("ucs:"):
|
|
87
|
-
return "unresolved_call_site"
|
|
88
|
-
if id_str.startswith("sym:"):
|
|
89
|
-
return "symbol"
|
|
90
|
-
if id_str.startswith("route:") or id_str.startswith("r:"):
|
|
91
|
-
return "route"
|
|
92
|
-
if id_str.startswith("client:") or id_str.startswith("c:"):
|
|
93
|
-
return "client"
|
|
94
|
-
if id_str.startswith("producer:") or id_str.startswith("p:"):
|
|
95
|
-
return "producer"
|
|
96
|
-
raise ValueError(f"Unknown id prefix for `{id_str}`")
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def _resolve_node_kind(
|
|
100
|
-
graph: LadybugGraph,
|
|
101
|
-
node_id: str,
|
|
102
|
-
) -> Literal["symbol", "route", "client", "producer", "unresolved_call_site"]:
|
|
103
|
-
try:
|
|
104
|
-
return _node_kind_from_id(node_id)
|
|
105
|
-
except ValueError:
|
|
106
|
-
pass
|
|
107
|
-
if graph._rows("MATCH (n:Symbol) WHERE n.id = $id RETURN n.id AS id LIMIT 1", {"id": node_id}): # noqa: SLF001
|
|
108
|
-
return "symbol"
|
|
109
|
-
if graph._rows("MATCH (n:Route) WHERE n.id = $id RETURN n.id AS id LIMIT 1", {"id": node_id}): # noqa: SLF001
|
|
110
|
-
return "route"
|
|
111
|
-
if graph._rows("MATCH (n:Client) WHERE n.id = $id RETURN n.id AS id LIMIT 1", {"id": node_id}): # noqa: SLF001
|
|
112
|
-
return "client"
|
|
113
|
-
if graph._rows("MATCH (n:Producer) WHERE n.id = $id RETURN n.id AS id LIMIT 1", {"id": node_id}): # noqa: SLF001
|
|
114
|
-
return "producer"
|
|
115
|
-
raise ValueError(f"Unknown id prefix for `{node_id}`")
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def _node_ref_from_row(kind: Literal["symbol", "route", "client", "producer"], row: dict[str, Any]) -> NodeRef:
|
|
119
|
-
"""Map a graph store row to a :class:`NodeRef`.
|
|
120
|
-
|
|
121
|
-
``fqn`` is set to each kind's documented natural identifier (README
|
|
122
|
-
§"jrag — agent CLI") so ``jrag_envelope.node_key`` (which checks ``fqn``
|
|
123
|
-
first) keys nodes correctly and no raw graph id leaks:
|
|
124
|
-
|
|
125
|
-
* symbol -> symbol fqn
|
|
126
|
-
* route -> ``"METHOD path"`` (HTTP endpoint); ``"topic:<name>"`` when a
|
|
127
|
-
kafka topic surfaces as :Route; ``""`` for a phantom route
|
|
128
|
-
(node_key then falls through to the composed ``file``)
|
|
129
|
-
* client -> ``"member_fqn->target_service"``
|
|
130
|
-
* producer-> ``"topic:<name>"``
|
|
131
|
-
|
|
132
|
-
Rich detail (member_fqn / target_service / method / path / topic /
|
|
133
|
-
framework / filename / start_line / resolved) is populated alongside so
|
|
134
|
-
find filter-mode nodes render with the same shape as the dedicated
|
|
135
|
-
listings (``http-routes`` / ``http-clients`` / ``producers``) instead of
|
|
136
|
-
collapsing to ``{"kind": ...}`` after the envelope's projection+drop-empty.
|
|
137
|
-
"""
|
|
138
|
-
microservice = str(row.get("microservice") or "") or None
|
|
139
|
-
module = str(row.get("module") or "") or None
|
|
140
|
-
filename = str(row.get("filename") or "") or None
|
|
141
|
-
start_line = row.get("start_line")
|
|
142
|
-
try:
|
|
143
|
-
start_line = int(start_line) if start_line not in (None, "") else None
|
|
144
|
-
except (TypeError, ValueError):
|
|
145
|
-
start_line = None
|
|
146
|
-
resolved_raw = row.get("resolved")
|
|
147
|
-
resolved = bool(resolved_raw) if resolved_raw is not None else None
|
|
148
|
-
nid = str(row.get("id") or "")
|
|
149
|
-
|
|
150
|
-
if kind == "symbol":
|
|
151
|
-
fqn = str(row.get("fqn") or "")
|
|
152
|
-
role = str(row.get("role") or "") or None
|
|
153
|
-
symbol_kind_val = str(row.get("symbol_kind") or row.get("kind") or "").strip()
|
|
154
|
-
symbol_kind = symbol_kind_val or None
|
|
155
|
-
return NodeRef(
|
|
156
|
-
id=nid, kind="symbol", fqn=fqn,
|
|
157
|
-
name=str(row.get("name") or "") or None,
|
|
158
|
-
symbol_kind=symbol_kind,
|
|
159
|
-
microservice=microservice, module=module, role=role,
|
|
160
|
-
filename=filename, start_line=start_line,
|
|
161
|
-
generated=bool(row.get("generated")) if row.get("generated") is not None else None,
|
|
162
|
-
generated_by=str(row.get("generated_by")) if row.get("generated_by") else None,
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
if kind == "route":
|
|
166
|
-
method = str(row.get("method") or "")
|
|
167
|
-
path = str(row.get("path_template") or row.get("path") or "")
|
|
168
|
-
topic = str(row.get("topic") or "") or None
|
|
169
|
-
if method or path:
|
|
170
|
-
fqn = f"{method} {path}".strip()
|
|
171
|
-
elif topic:
|
|
172
|
-
fqn = f"topic:{topic}"
|
|
173
|
-
else:
|
|
174
|
-
fqn = ""
|
|
175
|
-
return NodeRef(
|
|
176
|
-
id=nid, kind="route", fqn=fqn, name=(path or topic),
|
|
177
|
-
method=method or None, path=path or None, topic=topic,
|
|
178
|
-
framework=str(row.get("framework") or "") or None,
|
|
179
|
-
microservice=microservice, module=module,
|
|
180
|
-
filename=filename, start_line=start_line, resolved=resolved,
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
if kind == "client":
|
|
184
|
-
mfqn = str(row.get("member_fqn") or "")
|
|
185
|
-
tgt = str(row.get("target_service") or "")
|
|
186
|
-
method = str(row.get("method") or "")
|
|
187
|
-
path = str(row.get("path_template") or row.get("path") or "")
|
|
188
|
-
member_fqn = mfqn or None
|
|
189
|
-
target_service = tgt or None
|
|
190
|
-
member_simple = (mfqn.rsplit(".", 1)[-1] if mfqn else "") or None
|
|
191
|
-
# Build the contract id ``member_fqn->target_service`` from the RAW
|
|
192
|
-
# strings: ``member_fqn`` was normalized to None above, so interpolating
|
|
193
|
-
# it would emit ``"None-><target>"`` for member-less (brownfield/meta)
|
|
194
|
-
# clients. Fall back to whichever half is present; never a dangling arrow.
|
|
195
|
-
fqn = f"{mfqn}->{tgt}" if (mfqn and tgt) else (mfqn or tgt)
|
|
196
|
-
return NodeRef(
|
|
197
|
-
id=nid, kind="client",
|
|
198
|
-
fqn=fqn,
|
|
199
|
-
name=member_simple,
|
|
200
|
-
member_fqn=member_fqn, target_service=target_service,
|
|
201
|
-
method=method or None, path=path or None,
|
|
202
|
-
client_kind=str(row.get("client_kind") or "") or None,
|
|
203
|
-
microservice=microservice, module=module,
|
|
204
|
-
filename=filename, start_line=start_line, resolved=resolved,
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
# producer
|
|
208
|
-
topic = str(row.get("topic") or "") or None
|
|
209
|
-
member_fqn = str(row.get("member_fqn") or "") or None
|
|
210
|
-
member_simple = (member_fqn.rsplit(".", 1)[-1] if member_fqn else "") or None
|
|
211
|
-
return NodeRef(
|
|
212
|
-
id=nid, kind="producer",
|
|
213
|
-
fqn=(f"topic:{topic}" if topic else ""),
|
|
214
|
-
name=(topic or member_simple),
|
|
215
|
-
topic=topic, broker=str(row.get("broker") or "") or None,
|
|
216
|
-
producer_kind=str(row.get("producer_kind") or "") or None,
|
|
217
|
-
member_fqn=member_fqn,
|
|
218
|
-
microservice=microservice, module=module,
|
|
219
|
-
filename=filename, start_line=start_line, resolved=resolved,
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
def _to_structured_hints(raw: list[Any]) -> list[StructuredHint]:
|
|
224
|
-
return [StructuredHint(label=h.label, tool=h.tool, args=h.args, actionable=h.actionable, reason=h.reason) for h in raw]
|
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
"""Shared valid role and capability label sets for Java indexers and MCP.
|
|
2
|
-
|
|
3
|
-
Used by `ast_java` inference, brownfield config validation in `graph_enrich`,
|
|
4
|
-
and resolver steps for `@CodebaseRole` / `@CodebaseCapability`."""
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from typing import Literal
|
|
9
|
-
|
|
10
|
-
from java_codebase_rag.ast.ast_java import (
|
|
11
|
-
ROLE_ANNOTATIONS,
|
|
12
|
-
_INJECTED_TYPES_TO_CAPABILITY,
|
|
13
|
-
_METHOD_ANN_TO_CAPABILITY,
|
|
14
|
-
_SUPERTYPE_TO_CAPABILITY,
|
|
15
|
-
_TYPE_ANN_TO_CAPABILITY,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
# Roles assignable by indexing: Spring stereotype values plus DTO. ``OTHER`` is the
|
|
19
|
-
# built-in inference fallback (ast_java.infer_role when nothing matches) and is
|
|
20
|
-
# deliberately excluded here — it is a read-side value (the mcp_v2 ``Role`` enum
|
|
21
|
-
# includes it) but not a role a user may set via @CodebaseRole / role_overrides.
|
|
22
|
-
VALID_ROLES: frozenset[str] = frozenset((*ROLE_ANNOTATIONS.values(), "DTO"))
|
|
23
|
-
|
|
24
|
-
VALID_CAPABILITIES: frozenset[str] = frozenset(
|
|
25
|
-
{
|
|
26
|
-
*_METHOD_ANN_TO_CAPABILITY.values(),
|
|
27
|
-
*_TYPE_ANN_TO_CAPABILITY.values(),
|
|
28
|
-
*_INJECTED_TYPES_TO_CAPABILITY.values(),
|
|
29
|
-
*_SUPERTYPE_TO_CAPABILITY.values(),
|
|
30
|
-
}
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
VALID_ROUTE_FRAMEWORKS: frozenset[str] = frozenset((
|
|
34
|
-
"spring_mvc",
|
|
35
|
-
"webflux",
|
|
36
|
-
))
|
|
37
|
-
|
|
38
|
-
VALID_ROUTE_KINDS: frozenset[str] = frozenset((
|
|
39
|
-
"http_endpoint",
|
|
40
|
-
"http_consumer",
|
|
41
|
-
"kafka_topic",
|
|
42
|
-
"rabbit_queue",
|
|
43
|
-
"jms_destination",
|
|
44
|
-
"stream_binding",
|
|
45
|
-
))
|
|
46
|
-
|
|
47
|
-
VALID_CLIENT_KINDS: frozenset[str] = frozenset((
|
|
48
|
-
"feign_method",
|
|
49
|
-
"rest_template",
|
|
50
|
-
"web_client",
|
|
51
|
-
))
|
|
52
|
-
# Named members of VALID_CLIENT_KINDS — reference these at emit/match sites so a
|
|
53
|
-
# rename in the set above cannot silently desync callers (issue #359).
|
|
54
|
-
CLIENT_KIND_FEIGN_METHOD = "feign_method"
|
|
55
|
-
CLIENT_KIND_REST_TEMPLATE = "rest_template"
|
|
56
|
-
CLIENT_KIND_WEB_CLIENT = "web_client"
|
|
57
|
-
|
|
58
|
-
VALID_PRODUCER_KINDS: frozenset[str] = frozenset((
|
|
59
|
-
"kafka_send",
|
|
60
|
-
"stream_bridge_send",
|
|
61
|
-
))
|
|
62
|
-
|
|
63
|
-
VALID_HTTP_CALL_STRATEGIES: frozenset[str] = frozenset((
|
|
64
|
-
"feign_method",
|
|
65
|
-
"rest_template",
|
|
66
|
-
"web_client",
|
|
67
|
-
"unresolved",
|
|
68
|
-
))
|
|
69
|
-
|
|
70
|
-
VALID_ASYNC_CALL_STRATEGIES: frozenset[str] = frozenset((
|
|
71
|
-
"kafka_template",
|
|
72
|
-
"stream_bridge",
|
|
73
|
-
"rabbit_template",
|
|
74
|
-
"jms_template",
|
|
75
|
-
"unresolved",
|
|
76
|
-
))
|
|
77
|
-
|
|
78
|
-
VALID_HTTP_CALL_MATCHES: frozenset[str] = frozenset((
|
|
79
|
-
"cross_service",
|
|
80
|
-
"intra_service",
|
|
81
|
-
"ambiguous",
|
|
82
|
-
"phantom",
|
|
83
|
-
"unresolved",
|
|
84
|
-
))
|
|
85
|
-
|
|
86
|
-
VALID_RESOLVE_REASONS: frozenset[str] = frozenset((
|
|
87
|
-
"exact_id",
|
|
88
|
-
"exact_fqn",
|
|
89
|
-
"fqn_suffix",
|
|
90
|
-
"short_name",
|
|
91
|
-
"route_template",
|
|
92
|
-
"route_method_path",
|
|
93
|
-
"route_topic",
|
|
94
|
-
"route_topic_prefix",
|
|
95
|
-
"client_target",
|
|
96
|
-
"client_target_path",
|
|
97
|
-
"client_name",
|
|
98
|
-
"client_fqn",
|
|
99
|
-
"producer_topic",
|
|
100
|
-
"producer_topic_prefix",
|
|
101
|
-
))
|
|
102
|
-
|
|
103
|
-
# Brownfield / fallback edge resolution strategies (hints v2 neighbors fuzzy signal).
|
|
104
|
-
# ``phantom`` / ``chained_receiver`` are not CALLS edge strategies after PR-3 (receiver
|
|
105
|
-
# failures live on ``UnresolvedCallSite``); they remain on HTTP/ASYNC match literals only.
|
|
106
|
-
FUZZY_STRATEGY_SET: frozenset[str] = frozenset({
|
|
107
|
-
"layer_c_source",
|
|
108
|
-
"layer_b_fqn",
|
|
109
|
-
"overload_ambiguous",
|
|
110
|
-
"implicit_super",
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
VALID_UNRESOLVED_CALL_REASONS: frozenset[str] = frozenset({
|
|
114
|
-
"phantom_unresolved_receiver",
|
|
115
|
-
"chained_receiver",
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
# Union of fuzzy + non-fuzzy resolver strategies that may appear on graph edges
|
|
119
|
-
# carrying a `strategy` column (brownfield layers, codebase stubs, call-graph tiers,
|
|
120
|
-
# HTTP/async dispatch literals). Used by `EdgeSpec.brownfield_resolver_sourced`.
|
|
121
|
-
BROWNFIELD_RESOLVER_STRATEGY_SET: frozenset[str] = frozenset({
|
|
122
|
-
*FUZZY_STRATEGY_SET,
|
|
123
|
-
# Receiver-tier / HTTP match literals — not CALLS edge strategies after PR-3 UCS facet.
|
|
124
|
-
"phantom",
|
|
125
|
-
"chained_receiver",
|
|
126
|
-
"layer_b_ann",
|
|
127
|
-
"layer_a_meta",
|
|
128
|
-
"codebase_route",
|
|
129
|
-
"codebase_client",
|
|
130
|
-
"codebase_producer",
|
|
131
|
-
"annotation",
|
|
132
|
-
"spel",
|
|
133
|
-
"constant_ref",
|
|
134
|
-
*VALID_HTTP_CALL_STRATEGIES,
|
|
135
|
-
*VALID_ASYNC_CALL_STRATEGIES,
|
|
136
|
-
*VALID_CLIENT_KINDS,
|
|
137
|
-
*VALID_PRODUCER_KINDS,
|
|
138
|
-
"import_map",
|
|
139
|
-
"static_import",
|
|
140
|
-
"static_import_wildcard",
|
|
141
|
-
"constructor",
|
|
142
|
-
"method_reference",
|
|
143
|
-
"this_super",
|
|
144
|
-
"unique_type_name",
|
|
145
|
-
"suffix",
|
|
146
|
-
"same_module",
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
NodeKind = Literal["Symbol", "Route", "Client", "Producer"]
|
|
150
|
-
Cardinality = Literal["many_to_many", "many_to_one", "one_to_many", "one_to_one"]
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
@dataclass(frozen=True)
|
|
154
|
-
class EdgeAttr:
|
|
155
|
-
name: str
|
|
156
|
-
graph_type: str
|
|
157
|
-
purpose: str
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
@dataclass(frozen=True)
|
|
161
|
-
class EdgeSpec:
|
|
162
|
-
name: str
|
|
163
|
-
src: NodeKind
|
|
164
|
-
dst: NodeKind
|
|
165
|
-
cardinality: Cardinality
|
|
166
|
-
brownfield_resolver_sourced: bool
|
|
167
|
-
attrs: tuple[EdgeAttr, ...]
|
|
168
|
-
purpose: str
|
|
169
|
-
typical_traversals: dict[str, str]
|
|
170
|
-
member_only: bool = False
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
_SYMBOL_TYPE_TRAVERSAL = (
|
|
174
|
-
"neighbors(['{id}'],'out',['DECLARES']) "
|
|
175
|
-
"then neighbors(member_ids,'{direction}',['{edge}'])"
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
_COMPOSED_MEMBER_TYPE_TRAVERSAL = (
|
|
179
|
-
"neighbors(['{id}'],'out',['DECLARES.{edge}']) — or "
|
|
180
|
-
"neighbors(['{id}'],'out',['DECLARES']) then neighbors(member_ids,'{direction}',['{edge}'])"
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
EDGE_SCHEMA: dict[str, EdgeSpec] = {
|
|
184
|
-
"EXTENDS": EdgeSpec(
|
|
185
|
-
name="EXTENDS",
|
|
186
|
-
src="Symbol",
|
|
187
|
-
dst="Symbol",
|
|
188
|
-
cardinality="many_to_one",
|
|
189
|
-
brownfield_resolver_sourced=False,
|
|
190
|
-
attrs=(
|
|
191
|
-
EdgeAttr("dst_name", "STRING", "raw supertype name as written in source"),
|
|
192
|
-
EdgeAttr("dst_fqn", "STRING", "best-effort resolved FQN of the supertype"),
|
|
193
|
-
EdgeAttr("resolved", "BOOLEAN", "True iff dst_fqn was resolved to an in-graph Symbol"),
|
|
194
|
-
),
|
|
195
|
-
purpose="class or interface direct supertype relation",
|
|
196
|
-
typical_traversals={
|
|
197
|
-
"type_subject": _SYMBOL_TYPE_TRAVERSAL.format(id="{id}", direction="{direction}", edge="EXTENDS"),
|
|
198
|
-
"member_subject": "neighbors(['{id}'],'out',['EXTENDS'])",
|
|
199
|
-
"alien_subject": "EXTENDS connects Symbol → Symbol; use a type or member Symbol id",
|
|
200
|
-
},
|
|
201
|
-
),
|
|
202
|
-
"IMPLEMENTS": EdgeSpec(
|
|
203
|
-
name="IMPLEMENTS",
|
|
204
|
-
src="Symbol",
|
|
205
|
-
dst="Symbol",
|
|
206
|
-
cardinality="many_to_many",
|
|
207
|
-
brownfield_resolver_sourced=False,
|
|
208
|
-
attrs=(
|
|
209
|
-
EdgeAttr("dst_name", "STRING", "raw interface name as written in source"),
|
|
210
|
-
EdgeAttr("dst_fqn", "STRING", "best-effort resolved FQN of the interface"),
|
|
211
|
-
EdgeAttr("resolved", "BOOLEAN", "True iff dst_fqn was resolved to an in-graph Symbol"),
|
|
212
|
-
),
|
|
213
|
-
purpose="class implements interface relation",
|
|
214
|
-
typical_traversals={
|
|
215
|
-
"type_subject": _SYMBOL_TYPE_TRAVERSAL.format(id="{id}", direction="{direction}", edge="IMPLEMENTS"),
|
|
216
|
-
"member_subject": "neighbors(['{id}'],'out',['IMPLEMENTS'])",
|
|
217
|
-
"alien_subject": "IMPLEMENTS connects Symbol → Symbol; use a type or member Symbol id",
|
|
218
|
-
},
|
|
219
|
-
),
|
|
220
|
-
"INJECTS": EdgeSpec(
|
|
221
|
-
name="INJECTS",
|
|
222
|
-
src="Symbol",
|
|
223
|
-
dst="Symbol",
|
|
224
|
-
cardinality="many_to_many",
|
|
225
|
-
brownfield_resolver_sourced=False,
|
|
226
|
-
attrs=(
|
|
227
|
-
EdgeAttr("dst_name", "STRING", "raw injected type name as written in source"),
|
|
228
|
-
EdgeAttr("dst_fqn", "STRING", "best-effort resolved FQN of the injected type"),
|
|
229
|
-
EdgeAttr("resolved", "BOOLEAN", "True iff dst_fqn was resolved to an in-graph Symbol"),
|
|
230
|
-
EdgeAttr("mechanism", "STRING", "injection mechanism literal (constructor, field, setter, …)"),
|
|
231
|
-
EdgeAttr("annotation", "STRING", "injection annotation simple name when present"),
|
|
232
|
-
EdgeAttr("field_or_param", "STRING", "field or parameter name for the injection site"),
|
|
233
|
-
),
|
|
234
|
-
purpose="dependency injection edge from declaring type to injected type",
|
|
235
|
-
typical_traversals={
|
|
236
|
-
"type_subject": _SYMBOL_TYPE_TRAVERSAL.format(id="{id}", direction="{direction}", edge="INJECTS"),
|
|
237
|
-
"member_subject": "neighbors(['{id}'],'in',['INJECTS'])",
|
|
238
|
-
"alien_subject": "INJECTS connects Symbol → Symbol; use a type Symbol id",
|
|
239
|
-
},
|
|
240
|
-
),
|
|
241
|
-
"DECLARES": EdgeSpec(
|
|
242
|
-
name="DECLARES",
|
|
243
|
-
src="Symbol",
|
|
244
|
-
dst="Symbol",
|
|
245
|
-
cardinality="one_to_many",
|
|
246
|
-
brownfield_resolver_sourced=False,
|
|
247
|
-
attrs=(),
|
|
248
|
-
purpose="type declares member Symbol (method, constructor, nested type)",
|
|
249
|
-
typical_traversals={
|
|
250
|
-
"type_subject": "neighbors(['{id}'],'out',['DECLARES'])",
|
|
251
|
-
"member_subject": "neighbors(['{id}'],'in',['DECLARES'])",
|
|
252
|
-
"alien_subject": "DECLARES connects Symbol → Symbol; use a type Symbol id for outbound members",
|
|
253
|
-
},
|
|
254
|
-
),
|
|
255
|
-
"OVERRIDES": EdgeSpec(
|
|
256
|
-
name="OVERRIDES",
|
|
257
|
-
src="Symbol",
|
|
258
|
-
dst="Symbol",
|
|
259
|
-
cardinality="many_to_one",
|
|
260
|
-
brownfield_resolver_sourced=False,
|
|
261
|
-
attrs=(),
|
|
262
|
-
purpose="subtype method overrides supertype declared method with matching signature",
|
|
263
|
-
member_only=True,
|
|
264
|
-
typical_traversals={
|
|
265
|
-
"type_subject": _SYMBOL_TYPE_TRAVERSAL.format(id="{id}", direction="{direction}", edge="OVERRIDES"),
|
|
266
|
-
"member_subject": "neighbors(['{id}'],'out',['OVERRIDES'])",
|
|
267
|
-
"alien_subject": "OVERRIDES connects method Symbol → method Symbol",
|
|
268
|
-
},
|
|
269
|
-
),
|
|
270
|
-
"CALLS": EdgeSpec(
|
|
271
|
-
name="CALLS",
|
|
272
|
-
src="Symbol",
|
|
273
|
-
dst="Symbol",
|
|
274
|
-
cardinality="many_to_many",
|
|
275
|
-
brownfield_resolver_sourced=True,
|
|
276
|
-
attrs=(
|
|
277
|
-
EdgeAttr("call_site_line", "INT64", "source line of the call site"),
|
|
278
|
-
EdgeAttr("call_site_byte", "INT64", "source byte offset of the call site"),
|
|
279
|
-
EdgeAttr("arg_count", "INT64", "argument count at the call site (-1 for method references)"),
|
|
280
|
-
EdgeAttr("confidence", "DOUBLE", "resolver confidence in [0.0, 1.0]"),
|
|
281
|
-
EdgeAttr("strategy", "STRING", "call-graph resolution strategy literal"),
|
|
282
|
-
EdgeAttr("source", "STRING", "call-graph source tag"),
|
|
283
|
-
EdgeAttr("resolved", "BOOLEAN", "True iff callee Symbol was resolved in-graph"),
|
|
284
|
-
EdgeAttr(
|
|
285
|
-
"callee_declaring_role",
|
|
286
|
-
"STRING",
|
|
287
|
-
"role of the Symbol that declares the callee method",
|
|
288
|
-
),
|
|
289
|
-
),
|
|
290
|
-
purpose="intra-codebase method call from caller method to callee method",
|
|
291
|
-
member_only=True,
|
|
292
|
-
typical_traversals={
|
|
293
|
-
"type_subject": _SYMBOL_TYPE_TRAVERSAL.format(id="{id}", direction="{direction}", edge="CALLS"),
|
|
294
|
-
"member_subject": "neighbors(['{id}'],'out',['CALLS'])",
|
|
295
|
-
"alien_subject": "CALLS connects method Symbol → method Symbol",
|
|
296
|
-
},
|
|
297
|
-
),
|
|
298
|
-
"EXPOSES": EdgeSpec(
|
|
299
|
-
name="EXPOSES",
|
|
300
|
-
src="Symbol",
|
|
301
|
-
dst="Route",
|
|
302
|
-
cardinality="one_to_one",
|
|
303
|
-
brownfield_resolver_sourced=True,
|
|
304
|
-
attrs=(
|
|
305
|
-
EdgeAttr("confidence", "DOUBLE", "route extraction confidence in [0.0, 1.0]"),
|
|
306
|
-
EdgeAttr("strategy", "STRING", "route resolution strategy literal"),
|
|
307
|
-
),
|
|
308
|
-
purpose="declaring method exposes an inbound HTTP or messaging Route",
|
|
309
|
-
member_only=True,
|
|
310
|
-
typical_traversals={
|
|
311
|
-
"type_subject": _SYMBOL_TYPE_TRAVERSAL.format(id="{id}", direction="{direction}", edge="EXPOSES"),
|
|
312
|
-
"member_subject": "neighbors(['{id}'],'out',['EXPOSES'])",
|
|
313
|
-
"alien_subject": "EXPOSES connects method Symbol → Route; use a method Symbol id",
|
|
314
|
-
},
|
|
315
|
-
),
|
|
316
|
-
"DECLARES_CLIENT": EdgeSpec(
|
|
317
|
-
name="DECLARES_CLIENT",
|
|
318
|
-
src="Symbol",
|
|
319
|
-
dst="Client",
|
|
320
|
-
cardinality="one_to_many",
|
|
321
|
-
brownfield_resolver_sourced=True,
|
|
322
|
-
attrs=(
|
|
323
|
-
EdgeAttr("confidence", "DOUBLE", "client declaration confidence in [0.0, 1.0]"),
|
|
324
|
-
EdgeAttr("strategy", "STRING", "client resolution strategy literal"),
|
|
325
|
-
),
|
|
326
|
-
purpose="method declares an outbound HTTP client call site",
|
|
327
|
-
member_only=True,
|
|
328
|
-
typical_traversals={
|
|
329
|
-
"type_subject": _SYMBOL_TYPE_TRAVERSAL.format(
|
|
330
|
-
id="{id}", direction="{direction}", edge="DECLARES_CLIENT",
|
|
331
|
-
),
|
|
332
|
-
"member_subject": "neighbors(['{id}'],'out',['DECLARES_CLIENT'])",
|
|
333
|
-
"alien_subject": "DECLARES_CLIENT connects method Symbol → Client",
|
|
334
|
-
},
|
|
335
|
-
),
|
|
336
|
-
"DECLARES_PRODUCER": EdgeSpec(
|
|
337
|
-
name="DECLARES_PRODUCER",
|
|
338
|
-
src="Symbol",
|
|
339
|
-
dst="Producer",
|
|
340
|
-
cardinality="one_to_many",
|
|
341
|
-
brownfield_resolver_sourced=True,
|
|
342
|
-
attrs=(
|
|
343
|
-
EdgeAttr("confidence", "DOUBLE", "producer declaration confidence in [0.0, 1.0]"),
|
|
344
|
-
EdgeAttr("strategy", "STRING", "producer resolution strategy literal"),
|
|
345
|
-
),
|
|
346
|
-
purpose="method declares an outbound async producer call site",
|
|
347
|
-
member_only=True,
|
|
348
|
-
typical_traversals={
|
|
349
|
-
"type_subject": _SYMBOL_TYPE_TRAVERSAL.format(
|
|
350
|
-
id="{id}", direction="{direction}", edge="DECLARES_PRODUCER",
|
|
351
|
-
),
|
|
352
|
-
"member_subject": "neighbors(['{id}'],'out',['DECLARES_PRODUCER'])",
|
|
353
|
-
"alien_subject": "DECLARES_PRODUCER connects method Symbol → Producer",
|
|
354
|
-
},
|
|
355
|
-
),
|
|
356
|
-
"HTTP_CALLS": EdgeSpec(
|
|
357
|
-
name="HTTP_CALLS",
|
|
358
|
-
src="Client",
|
|
359
|
-
dst="Route",
|
|
360
|
-
cardinality="many_to_many",
|
|
361
|
-
brownfield_resolver_sourced=True,
|
|
362
|
-
attrs=(
|
|
363
|
-
EdgeAttr("confidence", "DOUBLE", "pass6 match confidence in [0.0, 1.0]"),
|
|
364
|
-
EdgeAttr("strategy", "STRING", "HTTP call resolution strategy literal"),
|
|
365
|
-
EdgeAttr("method_call", "STRING", "HTTP method of the call site"),
|
|
366
|
-
EdgeAttr("raw_uri", "STRING", "uninterpolated URI template from the call site"),
|
|
367
|
-
EdgeAttr("match", "STRING", "cross_service|intra_service|ambiguous|phantom|unresolved"),
|
|
368
|
-
),
|
|
369
|
-
purpose="resolved HTTP call from a declared Client to a target route",
|
|
370
|
-
typical_traversals={
|
|
371
|
-
"type_subject": (
|
|
372
|
-
"neighbors(['{id}'],'out',['DECLARES']) "
|
|
373
|
-
"then neighbors(member_ids,'out',['DECLARES_CLIENT']) "
|
|
374
|
-
"then neighbors(client_ids,'out',['HTTP_CALLS'])"
|
|
375
|
-
),
|
|
376
|
-
"member_subject": (
|
|
377
|
-
"neighbors(['{id}'],'out',['DECLARES_CLIENT']) "
|
|
378
|
-
"then neighbors(client_ids,'out',['HTTP_CALLS'])"
|
|
379
|
-
),
|
|
380
|
-
"route_subject": (
|
|
381
|
-
"neighbors(['{id}'],'in',['HTTP_CALLS']) "
|
|
382
|
-
"then neighbors(client_ids,'in',['DECLARES_CLIENT']) for declaring method"
|
|
383
|
-
),
|
|
384
|
-
"alien_subject": (
|
|
385
|
-
"HTTP_CALLS connects Client→Route; use DECLARES_CLIENT from a method Symbol, "
|
|
386
|
-
"or neighbors(client_id,'out',['HTTP_CALLS']) from a Client id"
|
|
387
|
-
),
|
|
388
|
-
},
|
|
389
|
-
),
|
|
390
|
-
"ASYNC_CALLS": EdgeSpec(
|
|
391
|
-
name="ASYNC_CALLS",
|
|
392
|
-
src="Producer",
|
|
393
|
-
dst="Route",
|
|
394
|
-
cardinality="many_to_many",
|
|
395
|
-
brownfield_resolver_sourced=True,
|
|
396
|
-
attrs=(
|
|
397
|
-
EdgeAttr("confidence", "DOUBLE", "pass6 match confidence in [0.0, 1.0]"),
|
|
398
|
-
EdgeAttr("strategy", "STRING", "async call resolution strategy literal"),
|
|
399
|
-
EdgeAttr("direction", "STRING", "produce|consume async direction literal"),
|
|
400
|
-
EdgeAttr("raw_topic", "STRING", "uninterpolated topic template from the call site"),
|
|
401
|
-
EdgeAttr("match", "STRING", "cross_service|intra_service|ambiguous|phantom|unresolved"),
|
|
402
|
-
),
|
|
403
|
-
purpose="resolved async call from a declared Producer to a topic route",
|
|
404
|
-
typical_traversals={
|
|
405
|
-
"type_subject": (
|
|
406
|
-
"neighbors(['{id}'],'out',['DECLARES']) "
|
|
407
|
-
"then neighbors(member_ids,'out',['DECLARES_PRODUCER']) "
|
|
408
|
-
"then neighbors(producer_ids,'out',['ASYNC_CALLS'])"
|
|
409
|
-
),
|
|
410
|
-
"member_subject": (
|
|
411
|
-
"neighbors(['{id}'],'out',['DECLARES_PRODUCER']) "
|
|
412
|
-
"then neighbors(producer_ids,'out',['ASYNC_CALLS'])"
|
|
413
|
-
),
|
|
414
|
-
"route_subject": (
|
|
415
|
-
"neighbors(['{id}'],'in',['ASYNC_CALLS']) "
|
|
416
|
-
"then neighbors(producer_ids,'in',['DECLARES_PRODUCER']) for declaring method"
|
|
417
|
-
),
|
|
418
|
-
"alien_subject": (
|
|
419
|
-
"ASYNC_CALLS connects Producer→Route; use DECLARES_PRODUCER from a method Symbol, "
|
|
420
|
-
"or neighbors(producer_id,'out',['ASYNC_CALLS']) from a Producer id"
|
|
421
|
-
),
|
|
422
|
-
},
|
|
423
|
-
),
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
ResolveReason = Literal[
|
|
427
|
-
"exact_id",
|
|
428
|
-
"exact_fqn",
|
|
429
|
-
"fqn_suffix",
|
|
430
|
-
"short_name",
|
|
431
|
-
"route_template",
|
|
432
|
-
"route_method_path",
|
|
433
|
-
"route_topic",
|
|
434
|
-
"route_topic_prefix",
|
|
435
|
-
"client_target",
|
|
436
|
-
"client_target_path",
|
|
437
|
-
"client_name",
|
|
438
|
-
"client_fqn",
|
|
439
|
-
"producer_topic",
|
|
440
|
-
"producer_topic_prefix",
|
|
441
|
-
]
|
|
442
|
-
|
|
443
|
-
__all__ = [
|
|
444
|
-
"VALID_ROLES",
|
|
445
|
-
"VALID_CAPABILITIES",
|
|
446
|
-
"VALID_ROUTE_FRAMEWORKS",
|
|
447
|
-
"VALID_ROUTE_KINDS",
|
|
448
|
-
"VALID_CLIENT_KINDS",
|
|
449
|
-
"CLIENT_KIND_FEIGN_METHOD",
|
|
450
|
-
"CLIENT_KIND_REST_TEMPLATE",
|
|
451
|
-
"CLIENT_KIND_WEB_CLIENT",
|
|
452
|
-
"VALID_PRODUCER_KINDS",
|
|
453
|
-
"VALID_HTTP_CALL_STRATEGIES",
|
|
454
|
-
"VALID_ASYNC_CALL_STRATEGIES",
|
|
455
|
-
"VALID_HTTP_CALL_MATCHES",
|
|
456
|
-
"VALID_RESOLVE_REASONS",
|
|
457
|
-
"FUZZY_STRATEGY_SET",
|
|
458
|
-
"BROWNFIELD_RESOLVER_STRATEGY_SET",
|
|
459
|
-
"NodeKind",
|
|
460
|
-
"Cardinality",
|
|
461
|
-
"EdgeAttr",
|
|
462
|
-
"EdgeSpec",
|
|
463
|
-
"EDGE_SCHEMA",
|
|
464
|
-
"ResolveReason",
|
|
465
|
-
]
|