Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions agents/java-codebase-rag-explorer.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ You are a codebase navigation specialist powered by the java-codebase-rag MCP.

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

## Ontology: 15
## 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.

Expand Down Expand Up @@ -243,7 +243,7 @@ Returns **edges** with `attrs` (`confidence`, `strategy`, `match`, … on cross-
| `COMPONENT` | General Spring component |
| `CONFIG` | `@Configuration` class |
| `ENTITY` | JPA / persistence entity |
| `CLIENT` | Outbound HTTP call wrapper (Feign, RestTemplate, WebClient) |
| `CLIENT` | Outbound call wrapper (HTTP and messaging) |
| `MAPPER` | Data mapper / converter |
| `DTO` | Data transfer object — data carrier, no logic |
| `OTHER` | Infrastructure / utility / framework / JDK / unclassified |
Expand Down
20 changes: 19 additions & 1 deletion ast_java.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
# Phase 11: `EDGE_SCHEMA` in `java_ontology.py` (canonical edge navigation schema; v14 re-index).
# Phase 12: CALLS `callee_declaring_role`, supertype-walk dedup, pass3 unresolved counters (v15 re-index).
# Bumps whenever extraction / enrichment semantics change.
ONTOLOGY_VERSION = 15
ONTOLOGY_VERSION = 16

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


def _type_injects_messaging(type_decl: "TypeDecl") -> bool:
"""True when the type injects a messaging template via field or constructor."""
for fld in type_decl.fields:
if fld.type_name in _INJECTED_TYPES_TO_CAPABILITY:
return True
for method in type_decl.methods:
if method.is_constructor:
for p in method.parameters:
if p.type_name in _INJECTED_TYPES_TO_CAPABILITY:
return True
return False


def infer_role_for_type(type_decl: "TypeDecl") -> str:
"""Role inference that also detects DTO-like passive data carriers.

Expand Down Expand Up @@ -2763,6 +2776,11 @@ def infer_role_for_type(type_decl: "TypeDecl") -> str:
if name.endswith(suffix) and name != suffix:
return "DTO"

# Types injecting messaging templates are outbound callers (CLIENT role),
# symmetric with CONTROLLER covering both HTTP and messaging inbound.
if _type_injects_messaging(type_decl):
return "CLIENT"

return "OTHER"


Expand Down
4 changes: 2 additions & 2 deletions docs/AGENT-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Copy the block between `<!-- BEGIN` and `<!-- END` into your project's `AGENTS.m

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

**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.
**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.

**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.

Expand Down Expand Up @@ -234,7 +234,7 @@ Returns **edges** with `attrs` (`confidence`, `strategy`, `match`, … on cross-
| `COMPONENT` | General Spring component |
| `CONFIG` | `@Configuration` class |
| `ENTITY` | JPA / persistence entity |
| `CLIENT` | Outbound HTTP call wrapper (Feign, RestTemplate, WebClient) |
| `CLIENT` | Outbound call wrapper (HTTP and messaging) |
| `MAPPER` | Data mapper / converter |
| `DTO` | Data transfer object — data carrier, no logic |
| `OTHER` | Infrastructure / utility / framework / JDK / unclassified |
Expand Down
4 changes: 2 additions & 2 deletions skills/explore-codebase/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Any time you need to understand structure in an indexed Java codebase: locating

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

**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.
**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.

**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.

Expand Down Expand Up @@ -235,7 +235,7 @@ Returns **edges** with `attrs` (`confidence`, `strategy`, `match`, … on cross-
| `COMPONENT` | General Spring component |
| `CONFIG` | `@Configuration` class |
| `ENTITY` | JPA / persistence entity |
| `CLIENT` | Outbound HTTP call wrapper (Feign, RestTemplate, WebClient) |
| `CLIENT` | Outbound call wrapper (HTTP and messaging) |
| `MAPPER` | Data mapper / converter |
| `DTO` | Data transfer object — data carrier, no logic |
| `OTHER` | Infrastructure / utility / framework / JDK / unclassified |
Expand Down
6 changes: 3 additions & 3 deletions tests/test_agent_skills_static.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

SKILLS_DIR = Path(__file__).resolve().parent.parent / "skills"
SKILL_NAME = "explore-codebase"
EXPECTED_SKILL_DIRS = {"explore-codebase", "navigate-codebase"}
EXPECTED_SKILL_DIRS = {"explore-codebase"}
SKILL_PATH = SKILLS_DIR / SKILL_NAME / "SKILL.md"


Expand Down Expand Up @@ -167,9 +167,9 @@ def test_edge_type_refs_are_valid(self):
class TestBodyStructure:
"""Skill body must contain key sections."""

def test_has_worked_example(self):
def test_has_canonical_workflow(self):
_, body = _read_skill()
assert "## Worked example" in body, "SKILL.md missing '## Worked example'"
assert "## Canonical workflow" in body, "SKILL.md missing '## Canonical workflow'"

def test_has_decision_tree(self):
_, body = _read_skill()
Expand Down
6 changes: 4 additions & 2 deletions tests/test_client_role_rename.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,15 @@ def test_no_legacy_feign_client_role_in_graph(tmp_path: Path) -> None:
assert g.list_by_role("FEIGN_CLIENT") == []


def test_resttemplate_class_unchanged(tmp_path: Path) -> None:
def test_resttemplate_class_gets_client_role_from_messaging(tmp_path: Path) -> None:
root = tmp_path / "proj"
_copy_fixture(root)
g = _build_graph(root, tmp_path / "g.kuzu")
sym = _symbol_by_fqn(g.find_by_name_or_fqn("smoke.a.ClientA"), "smoke.a.ClientA")
assert sym is not None
assert sym.role == "OTHER"
# ClientA injects KafkaTemplate → CLIENT role (symmetric with CONTROLLER)
assert sym.role == "CLIENT"
assert "MESSAGE_PRODUCER" in sym.capabilities
assert "HTTP_CLIENT" not in sym.capabilities


Expand Down
Loading