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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ Unresolved targets become **phantom** nodes (`resolved=false`, FQN guessed from
| `CALLS` | method → method | In-process call (confidence-scored, strategy-tagged). |
| `EXPOSES` | type → route | Type exposes an HTTP/async route. |
| `HTTP_CALLS` | client → route | Cross-service HTTP call (caller-side Client to target Route). |
| `ASYNC_CALLS` | symbol → route | Cross-service async (Kafka, Rabbit, JMS, …). |
| `ASYNC_CALLS` | producer → route | Cross-service async (Kafka, Rabbit, JMS, …). |

JDK / Spring / Lombok callees are represented as **phantom** method symbols at index time. Caller/callee traversals default to `exclude_external=true` so those edges are filtered by FQN prefix without dropping them from the graph.

Expand Down Expand Up @@ -426,7 +426,7 @@ Resolution order for `microservice`:

Current ontology version is **14**. Any index built before this version must be rebuilt via `cocoindex update ... --full-reprocess -f` or a full `java-codebase-rag reprocess` (no selective flags) so vectors and graph stay aligned. Until re-indexed, the server defensively JSON-decodes string-form list columns so nothing explodes, but filters like `array_contains` will not work.

Ontology **14** introduces `EDGE_SCHEMA` in `java_ontology.py` as the canonical edge navigation schema (see `docs/EDGE-NAVIGATION.md`). **`HTTP_CALLS` is `Client → Route`** (SCHEMA-V2 PR-B). **`ASYNC_CALLS` remains `Symbol → Route` until PR-C**, which adds the `Producer` node, `DECLARES_PRODUCER`, and flips `ASYNC_CALLS` to `Producer → Route`. Run one full reprocess after upgrading through the SCHEMA-V2 sequence (or when you need the v14 ontology gate).
Ontology **14** introduces `EDGE_SCHEMA` in `java_ontology.py` as the canonical edge navigation schema (see `docs/EDGE-NAVIGATION.md`). **`HTTP_CALLS` is `Client → Route`** (SCHEMA-V2 PR-B). **`ASYNC_CALLS` is `Producer → Route`** with `DECLARES_PRODUCER` (SCHEMA-V2 PR-C). Run one full reprocess after upgrading through the SCHEMA-V2 sequence (or when you need the v14 ontology gate).

Ontology **13** materializes stored `OVERRIDES` edges between method Symbols (subtype override → supertype declaration, matching `signature` on a direct `IMPLEMENTS` / `EXTENDS` hop). `neighbors(edge_types=["OVERRIDES"])` traverses this relationship; `OVERRIDDEN_BY*` keys in `edge_summary` remain describe-time rollups only.

Expand Down
183 changes: 173 additions & 10 deletions build_ast_graph.py

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions docs/AGENT-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
> Calibrated against ontology version **14** (see `ast_java.ONTOLOGY_VERSION` /
> `java_ontology.EDGE_SCHEMA` + valid sets): canonical edge navigation schema in
> `docs/EDGE-NAVIGATION.md`. v14 re-index required; `HTTP_CALLS` is `Client → Route`;
> PR-C adds `Producer` + `DECLARES_PRODUCER` and flips `ASYNC_CALLS`.
> `Producer` + `DECLARES_PRODUCER` and `ASYNC_CALLS` (`Producer → Route`) ship in v14.
> Still includes stored `OVERRIDES` Symbol→Symbol edges and v12 HTTP brownfield
> (`@CodebaseHttpClient`, shared `CodebaseHttpMethod` enum, inbound layer-C HTTP routes
> replace same-method built-in rows). **Design rationale:** navigation surface and tools —
Expand Down Expand Up @@ -96,7 +96,7 @@ Use these strings **verbatim** in `neighbors(..., edge_types=[...])`:
| Method overrides | `OVERRIDES` | Subtype **method** → supertype **declaration** method (same `signature`, one `IMPLEMENTS`/`EXTENDS` hop). `in` = overriders; `out` = overridden declarations |
| Method calls | `CALLS` | `in` = callers; `out` = callees |
| Service boundary | `EXPOSES` | Symbol → Route (handler exposes route) |
| Cross-service | `HTTP_CALLS`, `ASYNC_CALLS` | `HTTP_CALLS`: Client → Route; `ASYNC_CALLS`: Symbol → Route until SCHEMA-V2 PR-C (`Producer` node) |
| Cross-service | `HTTP_CALLS`, `ASYNC_CALLS` | `HTTP_CALLS`: Client → Route; `ASYNC_CALLS`: Producer → Route via `DECLARES_PRODUCER` |

Symmetric: cross-service and intra-service questions use the **same** `neighbors` call with different `edge_types`.

Expand Down Expand Up @@ -191,6 +191,7 @@ Exact allowed values for roles, capabilities, client kinds, etc. live in `java_o
| List interfaces in service S | `find(kind="symbol", filter={"microservice":S,"symbol_kind":"interface"})` | `neighbors` / `describe` |
| List HTTP or Kafka entry points | `find(kind="route", filter={...})` | `describe` |
| List Feign / HTTP clients | `find(kind="client", filter={...})` | `neighbors(..., out, ["HTTP_CALLS"])` if needed |
| List async producers | `find(kind="producer", filter={...})` | `neighbors(..., out, ["ASYNC_CALLS"])` if needed |
| Who calls method M? | Stable symbol id via `resolve`, `find`, or `search` | `neighbors(ids=sym_id, direction="in", edge_types=["CALLS"])` |
| What does M call? | Same | `neighbors(..., direction="out", edge_types=["CALLS"])` |
| Who hits this route? | `find(kind="route", ...)` or route id from logs | `neighbors(ids=route_id, direction="in", edge_types=["HTTP_CALLS","ASYNC_CALLS","EXPOSES"])` |
Expand Down Expand Up @@ -263,7 +264,7 @@ Virtual keys (`OVERRIDDEN_BY`, …) and composed dot-keys are **not** valid `Edg

### Ontology glossary (version 14)

Source of truth: `java_ontology.py` (`EDGE_SCHEMA`, valid sets). Strings are case-sensitive. Edge navigation: [`docs/EDGE-NAVIGATION.md`](./EDGE-NAVIGATION.md) — for `HTTP_CALLS`, traverse via `DECLARES_CLIENT` from a method Symbol or `neighbors` outbound from a Client id; `ASYNC_CALLS` still uses `*_current` member traversals until SCHEMA-V2 PR-C.
Source of truth: `java_ontology.py` (`EDGE_SCHEMA`, valid sets). Strings are case-sensitive. Edge navigation: [`docs/EDGE-NAVIGATION.md`](./EDGE-NAVIGATION.md) — for `HTTP_CALLS`, traverse via `DECLARES_CLIENT` from a method Symbol or `neighbors` outbound from a Client id; for `ASYNC_CALLS`, traverse via `DECLARES_PRODUCER` or outbound from a Producer id.

**Roles:** `CONTROLLER`, `SERVICE`, `REPOSITORY`, `COMPONENT`, `CONFIG`, `ENTITY`, `CLIENT`, `MAPPER`, `DTO`, `OTHER`.

Expand Down
32 changes: 26 additions & 6 deletions docs/EDGE-NAVIGATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
| CALLS | Symbol | Symbol | many_to_many | yes | yes |
| EXPOSES | Symbol | Route | one_to_one | yes | yes |
| DECLARES_CLIENT | Symbol | Client | one_to_many | yes | yes |
| DECLARES_PRODUCER | Symbol | Producer | one_to_many | yes | yes |
| HTTP_CALLS | Client | Route | many_to_many | yes | no |
| ASYNC_CALLS | Symbol | Route | many_to_many | yes | no |
| ASYNC_CALLS | Producer | Route | many_to_many | yes | no |

## EXTENDS

Expand Down Expand Up @@ -183,6 +184,26 @@
- `member_subject`: neighbors(['{id}'],'out',['DECLARES_CLIENT'])
- `alien_subject`: DECLARES_CLIENT connects method Symbol → Client

## DECLARES_PRODUCER

**Endpoints**: `Symbol → Producer`
**Cardinality**: `one_to_many`
**Brownfield-resolver-sourced**: yes
**Member-only** (hints): yes

**Purpose**: method declares an outbound async producer call site

**Attributes**:

- `confidence` (`DOUBLE`) — producer declaration confidence in [0.0, 1.0]
- `strategy` (`STRING`) — producer resolution strategy literal

**Typical traversals**:

- `type_subject`: neighbors(['{id}'],'out',['DECLARES']) then neighbors(member_ids,'{direction}',['DECLARES_PRODUCER'])
- `member_subject`: neighbors(['{id}'],'out',['DECLARES_PRODUCER'])
- `alien_subject`: DECLARES_PRODUCER connects method Symbol → Producer

## HTTP_CALLS

**Endpoints**: `Client → Route`
Expand All @@ -209,12 +230,12 @@

## ASYNC_CALLS

**Endpoints**: `Symbol → Route`
**Endpoints**: `Producer → Route`
**Cardinality**: `many_to_many`
**Brownfield-resolver-sourced**: yes
**Member-only** (hints): no

**Purpose**: resolved async call from declaring method to topic route (pre-flip: Symbol→Route; PR-C: Producer→Route)
**Purpose**: resolved async call from a declared Producer to a topic route

**Attributes**:

Expand All @@ -226,8 +247,7 @@

**Typical traversals**:

- `type_subject_current`: neighbors(['{id}'],'out',['DECLARES']) then neighbors(member_ids,'out',['ASYNC_CALLS'])
- `type_subject`: neighbors(['{id}'],'out',['DECLARES']) then neighbors(member_ids,'out',['DECLARES_PRODUCER']) then neighbors(producer_ids,'out',['ASYNC_CALLS'])
- `member_subject_current`: neighbors(['{id}'],'out',['ASYNC_CALLS'])
- `member_subject`: neighbors(['{id}'],'out',['DECLARES_PRODUCER']) then neighbors(producer_ids,'out',['ASYNC_CALLS'])
- `alien_subject`: ASYNC_CALLS is Symbol→Route until PR-C; use member_subject_current. After PR-C (Producer→Route), use member_subject via DECLARES_PRODUCER
- `route_subject`: neighbors(['{id}'],'in',['ASYNC_CALLS']) then neighbors(producer_ids,'in',['DECLARES_PRODUCER']) for declaring method
- `alien_subject`: ASYNC_CALLS connects Producer→Route; use DECLARES_PRODUCER from a method Symbol, or neighbors(producer_id,'out',['ASYNC_CALLS']) from a Producer id
16 changes: 8 additions & 8 deletions docs/PROPOSES-ORDER.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ When two or more proposes touch overlapping subsystems, the order they lock and

## Current in-flight set (as of 2026-05-16)

1. **SCHEMA-V2** — `propose/SCHEMA-V2-PROPOSE.md` (`Status: locked — implementing`; propose [#151](https://github.com/HumanBean17/java-codebase-rag/pull/151), plan [#155](https://github.com/HumanBean17/java-codebase-rag/pull/155))
- 4 code PRs: PR-A (`EDGE_SCHEMA` + ontology v14 bump), PR-B (`HTTP_CALLS` flip + downstream API), PR-C (`Producer` node + `ASYNC_CALLS` flip + GraphMeta + MCP parity), PR-D (hints v3).
2. **HINTS-V3** — `propose/HINTS-V3-PROPOSE.md` (`Status: locked — implementing via SCHEMA-V2 PR-D`; propose [#154](https://github.com/HumanBean17/java-codebase-rag/pull/154), plan [#155](https://github.com/HumanBean17/java-codebase-rag/pull/155))
1. **HINTS-V3** — `propose/HINTS-V3-PROPOSE.md` (`Status: locked — implementing via SCHEMA-V2 PR-D`; propose [#154](https://github.com/HumanBean17/java-codebase-rag/pull/154), plan [#155](https://github.com/HumanBean17/java-codebase-rag/pull/155))
- Implementation = SCHEMA-V2 PR-D (same PR).

**SCHEMA-V2** (PR-A/B/C) is **completed** — artefacts in `propose/completed/SCHEMA-V2-PROPOSE.md`, `plans/completed/PLAN-SCHEMA-V2.md`, `plans/completed/CURSOR-PROMPTS-SCHEMA-V2.md`. PR-D remains under HINTS-V3.

No other proposes are in flight.

## Lock and merge order

### Phase 1 — propose artefacts

```
SCHEMA-V2-PROPOSE.md [merged #151 — locked for code sequence]
SCHEMA-V2-PROPOSE.md [merged #151 — completed; propose/completed/]
HINTS-V3-PROPOSE.md [merged #154 — implementing in PR-D]
```
Expand All @@ -35,13 +35,13 @@ HINTS-V3-PROPOSE.md [merged #154 — implementing in PR-D]
### Phase 2 — plan + cursor-prompt artefacts

```
plans/PLAN-SCHEMA-V2.md [landed #155]
plans/CURSOR-PROMPTS-SCHEMA-V2.md
plans/completed/PLAN-SCHEMA-V2.md [landed #155; PR-A/B/C done]
plans/completed/CURSOR-PROMPTS-SCHEMA-V2.md
plans/PLAN-HINTS-V3.md
plans/CURSOR-PROMPTS-HINTS-V3.md
```

SCHEMA-V2 Decision 29: `PLAN-SCHEMA-V2.md` + `CURSOR-PROMPTS-SCHEMA-V2.md` are merge gates for **PR-A** (satisfied when [#155](https://github.com/HumanBean17/java-codebase-rag/pull/155) is on `master`).
SCHEMA-V2 Decision 29: plan + prompts merge gates for **PR-A** satisfied ([#155](https://github.com/HumanBean17/java-codebase-rag/pull/155) on `master`; artefacts now under `plans/completed/`).

By analogy: `PLAN-HINTS-V3.md` + `CURSOR-PROMPTS-HINTS-V3.md` are merge gates for **PR-D** (same PR).

Expand Down Expand Up @@ -76,4 +76,4 @@ No PR in this set is parallelizable.

## Maintenance

Update this file when a propose enters draft, locks, or its code PRs land. After PR-D merges, collapse to "no proposes in flight" until the next effort starts.
Update this file when a propose enters draft, locks, or its code PRs land. After PR-D merges, move HINTS-V3 artefacts to `completed/` and collapse to "no proposes in flight" until the next effort starts.
10 changes: 5 additions & 5 deletions docs/skills/java-codebase-explore.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ You cannot reason reliably about cross-service behaviour until these surfaces ex
**Sequence:**

1. Cluster routes by path prefix; **`describe`** on representative `route:` ids.
2. For each major route, **`neighbors(direction="in", edge_types=["EXPOSES"])`** to land on handler symbols; for inbound **`HTTP_CALLS`**, expect **Client** callers (then **`DECLARES_CLIENT` inbound** to the declaring method); **`ASYNC_CALLS`** inbound still lands on Symbol callers until PR-C.
3. Use **`find(kind="client", …)`** with the same microservice filter to list outbound integration points; follow outbound **`HTTP_CALLS`** from each Client (or **`ASYNC_CALLS`** from methods until Producer lands).
2. For each major route, **`neighbors(direction="in", edge_types=["EXPOSES"])`** to land on handler symbols; for inbound **`HTTP_CALLS`**, expect **Client** callers (then **`DECLARES_CLIENT` inbound** to the declaring method); for inbound **`ASYNC_CALLS`**, expect **Producer** callers (then **`DECLARES_PRODUCER` inbound** to the declaring method).
3. Use **`find(kind="client", …)`** and **`find(kind="producer", …)`** with the same microservice filter to list outbound integration points; follow outbound **`HTTP_CALLS`** from each Client and **`ASYNC_CALLS`** from each Producer.

**Stopping rule:** You can summarize how traffic enters the service, what modules/controllers own key paths, and what external systems it calls—**without** claiming tests, runtime config, or unindexed siblings exist in MCP.

Expand All @@ -116,7 +116,7 @@ You cannot reason reliably about cross-service behaviour until these surfaces ex
**Sequence:**

1. **`neighbors(direction="in", edge_types=["EXPOSES"])`** onto the handling symbol; walk **`CALLS`** outbound method-by-method.
2. When a method makes outbound HTTP, **`neighbors(..., out, ["DECLARES_CLIENT"])`** then outbound **`HTTP_CALLS`** from each Client id; for async (pre-PR-C), **`ASYNC_CALLS`** may still be direct from the method Symbol.
2. When a method makes outbound HTTP, **`neighbors(..., out, ["DECLARES_CLIENT"])`** then outbound **`HTTP_CALLS`** from each Client id; for async, **`neighbors(..., out, ["DECLARES_PRODUCER"])`** then outbound **`ASYNC_CALLS`** from each Producer id.
3. Stop at leaves, framework boundaries, or unresolved edges; read **`edge.attrs`** (`attrs.confidence`, `attrs.strategy`, `attrs.match`) and report low-confidence segments as resolver gaps, not as facts.

**Stopping rule:** You reach a stable leaf (external IO, message publish, clear terminal layer) **or** you document every unresolved hop with a concrete next non-MCP check.
Expand Down Expand Up @@ -239,11 +239,11 @@ Ten edge types:
| Group | Edges |
| ----- | ----- |
| Type wiring | `EXTENDS`, `IMPLEMENTS`, `INJECTS` |
| Containment | `DECLARES`, `DECLARES_CLIENT` |
| Containment | `DECLARES`, `DECLARES_CLIENT`, `DECLARES_PRODUCER` |
| Method overrides | `OVERRIDES` |
| Method calls | `CALLS` |
| Service boundary | `EXPOSES` |
| Cross-service | `HTTP_CALLS` (Client→Route), `ASYNC_CALLS` (Symbol→Route until Producer) |
| Cross-service | `HTTP_CALLS` (Client→Route), `ASYNC_CALLS` (Producer→Route) |

For exact argument shapes, recovery playbook, and slash aliases see
[`docs/AGENT-GUIDE.md`](https://github.com/HumanBean17/java-codebase-rag/blob/master/docs/AGENT-GUIDE.md) in the java-codebase-rag repo.
41 changes: 32 additions & 9 deletions java_ontology.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
"route_method_path",
"client_target",
"client_target_path",
"producer_topic",
"producer_topic_prefix",
))

# Brownfield / fallback edge resolution strategies (hints v2 neighbors fuzzy signal).
Expand Down Expand Up @@ -301,6 +303,26 @@ class EdgeSpec:
"alien_subject": "DECLARES_CLIENT connects method Symbol → Client",
},
),
"DECLARES_PRODUCER": EdgeSpec(
name="DECLARES_PRODUCER",
src="Symbol",
dst="Producer",
cardinality="one_to_many",
brownfield_resolver_sourced=True,
attrs=(
EdgeAttr("confidence", "DOUBLE", "producer declaration confidence in [0.0, 1.0]"),
EdgeAttr("strategy", "STRING", "producer resolution strategy literal"),
),
purpose="method declares an outbound async producer call site",
member_only=True,
typical_traversals={
"type_subject": _SYMBOL_TYPE_TRAVERSAL.format(
id="{id}", direction="{direction}", edge="DECLARES_PRODUCER",
),
"member_subject": "neighbors(['{id}'],'out',['DECLARES_PRODUCER'])",
"alien_subject": "DECLARES_PRODUCER connects method Symbol → Producer",
},
),
"HTTP_CALLS": EdgeSpec(
name="HTTP_CALLS",
src="Client",
Expand Down Expand Up @@ -337,7 +359,7 @@ class EdgeSpec:
),
"ASYNC_CALLS": EdgeSpec(
name="ASYNC_CALLS",
src="Symbol",
src="Producer",
dst="Route",
cardinality="many_to_many",
brownfield_resolver_sourced=True,
Expand All @@ -348,25 +370,24 @@ class EdgeSpec:
EdgeAttr("raw_topic", "STRING", "uninterpolated topic template from the call site"),
EdgeAttr("match", "STRING", "cross_service|intra_service|ambiguous|phantom|unresolved"),
),
purpose="resolved async call from declaring method to topic route (pre-flip: Symbol→Route; PR-C: Producer→Route)",
purpose="resolved async call from a declared Producer to a topic route",
typical_traversals={
"type_subject_current": (
"neighbors(['{id}'],'out',['DECLARES']) "
"then neighbors(member_ids,'out',['ASYNC_CALLS'])"
),
"type_subject": (
"neighbors(['{id}'],'out',['DECLARES']) "
"then neighbors(member_ids,'out',['DECLARES_PRODUCER']) "
"then neighbors(producer_ids,'out',['ASYNC_CALLS'])"
),
"member_subject_current": "neighbors(['{id}'],'out',['ASYNC_CALLS'])",
"member_subject": (
"neighbors(['{id}'],'out',['DECLARES_PRODUCER']) "
"then neighbors(producer_ids,'out',['ASYNC_CALLS'])"
),
"route_subject": (
"neighbors(['{id}'],'in',['ASYNC_CALLS']) "
"then neighbors(producer_ids,'in',['DECLARES_PRODUCER']) for declaring method"
),
"alien_subject": (
"ASYNC_CALLS is Symbol→Route until PR-C; use member_subject_current. "
"After PR-C (Producer→Route), use member_subject via DECLARES_PRODUCER"
"ASYNC_CALLS connects Producer→Route; use DECLARES_PRODUCER from a method Symbol, "
"or neighbors(producer_id,'out',['ASYNC_CALLS']) from a Producer id"
),
},
),
Expand All @@ -381,6 +402,8 @@ class EdgeSpec:
"route_method_path",
"client_target",
"client_target_path",
"producer_topic",
"producer_topic_prefix",
]

__all__ = [
Expand Down
Loading
Loading