Skip to content

Commit e1bade8

Browse files
HumanBean17claude
andcommitted
fix(ontology): assign CLIENT role to message producer types (#253)
Types injecting KafkaTemplate, RabbitTemplate, JmsTemplate, StreamBridge, or ApplicationEventPublisher now get CLIENT role (symmetry with CONTROLLER covering both HTTP and messaging inbound). Previously these fell through to OTHER despite having MESSAGE_PRODUCER capability. Ontology bumped 15 → 16; re-index required. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent f0711d9 commit e1bade8

5 files changed

Lines changed: 29 additions & 9 deletions

File tree

agents/java-codebase-rag-explorer.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ You are a codebase navigation specialist powered by the java-codebase-rag MCP.
1717

1818
Java production sources plus SQL and YAML (use `search` `table`: `java`, `sql`, `yaml`, or `all`).
1919

20-
## Ontology: 15
20+
## Ontology: 16
2121

2222
If results look structurally wrong or empty across tools, the index may be missing, stale, or built with a different `ontology_version`; you cannot re-index via MCP — ask the operator to rebuild.
2323

@@ -243,7 +243,7 @@ Returns **edges** with `attrs` (`confidence`, `strategy`, `match`, … on cross-
243243
| `COMPONENT` | General Spring component |
244244
| `CONFIG` | `@Configuration` class |
245245
| `ENTITY` | JPA / persistence entity |
246-
| `CLIENT` | Outbound HTTP call wrapper (Feign, RestTemplate, WebClient) |
246+
| `CLIENT` | Outbound call wrapper (HTTP and messaging) |
247247
| `MAPPER` | Data mapper / converter |
248248
| `DTO` | Data transfer object — data carrier, no logic |
249249
| `OTHER` | Infrastructure / utility / framework / JDK / unclassified |

ast_java.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
# Phase 11: `EDGE_SCHEMA` in `java_ontology.py` (canonical edge navigation schema; v14 re-index).
8484
# Phase 12: CALLS `callee_declaring_role`, supertype-walk dedup, pass3 unresolved counters (v15 re-index).
8585
# Bumps whenever extraction / enrichment semantics change.
86-
ONTOLOGY_VERSION = 15
86+
ONTOLOGY_VERSION = 16
8787

8888
ROLE_ANNOTATIONS: dict[str, str] = {
8989
# Spring Web
@@ -2732,6 +2732,19 @@ def infer_role(annotation_names: Iterable[str]) -> str:
27322732
return "OTHER"
27332733

27342734

2735+
def _type_injects_messaging(type_decl: "TypeDecl") -> bool:
2736+
"""True when the type injects a messaging template via field or constructor."""
2737+
for fld in type_decl.fields:
2738+
if fld.type_name in _INJECTED_TYPES_TO_CAPABILITY:
2739+
return True
2740+
for method in type_decl.methods:
2741+
if method.is_constructor:
2742+
for p in method.parameters:
2743+
if p.type_name in _INJECTED_TYPES_TO_CAPABILITY:
2744+
return True
2745+
return False
2746+
2747+
27352748
def infer_role_for_type(type_decl: "TypeDecl") -> str:
27362749
"""Role inference that also detects DTO-like passive data carriers.
27372750
@@ -2763,6 +2776,11 @@ def infer_role_for_type(type_decl: "TypeDecl") -> str:
27632776
if name.endswith(suffix) and name != suffix:
27642777
return "DTO"
27652778

2779+
# Types injecting messaging templates are outbound callers (CLIENT role),
2780+
# symmetric with CONTROLLER covering both HTTP and messaging inbound.
2781+
if _type_injects_messaging(type_decl):
2782+
return "CLIENT"
2783+
27662784
return "OTHER"
27672785

27682786

docs/AGENT-GUIDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Copy the block between `<!-- BEGIN` and `<!-- END` into your project's `AGENTS.m
1414

1515
**Indexed content:** Java production sources plus SQL and YAML (use `search` `table`: `java`, `sql`, `yaml`, or `all`).
1616

17-
**Ontology: 15** — if results look structurally wrong or empty across tools, the index may be missing, stale, or built with a different `ontology_version`; you cannot re-index via MCP — ask the operator to rebuild.
17+
**Ontology: 16** — if results look structurally wrong or empty across tools, the index may be missing, stale, or built with a different `ontology_version`; you cannot re-index via MCP — ask the operator to rebuild.
1818

1919
**Responses:** On success, `search`, `find`, `describe`, `neighbors`, and `resolve` may include two top-level fields: `hints_structured` (≤5 suggested next-tool calls) and `advisories` (≤5 pure informational strings). Each `hints_structured` entry has `tool`, `args`, `actionable`, `label`, and `reason`. `actionable=true` means you can call the tool directly with `args`; `actionable=false` means partial/advisory — fill missing values or use as guidance. `reason` explains why the hint was emitted. `advisories` carry context education (fuzzy strategy warnings, role collision explanations, etc.) with no tool call suggestion. For `search`/`find`, echoed `limit`/`offset`. Hints are advisory; ignore them when `success` is false.
2020

@@ -234,7 +234,7 @@ Returns **edges** with `attrs` (`confidence`, `strategy`, `match`, … on cross-
234234
| `COMPONENT` | General Spring component |
235235
| `CONFIG` | `@Configuration` class |
236236
| `ENTITY` | JPA / persistence entity |
237-
| `CLIENT` | Outbound HTTP call wrapper (Feign, RestTemplate, WebClient) |
237+
| `CLIENT` | Outbound call wrapper (HTTP and messaging) |
238238
| `MAPPER` | Data mapper / converter |
239239
| `DTO` | Data transfer object — data carrier, no logic |
240240
| `OTHER` | Infrastructure / utility / framework / JDK / unclassified |

skills/explore-codebase/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Any time you need to understand structure in an indexed Java codebase: locating
1515

1616
**Indexed content:** Java production sources plus SQL and YAML (use `search` `table`: `java`, `sql`, `yaml`, or `all`).
1717

18-
**Ontology: 15** — if results look structurally wrong or empty across tools, the index may be missing, stale, or built with a different `ontology_version`; you cannot re-index via MCP — ask the operator to rebuild.
18+
**Ontology: 16** — if results look structurally wrong or empty across tools, the index may be missing, stale, or built with a different `ontology_version`; you cannot re-index via MCP — ask the operator to rebuild.
1919

2020
**Responses:** On success, `search`, `find`, `describe`, `neighbors`, and `resolve` may include two top-level fields: `hints_structured` (≤5 suggested next-tool calls) and `advisories` (≤5 pure informational strings). Each `hints_structured` entry has `tool`, `args`, `actionable`, `label`, and `reason`. `actionable=true` means you can call the tool directly with `args`; `actionable=false` means partial/advisory — fill missing values or use as guidance. `reason` explains why the hint was emitted. `advisories` carry context education (fuzzy strategy warnings, role collision explanations, etc.) with no tool call suggestion. For `search`/`find`, echoed `limit`/`offset`. Hints are advisory; ignore them when `success` is false.
2121

@@ -235,7 +235,7 @@ Returns **edges** with `attrs` (`confidence`, `strategy`, `match`, … on cross-
235235
| `COMPONENT` | General Spring component |
236236
| `CONFIG` | `@Configuration` class |
237237
| `ENTITY` | JPA / persistence entity |
238-
| `CLIENT` | Outbound HTTP call wrapper (Feign, RestTemplate, WebClient) |
238+
| `CLIENT` | Outbound call wrapper (HTTP and messaging) |
239239
| `MAPPER` | Data mapper / converter |
240240
| `DTO` | Data transfer object — data carrier, no logic |
241241
| `OTHER` | Infrastructure / utility / framework / JDK / unclassified |

tests/test_client_role_rename.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,15 @@ def test_no_legacy_feign_client_role_in_graph(tmp_path: Path) -> None:
6262
assert g.list_by_role("FEIGN_CLIENT") == []
6363

6464

65-
def test_resttemplate_class_unchanged(tmp_path: Path) -> None:
65+
def test_resttemplate_class_gets_client_role_from_messaging(tmp_path: Path) -> None:
6666
root = tmp_path / "proj"
6767
_copy_fixture(root)
6868
g = _build_graph(root, tmp_path / "g.kuzu")
6969
sym = _symbol_by_fqn(g.find_by_name_or_fqn("smoke.a.ClientA"), "smoke.a.ClientA")
7070
assert sym is not None
71-
assert sym.role == "OTHER"
71+
# ClientA injects KafkaTemplate → CLIENT role (symmetric with CONTROLLER)
72+
assert sym.role == "CLIENT"
73+
assert "MESSAGE_PRODUCER" in sym.capabilities
7274
assert "HTTP_CLIENT" not in sym.capabilities
7375

7476

0 commit comments

Comments
 (0)