Skip to content

Commit 3b8dbc0

Browse files
address PR-176 review: find round-trip, docs, landing hygiene
Add find_v2 kuzu_graph round-trip and negative gates; update HINTS-ROAD-SIGNS catalog/UCs for v4 find success; move v4 propose and plan to completed/. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a080e39 commit 3b8dbc0

6 files changed

Lines changed: 50 additions & 14 deletions

File tree

mcp_hints.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
(issue #161 producer/override-route amendments in that appendix).
55
v2 resolve + neighbors fuzzy-strategy catalog: ``propose/completed/HINTS-V2-PROPOSE.md`` Appendix A.
66
v3 empty-neighbors structural catalog: ``propose/completed/HINTS-V3-PROPOSE.md`` §3.1–3.3.
7-
v4 non-empty neighbors success-path catalog: ``propose/HINTS-V4-SUCCESS-PATH-PROPOSE.md``.
7+
v4 success-path catalog: ``propose/completed/HINTS-V4-SUCCESS-PATH-PROPOSE.md``.
88
Priority cap: same propose §7.12 / ``plans/completed/PLAN-HINTS.md`` principles.
99
"""
1010

File renamed without changes.
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Plan: HINTS-V4 (success-path road signs)
22

3-
Status: **active (PR-A in progress)**. This plan implements
4-
[`propose/HINTS-V4-SUCCESS-PATH-PROPOSE.md`](../propose/HINTS-V4-SUCCESS-PATH-PROPOSE.md)
3+
Status: **completed** (PR-A [#175](https://github.com/HumanBean17/java-codebase-rag/pull/175), PR-B [#176](https://github.com/HumanBean17/java-codebase-rag/pull/176)). This plan implements
4+
[`propose/completed/HINTS-V4-SUCCESS-PATH-PROPOSE.md`](../propose/completed/HINTS-V4-SUCCESS-PATH-PROPOSE.md)
55
(issue [#163](https://github.com/HumanBean17/java-codebase-rag/issues/163)).
66

77
Depends on: **NEIGHBORS-DOT-KEY-TRAVERSAL** landed ([#171](https://github.com/HumanBean17/java-codebase-rag/pull/171);
@@ -178,10 +178,10 @@ Implement **verbatim** names from the propose:
178178

179179
## Definition of done (PR-B)
180180

181-
- [ ] F1–F3 wired; page-full + empty-resolve behavior unchanged.
182-
- [ ] Named find tests pass; optional S1 test if implemented.
183-
- [ ] Appendix paragraph added to `HINTS-ROAD-SIGNS-PROPOSE.md`.
184-
- [ ] Full test suite green; no ontology/README requirement unless reviewer asks for README mention.
181+
- [x] F1–F3 wired; page-full + empty-resolve behavior unchanged.
182+
- [x] Named find tests pass; optional S1 test if implemented (S1 deferred).
183+
- [x] Appendix paragraph added to `HINTS-ROAD-SIGNS-PROPOSE.md`.
184+
- [x] Full test suite green; no ontology/README requirement unless reviewer asks for README mention.
185185

186186
## Implementation step list
187187

@@ -227,9 +227,9 @@ Implement **verbatim** names from the propose:
227227

228228
# Tracking
229229

230-
- `PR-A`: _landed (PR open)_
231-
- `PR-B`: _pending_
230+
- `PR-A`: _landed ([#175](https://github.com/HumanBean17/java-codebase-rag/pull/175))_
231+
- `PR-B`: _landed ([#176](https://github.com/HumanBean17/java-codebase-rag/pull/176))_
232232

233233
## Cursor handoff
234234

235-
[`plans/CURSOR-PROMPTS-HINTS-V4.md`](./CURSOR-PROMPTS-HINTS-V4.md)
235+
[`plans/completed/CURSOR-PROMPTS-HINTS-V4.md`](./CURSOR-PROMPTS-HINTS-V4.md)

propose/completed/HINTS-ROAD-SIGNS-PROPOSE.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ The full catalog of hints emitted at v1. Each row is one template; multiple may
108108
| `describe` (client node) | always | `declaring method: neighbors([{id}],'in',['DECLARES_CLIENT'])` |
109109
| `find` | `len(results) == 0` and filter has an identifier-shaped value (e.g. `fqn_prefix`, `target_service`) | `no matches — try resolve(identifier, hint_kind='{kind}') for canonical lookup` |
110110
| `find` | `len(results) >= limit` (page-full) | `result page full at {limit} — narrow filter or paginate` |
111+
| `find` (route) | `len(results) > 0`, not page-full | `handler: neighbors(['{id}'],'in',['EXPOSES'])``{id}` = `results[0].id` (#163 v4) |
112+
| `find` (client) | same, `kind == client` | `HTTP targets: neighbors(['{id}'],'out',['HTTP_CALLS'])` (#163 v4) |
113+
| `find` (producer) | same, `kind == producer` | `async targets: neighbors(['{id}'],'out',['ASYNC_CALLS'])` (#163 v4) |
111114
| `neighbors` | `len(results) == 0` and `len(edge_types) > 0` | `0 results — check if the requested edge_types apply to this kind` |
112115
| `neighbors` | rows include `edge_type='DECLARES'` to method targets, **and** any of those methods has known `DECLARES_CLIENT` out in summary | (deferred — needs second-hop awareness; not in v1) |
113116
| `search` | `len(results) == limit` **and** `(max_score - min_score) < 0.1 * max_score` (structural low-confidence signal — no absolute threshold). Requires `SearchOutput.limit` echo per §3.1 / §7.18. | `results look weak — narrow the query or try find(role=…)` |
@@ -138,8 +141,9 @@ This catalog is the v1 lock. Adding a new template requires a propose-doc amendm
138141
| UC9 | Agent does `neighbors([class_id], 'out', ['DECLARES_CLIENT'])` and gets 0 (correctly, because DECLARES_CLIENT lives on methods) | neighbors | "0 results — check if the requested edge_types apply to this kind" |
139142
| UC10 | Agent does `search`, gets a full page of hits all clustered within 10% of the top score (no dominant match) | search | "results look weak — narrow the query or try find(role=…)" |
140143
| UC11 | Agent describes a leaf method (no rollups, no override axis) | describe | (no hints — clean output, agent has all it needs) |
141-
| UC12 | Agent does `find` with successful match list | find | (no hints — page not full, results present) |
142-
| UC13 | Agent does `neighbors` with results matching all requested edge_types | neighbors | (no hints — happy path) |
144+
| UC12 | Agent does `find` on route, client, or producer with successful match list, not page-full | find | v4 success follow-up (F1–F3): handler / HTTP targets / async targets using `results[0].id` |
145+
| UC12b | Agent does `find(kind=symbol)` with successful match list | find | (no v4 find hints — symbol kind out of F1–F3 scope) |
146+
| UC13 | Agent does `neighbors` with homogeneous results at `offset==0`, single edge type | neighbors | v4 success follow-ups (N1a–N7) when triggers match; else fuzzy-strategy meta only |
143147
| UC14 | Agent describes a class with `DECLARES.DECLARES_CLIENT` and `DECLARES.EXPOSES` both non-zero, plus `OVERRIDDEN_BY` (it's an interface) | describe | up to 3 hints — clients via members, routes via members, overriders if applicable |
144148

145149
15 realistic cases (UC6a + UC6b counted separately). The §2.5 cap is exercised by a dedicated test scenario in §6 (a hand-crafted record with > 5 firing conditions), not by a hypothetical UC — the UC re-walk is the design-validation move, the cap is a guardrail with its own test.
@@ -266,6 +270,9 @@ kind == producer, always → "declaring method: neighbors(['{id}'],'in',
266270
# FindOutput
267271
results==[] and filter has identifier-shaped value → "no matches — try resolve(identifier, hint_kind='{kind}') for canonical lookup"
268272
len(results) >= limit → "result page full at {limit} — narrow filter or paginate"
273+
kind==route, len(results)>0, not page-full → "handler: neighbors(['{id}'],'in',['EXPOSES'])" # v4 #163
274+
kind==client, len(results)>0, not page-full → "HTTP targets: neighbors(['{id}'],'out',['HTTP_CALLS'])" # v4 #163
275+
kind==producer, len(results)>0, not page-full → "async targets: neighbors(['{id}'],'out',['ASYNC_CALLS'])" # v4 #163
269276
270277
# NeighborsOutput
271278
results==[] and edge_types non-empty → "0 results — check if the requested edge_types apply to this kind"
@@ -280,7 +287,7 @@ File placement (`mcp_hints.py`), function decomposition, integration points in `
280287

281288
**Amendment (2026-05-16, issue #161 / PR #164)** — five describe templates for the producer axis and override-route rollup, symmetric with the client/route rows above: `DECLARES.DECLARES_PRODUCER`, `DECLARES_PRODUCER`, `OVERRIDDEN_BY.DECLARES_PRODUCER`, `OVERRIDDEN_BY.EXPOSES`, and `kind == producer` declaring-method hint. No ontology or re-index change.
282289

283-
**Amendment (v4 success-path, issue #163)** — second partial dot-key emission reversal: non-empty `neighbors` success hints on type Symbol origins may recommend `DECLARES.DECLARES_CLIENT`, `DECLARES.DECLARES_PRODUCER`, and `DECLARES.EXPOSES` (matching describe rollups). v3 empty structural `neighbors` hints still never use dot-keys (`_filter_neighbors_dotkey_hints` applies to the empty branch only). `OVERRIDDEN_BY.*` dot-keys remain describe-only. No ontology or re-index change.
290+
**Amendment (v4 success-path, issue #163)**(1) **Find:** non-page-full `find` on route/client/producer emits F1–F3 (`handler` / `HTTP targets` / `async targets`); `{id}` = `results[0].id` when multiple matches. (2) **Neighbors — second partial dot-key emission reversal:** non-empty success hints on type Symbol origins may recommend `DECLARES.DECLARES_CLIENT`, `DECLARES.DECLARES_PRODUCER`, and `DECLARES.EXPOSES` (matching describe rollups). v3 empty structural `neighbors` hints still never use dot-keys (`_filter_neighbors_dotkey_hints` applies to the empty branch only). `OVERRIDDEN_BY.*` dot-keys remain describe-only. No ontology or re-index change.
284291

285292
**What stayed unchanged from the first draft**
286293

propose/HINTS-V4-SUCCESS-PATH-PROPOSE.md renamed to propose/completed/HINTS-V4-SUCCESS-PATH-PROPOSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Status
44

5-
**Status**: locked — not yet implemented. Tracks [issue #163](https://github.com/HumanBean17/java-codebase-rag/issues/163). Implementation plan: [`plans/PLAN-HINTS-V4.md`](../plans/PLAN-HINTS-V4.md) (planning PR [#174](https://github.com/HumanBean17/java-codebase-rag/pull/174)); Cursor prompts: [`plans/CURSOR-PROMPTS-HINTS-V4.md`](../plans/CURSOR-PROMPTS-HINTS-V4.md).
5+
**Status**: completed (landed PRs [#175](https://github.com/HumanBean17/java-codebase-rag/pull/175) neighbors, [#176](https://github.com/HumanBean17/java-codebase-rag/pull/176) find). Tracks [issue #163](https://github.com/HumanBean17/java-codebase-rag/issues/163). Plan: [`plans/completed/PLAN-HINTS-V4.md`](../plans/completed/PLAN-HINTS-V4.md); Cursor prompts: [`plans/completed/CURSOR-PROMPTS-HINTS-V4.md`](../plans/completed/CURSOR-PROMPTS-HINTS-V4.md).
66

77
**Depends on (landed):** [NEIGHBORS-DOT-KEY-TRAVERSAL](./completed/NEIGHBORS-DOT-KEY-TRAVERSAL-PROPOSE.md) ([#171](https://github.com/HumanBean17/java-codebase-rag/pull/171)) — `neighbors` accepts `DECLARES.DECLARES_CLIENT`, `DECLARES.DECLARES_PRODUCER`, `DECLARES.EXPOSES` on type Symbol origins; describe rollup templates already prescribe those dot-keys.
88

tests/test_mcp_hints.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,35 @@ def test_hints_find_success_uses_first_result_id_when_multiple() -> None:
12251225
assert mcp_hints.TPL_FIND_SUCCESS_HANDLER.format(id=second) not in hints
12261226

12271227

1228+
def test_hints_find_symbol_success_emits_no_v4_followup() -> None:
1229+
sym_id = "sym:com.example.T"
1230+
payload = _find_success_payload("symbol", sym_id)
1231+
hints = generate_hints("find", payload)
1232+
v4_markers = (
1233+
mcp_hints.TPL_FIND_SUCCESS_HANDLER,
1234+
mcp_hints.TPL_FIND_SUCCESS_HTTP_TARGETS,
1235+
mcp_hints.TPL_FIND_SUCCESS_ASYNC_TARGETS,
1236+
)
1237+
assert not any(m.format(id=sym_id) in hints for m in v4_markers)
1238+
1239+
1240+
def test_hints_find_success_silent_when_first_result_missing_id() -> None:
1241+
payload = _find_success_payload("route", "route:unused")
1242+
payload["results"] = [{"kind": "route"}]
1243+
hints = generate_hints("find", payload)
1244+
assert not any("handler:" in h and "EXPOSES" in h for h in hints)
1245+
1246+
1247+
def test_hints_find_v2_route_success_emits_handler(kuzu_graph) -> None:
1248+
out = find_v2("route", {"path_prefix": "/api"}, graph=kuzu_graph, limit=500, offset=0)
1249+
assert out.success is True
1250+
assert out.results
1251+
rid = out.results[0].id
1252+
want = mcp_hints.TPL_FIND_SUCCESS_HANDLER.format(id=rid)
1253+
assert want in out.hints
1254+
assert mcp_hints.TPL_FIND_PAGE_FULL.format(limit=500) not in out.hints
1255+
1256+
12281257
def test_hints_find_page_full_requires_has_more_results_flag() -> None:
12291258
full_page = {
12301259
"success": True,

0 commit comments

Comments
 (0)