From 3e89a533eaf0e0ae317ed186a8977e6f9262e09e Mon Sep 17 00:00:00 2001 From: Dmitry Teryaev Date: Sat, 16 May 2026 14:32:27 +0300 Subject: [PATCH 1/2] add plan and cursor prompts for hints v2 Co-authored-by: Cursor --- plans/CURSOR-PROMPTS-HINTS-V2.md | 201 ++++++++++++++++++++++ plans/PLAN-HINTS-V2.md | 284 +++++++++++++++++++++++++++++++ 2 files changed, 485 insertions(+) create mode 100644 plans/CURSOR-PROMPTS-HINTS-V2.md create mode 100644 plans/PLAN-HINTS-V2.md diff --git a/plans/CURSOR-PROMPTS-HINTS-V2.md b/plans/CURSOR-PROMPTS-HINTS-V2.md new file mode 100644 index 0000000..92629e6 --- /dev/null +++ b/plans/CURSOR-PROMPTS-HINTS-V2.md @@ -0,0 +1,201 @@ +# Cursor task prompts — Hints v2 + +Status: **active (planning)**. Plan: +[`plans/PLAN-HINTS-V2.md`](./PLAN-HINTS-V2.md). Propose: +[`propose/HINTS-V2-PROPOSE.md`](../propose/HINTS-V2-PROPOSE.md). + +One prompt per PR. Copy the fenced **Prompt** block into Cursor agent mode with the +listed `@-files` attached. + +**Landing order:** PR-HINTS-V2-A → PR-HINTS-V2-B. Do not start PR-HINTS-V2-B until +PR-HINTS-V2-A is merged to `master`. + +**Universal rules:** + +- Use `.venv/bin/python` and `.venv/bin/ruff` only (repo venv). +- Nothing reachable from MCP tool handlers may write to **stdout** (`server.py` stdio rule). +- If ambiguous versus the plan, stop and ask — do not expand scope. +- Do not push git from the agent unless the user explicitly asked. + +--- + +## PR-HINTS-V2-A — `resolve` hints + +**Branch:** `feat/hints-v2-resolve` off `master`. +**Base:** `master`. +**Plan section:** [`plans/PLAN-HINTS-V2.md`](./PLAN-HINTS-V2.md) § PR-A. +**PR title:** `feat(hints): add hints field and rules to ResolveOutput` + +**Attach (`@-files`):** + +- `@plans/PLAN-HINTS-V2.md` (PR-A section + principles) +- `@propose/HINTS-V2-PROPOSE.md` (§3–§4, Appendix A, Decisions §7.14–§7.22) +- `@propose/completed/HINTS-ROAD-SIGNS-PROPOSE.md` (v1 cap/priority context only) +- `@mcp_hints.py` +- `@mcp_v2.py` (`ResolveOutput`, `resolve_v2`, parsers `_resolve_parse_*`) +- `@server.py` (`resolve` tool description) +- `@README.md` (MCP v2 response extras) +- `@tests/test_mcp_hints.py` + +**Prompt:** + +```` +You are implementing PR-HINTS-V2-A from `plans/PLAN-HINTS-V2.md` (the **PR-A** section). + +Read the PR-A **File-by-file changes** and **Tests for PR-A** before coding. If this +prompt and the plan disagree, the plan wins; the propose fills background and locked +template strings (Appendix A). + +## Scope + +1. **`mcp_v2.py`** — Add `resolved_identifier` and `hints` to `ResolveOutput` + (`extra="forbid"` preserved). On every `success=True` path set + `resolved_identifier` to the trimmed identifier. On `success=False` set + `hints=[]` and `resolved_identifier=None`. After assembling success output, build + hint payload (`status`, `resolved_identifier`, `candidates`, plumbed `hint_kind`, + optional `path_prefix_seed` / `target_service_seed` from existing parsers) and set + `hints=generate_hints("resolve", payload)`. +2. **`mcp_hints.py`** — Extend `generate_hints` for `output_kind == "resolve"` with + the four templates and rules in `propose/HINTS-V2-PROPOSE.md` Appendix A (120-char + drop-on-overflow, wildcard suppression, seed suppression). Use `PRIORITY_META`. +3. **`README.md`** — Document `resolve` `hints` + `resolved_identifier` under MCP v2 + response extras; link v2 propose. +4. **`server.py`** — Minimal `resolve` tool description mention of advisory `hints`. +5. **Tests** — Implement every `test_*` name listed under **Tests for PR-A** in + `plans/PLAN-HINTS-V2.md` (verbatim names). Assert hint presence via substrings, not + full-string equality. + +## Out of scope (do NOT touch) + +- `FUZZY_STRATEGY_SET`, neighbors fuzzy template (PR-HINTS-V2-B). +- `java_ontology.py` (except if you discover an unrelated typo — stop and ask). +- `build_ast_graph.py`, `ONTOLOGY_VERSION`, graph schema. +- Changes to v1 `search` / `find` / `describe` / `neighbors` hint catalogs. +- Per-candidate hints, `truncated` on `ResolveOutput`, structured `next_actions`. +- Special-casing `tests/bank-chat-system/` in production code. +- Drive-by refactors outside listed files. + +## Deliverables + +1. `ResolveOutput` exposes `hints` and `resolved_identifier` per contract. +2. Resolve hint rules wired; `status: one` and validation failures emit `hints: []`. +3. All PR-A named tests exist and pass. +4. README + server copy updated. + +## Tests to run (iteration loop) + +```bash +.venv/bin/ruff check mcp_hints.py mcp_v2.py tests/test_mcp_hints.py +.venv/bin/python -m pytest tests/test_mcp_hints.py -v -k "resolve" +``` + +Before PR open: + +```bash +.venv/bin/ruff check . +.venv/bin/python -m pytest tests -v +``` + +## Sentinel checks + +On `git diff master..HEAD`, these patterns should be **zero** matches outside allowed files: + +- `FUZZY_STRATEGY_SET` (PR-B only) +- `TPL_NEIGHBORS_FUZZY_STRATEGY` (PR-B only) +- `ONTOLOGY_VERSION` changes (not in this PR) + +## Definition of Done + +- [ ] PR-A plan definition of done satisfied. +- [ ] PR title: `feat(hints): add hints field and rules to ResolveOutput` +- [ ] PR body: scope, link to plan + propose, test commands run, note no re-index. +```` + +--- + +## PR-HINTS-V2-B — neighbors fuzzy-strategy hint + +**Branch:** `feat/hints-v2-neighbors-fuzzy` off `master` **after PR-HINTS-V2-A is merged**. +**Base:** `master` at merge commit of PR-HINTS-V2-A. +**Plan section:** [`plans/PLAN-HINTS-V2.md`](./PLAN-HINTS-V2.md) § PR-B. +**PR title:** `feat(hints): emit fuzzy-strategy hint when neighbors results carry brownfield/fallback edges` + +**Attach (`@-files`):** + +- `@plans/PLAN-HINTS-V2.md` (PR-B section + principles) +- `@propose/HINTS-V2-PROPOSE.md` (§3.2 fuzzy set, §4 UC6–UC11/UC17, Appendix A) +- `@java_ontology.py` +- `@mcp_hints.py` +- `@mcp_v2.py` (`neighbors_v2` payload shape — read only unless echo change needed) +- `@README.md` +- `@server.py` (optional neighbors description tweak) +- `@tests/test_mcp_hints.py` +- `@tests/conftest.py` (if round-trip needs fixture/session context) +- `@tests/test_call_graph_smoke_roundtrip.py` (reference for fuzzy `CALLS` strategies) + +**Prompt:** + +```` +You are implementing PR-HINTS-V2-B from `plans/PLAN-HINTS-V2.md` (the **PR-B** section). + +PR-HINTS-V2-A is already on `master` (`ResolveOutput.hints`, resolve catalog). Do not +re-land resolve work here. + +Read **Tests for PR-B** and propose §3.2 / Appendix A before coding. + +## Scope + +1. **`java_ontology.py`** — Add `FUZZY_STRATEGY_SET` frozenset with locked members from + the propose; export in `__all__`. +2. **`mcp_hints.py`** — Import set; add `TPL_NEIGHBORS_FUZZY_STRATEGY` and + `_any_fuzzy_strategy`; extend `neighbors` branch to append one meta hint when any + result edge has fuzzy `attrs.strategy`. Preserve existing empty-result hint (UC11). +3. **`README.md`** — Document neighbors fuzzy-strategy hint under MCP v2 extras. +4. **`server.py`** — Only if needed: brief mention that edge `attrs.strategy` indicates + resolution quality. +5. **Tests** — Implement all `test_*` names under **Tests for PR-B** in the plan + (verbatim). Craft payloads for unit tests; round-trip via `neighbors_v2` + graph + (discover fuzzy edge with Cypher — avoid unconditional skip). + +## Out of scope (do NOT touch) + +- `ResolveOutput`, resolve templates, `resolve_v2` plumbing (PR-A). +- Ontology version bump, `build_ast_graph.py`, re-index docs. +- Issue #147 CI grep invariant (unless user explicitly expanded scope). +- Per-row neighbors hints, confidence thresholds, v1 catalog changes beyond neighbors branch. +- Special-casing `tests/bank-chat-system/` in production code. + +## Deliverables + +1. `FUZZY_STRATEGY_SET` in ontology; neighbors fuzzy hint wired. +2. All PR-B named tests + round-trip pass. +3. README updated. + +## Tests to run (iteration loop) + +```bash +.venv/bin/ruff check java_ontology.py mcp_hints.py tests/test_mcp_hints.py +.venv/bin/python -m pytest tests/test_mcp_hints.py -v -k "neighbors and fuzzy or neighbors_empty" +``` + +Before PR open: + +```bash +.venv/bin/ruff check . +.venv/bin/python -m pytest tests -v +``` + +## Sentinel checks + +On `git diff master..HEAD`, these should be **zero** outside resolve-related files +(PR-A already merged — should not appear in this PR diff at all): + +- Changes to `ResolveOutput` fields beyond what master already has from PR-A +- `TPL_RESOLVE_*` additions (belong to PR-A only) + +## Definition of Done + +- [ ] PR-B plan definition of done satisfied. +- [ ] PR title: `feat(hints): emit fuzzy-strategy hint when neighbors results carry brownfield/fallback edges` +- [ ] PR body: scope, plan + propose links, test commands, no re-index callout. +```` diff --git a/plans/PLAN-HINTS-V2.md b/plans/PLAN-HINTS-V2.md new file mode 100644 index 0000000..9fe31c8 --- /dev/null +++ b/plans/PLAN-HINTS-V2.md @@ -0,0 +1,284 @@ +# Plan: Hints v2 (`resolve` + neighbors fuzzy-strategy) + +Status: **active (planning)**. This plan implements +[`propose/HINTS-V2-PROPOSE.md`](../propose/HINTS-V2-PROPOSE.md). + +Depends on: **none** (v1 hints on `search` / `find` / `describe` / `neighbors` and the +`resolve` tool are already landed on `master`). + +## Goal + +- **PR-A:** Add `hints: list[str]` and `resolved_identifier: str | None` to + `ResolveOutput`; implement the four locked resolve templates in `mcp_hints.py`; + wire `resolve_v2` so success-true responses populate echoed fields and hint payload + (including route/client seeds); cover every resolve use case in §4 with named tests + plus one `resolve_v2` round-trip. +- **PR-B:** Add `FUZZY_STRATEGY_SET` to `java_ontology.py`; extend the `neighbors` + branch of `generate_hints` to emit one meta-tier hint when any result edge carries a + fuzzy `attrs.strategy`; cover UC6–UC10 and UC17 with named tests plus one + `neighbors_v2` round-trip on a graph that exposes a fuzzy edge. + +## Principles (do not relitigate in review) + +- **Add rules; do not change v1 shape.** Existing four outputs keep their `hints` + contract. v2 only adds `ResolveOutput` fields and new catalog rows. +- **Strategy over confidence.** Neighbors fuzzy signal uses categorical + `attrs.strategy ∈ FUZZY_STRATEGY_SET`, not a confidence threshold. +- **Cap discipline unchanged.** ≤5 hints, dedupe by rendered string, v1 §7.12 priority + (v2 rows are **meta-tier**, lowest). +- **No per-result hints on `neighbors`.** One fuzzy-strategy hint per output max; + agents read per-row `attrs.strategy` in the same payload. +- **Pure hint generation.** `generate_hints` reads only its payload dict (echoed output + fields + call-site plumbing); no graph I/O, no LLM. +- **120-char drop-on-overflow.** No truncation or ellipsis on resolve templates; overlong + rendered strings are omitted (UC2b). +- **Concrete filter seeds only.** Route/client `status: none` hints require non-empty + `path_prefix_seed` / `target_service_seed`; suppress when parsing yields nothing. +- **Additive for clients.** No deprecation shims; clients that ignore `hints` are + unchanged. +- **No ontology / re-index.** Query-time MCP surface only; `ontology_version` stays + **13**. + +## PR breakdown — overview + +| PR | Scope | Ontology bump | Areas of concern | Test buckets | Independent of | +| --- | --- | --- | --- | --- | --- | +| PR-A | `ResolveOutput` + resolve catalog + `resolve_v2` plumbing (`resolved_identifier`, seeds, `hints`) | **No** | Payload plumbing vs `find_v2` hybrid; wildcard/overflow suppression; `success=False` vs validation-`none`; template string drift vs real `search`/`find` params | Resolve unit payloads (§4 UC1–UC5, UC16), `generate_hints("resolve", …)`, `resolve_v2` round-trip | PR-B | +| PR-B | `FUZZY_STRATEGY_SET` in `java_ontology.py`, neighbors fuzzy template + `_any_fuzzy_strategy`, README/server copy | **No** | Set drift vs brownfield pipeline (issue #147); empty-results vs fuzzy both meta-tier; `attrs` shape on `Edge` dumps | Crafted neighbors payloads (UC6–UC10, UC17), regression UC11, `neighbors_v2` round-trip | PR-A | + +**Landing order:** **PR-A → PR-B**. + +## Resolved design decisions + +| Topic | Decision | +| --- | --- | +| `resolved_identifier` | Set on every `success=True` response to post-validation trimmed identifier; `None` on `success=False`. | +| Resolve hints fire on | `status: none` and `status: many` only; `status: one` → `hints: []`. | +| `hint_kind` default | `None` → symbol branch (`search(query=…)` template). | +| Wildcards in identifier | `*` / `?` in `resolved_identifier` suppress resolve hints (UC2c); do not emit `search(query='*')`. | +| `FUZZY_STRATEGY_SET` location | `java_ontology.py`; `mcp_hints.py` imports it. Locked members per propose §3.2. | +| `layer_b_ann` vs `layer_b_fqn` | `_ann` is primary (not in fuzzy set); `_fqn` is fuzzy. | +| Truncated candidate cap | Hint says `{n} candidates` with `n = len(candidates)`; no `truncated` flag on output (§5 carve-out). | +| Issue #147 CI invariant | **Out of scope for this plan** unless already on `master`; land separately or as a small follow-up chore PR that classifies every `resolution_strategy=` literal against ontology sets. | +| v1 catalog | Unchanged except new rows; locked strings in propose Appendix A. | + +--- + +# PR-A — `resolve` hints + +## File-by-file changes + +### 1. `mcp_v2.py` + +- Extend `ResolveOutput` with: + - `resolved_identifier: str | None = None` — post-validation trimmed identifier; + `None` when `success=False`. + - `hints: list[str] = Field(default_factory=list, description=MCP_HINTS_FIELD_DESCRIPTION)`. +- Preserve `extra="forbid"`. +- Refactor success-path assembly so every `success=True` output sets + `resolved_identifier` to the trimmed request identifier (including `status: none` + from wildcards and no-match). +- After building the success output, compute hint payload and assign `hints`: + - Echo: `status`, `resolved_identifier`, `candidates`. + - Plumbed (not on model): `hint_kind`, optional `path_prefix_seed`, + `target_service_seed`. + - Seeds: reuse `_resolve_parse_route_method_path` (path from method+path identifier) + and `_resolve_parse_microservice_route` / client parsers consistent with + `_resolve_client_candidates` (microservice token or `target path` split) — match + propose §3.1.2 semantics; parser logic stays here, not in `mcp_hints.py`. + - Call `generate_hints("resolve", payload)` only when `success=True`. +- `success=False` paths (validation error, exceptions): `hints=[]`, + `resolved_identifier=None` (Decision §7.15 — validation rejection gets no v2 hint). + +### 2. `mcp_hints.py` + +- Extend `generate_hints` `output_kind` `Literal` to include `"resolve"`. +- Add verbatim templates and constants from propose Appendix A: + `TPL_RESOLVE_NONE_TRY_SEARCH`, `TPL_RESOLVE_NONE_TRY_FIND_ROUTE`, + `TPL_RESOLVE_NONE_TRY_FIND_CLIENT`, `TPL_RESOLVE_MANY_TIGHTEN`, + `_RESOLVE_HINT_MAX_CHARS = 120`, `_RESOLVE_WILDCARDS`. +- Implement `output_kind == "resolve"` branch per propose Appendix A wire-up + (`status: one` → `[]`; `many` → tighten when `len(candidates) > 1`; `none` → + kind-specific templates with suppression rules). +- All v2 resolve hints use `PRIORITY_META`. + +### 3. `server.py` (minimal) + +- Update `resolve` tool `description=` to mention successful responses may include + advisory `hints` (same tone as other v2 tools). No stdout changes. + +### 4. `README.md` + +- Extend MCP v2 **response extras** paragraph: `resolve` returns `hints` on success; + document `resolved_identifier` echo; link to + [`propose/HINTS-V2-PROPOSE.md`](../propose/HINTS-V2-PROPOSE.md) for v2 catalog + (keep v1 link for the original four tools). + +## Tests for PR-A + +Pure `generate_hints("resolve", …)` tests (craft payloads; no DB required unless noted): + +1. `test_hints_resolve_status_one_emits_empty` — UC1 +2. `test_hints_resolve_status_none_symbol_suggests_search` — UC2 +3. `test_hints_resolve_status_none_symbol_drop_on_overflow` — UC2b (identifier length + so rendered hint > 120) +4. `test_hints_resolve_status_none_symbol_wildcard_suppressed` — UC2c +5. `test_hints_resolve_status_none_route_suggests_find` — UC3 +6. `test_hints_resolve_status_none_route_no_seed_suppressed` — UC3b +7. `test_hints_resolve_status_none_client_suggests_find` — UC4 +8. `test_hints_resolve_status_none_client_no_seed_suppressed` — UC4b +9. `test_hints_resolve_status_many_emits_tighten` — UC5 / UC16 (`n` in substring) +10. `test_hints_resolve_status_many_truncated_cap_wording` — UC16b (`n=10` wording) +11. `test_hints_resolve_payload_missing_identifier_suppressed` — UC16c + +Integration: + +12. `test_hints_resolve_v2_round_trip` — `resolve_v2` against `kuzu_graph`: assert + `hints` present for a known `status: none` symbol identifier and empty for a known + `status: one`; assert `resolved_identifier` echoed on success; assert validation + failure yields `hints == []` and `resolved_identifier is None`. + +Optional hygiene (same PR if quick): + +13. `test_hints_resolve_templates_rendered_length_leq_120` — parametrize resolve + templates with realistic placeholders. + +**Assertion style:** presence + key substrings (e.g. `search(query=`, `find(kind='route'`, +`candidates — tighten`), not whole-string equality — per propose Risk §8. + +## Definition of done (PR-A) + +- [ ] `ResolveOutput` exposes `hints` and `resolved_identifier` per contract. +- [ ] All four resolve rules implemented; `status: one` and validation-failure paths + emit `hints: []`. +- [ ] Named tests above exist and pass. +- [ ] README + `server.py` resolve copy mention `hints`. +- [ ] `.venv/bin/ruff check .` and `.venv/bin/python -m pytest tests -v` green. + +## Implementation step list + +| # | Step | File(s) | Done when | +| --- | --- | --- | --- | +| 1 | Add model fields | `mcp_v2.py` | Pydantic schema validates | +| 2 | Implement resolve branch + templates | `mcp_hints.py` | Unit tests 1–11 pass | +| 3 | Wire `resolve_v2` payload + `resolved_identifier` | `mcp_v2.py` | Round-trip test passes | +| 4 | Docs / server description | `README.md`, `server.py` | Copy matches behavior | +| 5 | Add tests | `tests/test_mcp_hints.py` | All PR-A tests green | + +--- + +# PR-B — neighbors fuzzy-strategy hint + +## File-by-file changes + +### 1. `java_ontology.py` + +- Add `FUZZY_STRATEGY_SET` frozenset (locked contents per propose §3.2): + `layer_c_source`, `layer_b_fqn`, `phantom`, `chained_receiver`, + `overload_ambiguous`, `implicit_super`. +- Export in `__all__`. + +### 2. `mcp_hints.py` + +- Import `FUZZY_STRATEGY_SET` from `java_ontology`. +- Add `TPL_NEIGHBORS_FUZZY_STRATEGY` (verbatim propose Appendix A). +- Add `_any_fuzzy_strategy(edges: list[dict[str, Any]]) -> bool` inspecting + `edge["attrs"]["strategy"]`. +- Extend existing `neighbors` branch: when `results` non-empty and any fuzzy strategy + present, append one `PRIORITY_META` fuzzy hint (after empty-result check; UC11 + unchanged when `results` empty). +- Update module docstring to reference v2 propose for resolve + neighbors additions. + +### 3. `README.md` + +- Extend MCP v2 hints paragraph: neighbors may emit fuzzy-strategy meta hint; point to + v2 propose for catalog detail. + +### 4. `server.py` (optional, minimal) + +- If `neighbors` tool description does not already mention strategy attrs, add one line + that `attrs.strategy` on edges indicates resolution quality (no new tool params). + +## Tests for PR-B + +Pure `generate_hints("neighbors", …)` (craft `results` with `attrs.strategy`): + +1. `test_hints_neighbors_fuzzy_strategy_layer_c_source_emits` — UC6 +2. `test_hints_neighbors_fuzzy_strategy_annotation_absent` — UC7 +3. `test_hints_neighbors_fuzzy_strategy_calls_phantom_emits` — UC8 +4. `test_hints_neighbors_declares_no_strategy_attrs_empty` — UC9 +5. `test_hints_neighbors_multi_origin_fuzzy_emits_once` — UC10 (single hint string) +6. `test_hints_neighbors_layer_a_meta_no_fuzzy_hint` — UC17 +7. `test_hints_neighbors_empty_with_edge_types_still_emits_kind_check` — UC11 regression + (may already exist as `test_hints_neighbors_empty_with_edge_types_emits_kind_check` — + extend or duplicate only if PR-B changes branch ordering) + +Integration: + +8. `test_hints_neighbors_fuzzy_strategy_neighbors_v2_round_trip` — call `neighbors_v2` + on a graph edge known to carry a fuzzy `strategy` (discover via Cypher helper over + `kuzu_graph` or Tier-2 `call_graph_smoke` session); assert fuzzy hint substring in + `out.hints`. Prefer **no** unconditional `pytest.skip` — use a helper that fails with + a clear message if the chosen fixture lacks fuzzy edges. + +## Definition of done (PR-B) + +- [ ] `FUZZY_STRATEGY_SET` lives in `java_ontology.py` and is imported by `mcp_hints.py`. +- [ ] Fuzzy hint fires at most once per neighbors output; coexists with empty-result hint + only on disjoint conditions. +- [ ] Named tests + round-trip pass. +- [ ] README updated; v1 neighbors behavior unchanged when no fuzzy strategies. +- [ ] `ruff` + default `pytest tests -v` green. + +## Implementation step list + +| # | Step | File(s) | Done when | +| --- | --- | --- | --- | +| 1 | Add ontology set | `java_ontology.py` | Importable constant | +| 2 | Template + `_any_fuzzy_strategy` + neighbors branch | `mcp_hints.py` | Unit tests 1–7 pass | +| 3 | Round-trip neighbors test | `tests/test_mcp_hints.py` | Test 8 passes | +| 4 | README / optional server | `README.md`, `server.py` | Docs match | + +--- + +# Cross-PR risks and mitigations + +| # | Risk | Severity | Mitigation | +| --- | --- | --- | --- | +| 1 | Resolve hint duplicates generic `message` prose | Low | Intentional dual channel; hint embeds verbatim `resolved_identifier` (Decision §7.22). Tests use substrings, not full equality. | +| 2 | Missing payload plumbing → silent `hints: []` | Medium | `test_hints_resolve_payload_missing_identifier_suppressed` + round-trip asserts `resolved_identifier` on success. | +| 3 | Fuzzy hint noise in brownfield-heavy repos | Low | Meta-tier drops first under cap; single terse template. | +| 4 | `FUZZY_STRATEGY_SET` drifts from pipeline literals | Medium | Issue #147 follow-up; document locked set in ontology; code review checks new strategies. | +| 5 | PR-B merged before PR-A | Low | Enforce landing order; PR-B does not touch `ResolveOutput`. | +| 6 | Template/param drift (`search`, `find`, `neighbors`) | Medium | Reuse existing v1 patterns; round-trip tests import tool signatures where v1 already does. | + +# Out of scope + +- Ontology version bump or re-index. +- Per-row hints on `neighbors` or `ResolveCandidate`. +- Confidence-threshold hints; distinguishing `phantom` vs `layer_c_source` in rendered text. +- `truncated: bool` on `ResolveOutput`. +- Hints for `status: none` from validation rejection (`success=False`). +- Changes to v1 search/find/describe catalog rows (except neighbors branch extension in PR-B). +- Structured `next_actions`, `hints_version`, LLM-generated hints. +- Issue #147 CI classification invariant (unless explicitly added as a separate PR). +- Special-casing `tests/bank-chat-system/` in production hint logic. + +# Whole-plan done definition + +1. `resolve` success responses expose `hints` + `resolved_identifier` per propose §3.1; + resolve catalog covered by named tests and round-trip. +2. `neighbors` emits the fuzzy-strategy meta hint when any result edge has + `attrs.strategy ∈ FUZZY_STRATEGY_SET`; ontology set is the single source of truth. +3. README documents v2 behavior; v1 hints on the original four tools are unchanged. +4. Default test suite green without heavy env vars. + +# Tracking + +- `PR-A`: _pending_ +- `PR-B`: _pending_ + +## Cursor handoff + +Per-PR execution prompts: +[`plans/CURSOR-PROMPTS-HINTS-V2.md`](CURSOR-PROMPTS-HINTS-V2.md). From cff62ffc91db555851be5c1648e5b427131ef2dc Mon Sep 17 00:00:00 2001 From: Dmitry Teryaev Date: Sat, 16 May 2026 14:44:14 +0300 Subject: [PATCH 2/2] address pr 148 review on hints v2 plan Co-authored-by: Cursor --- plans/CURSOR-PROMPTS-HINTS-V2.md | 28 +++++++------ plans/PLAN-HINTS-V2.md | 67 +++++++++++++++++++------------- propose/HINTS-V2-PROPOSE.md | 2 +- 3 files changed, 58 insertions(+), 39 deletions(-) diff --git a/plans/CURSOR-PROMPTS-HINTS-V2.md b/plans/CURSOR-PROMPTS-HINTS-V2.md index 92629e6..2d590fc 100644 --- a/plans/CURSOR-PROMPTS-HINTS-V2.md +++ b/plans/CURSOR-PROMPTS-HINTS-V2.md @@ -49,12 +49,13 @@ template strings (Appendix A). ## Scope 1. **`mcp_v2.py`** — Add `resolved_identifier` and `hints` to `ResolveOutput` - (`extra="forbid"` preserved). On every `success=True` path set - `resolved_identifier` to the trimmed identifier. On `success=False` set - `hints=[]` and `resolved_identifier=None`. After assembling success output, build - hint payload (`status`, `resolved_identifier`, `candidates`, plumbed `hint_kind`, - optional `path_prefix_seed` / `target_service_seed` from existing parsers) and set - `hints=generate_hints("resolve", payload)`. + (`extra="forbid"` preserved). **Refactor to a unified success assembler** so wildcard + early-return (`*` / `?`) no longer bypasses echo/hint wiring — today it calls + `_resolve_build_output([])` directly. Every `success=True` path must set + `resolved_identifier=trimmed`, build payload with `hint_kind` plus parser-derived + seeds (`path_prefix_seed`, `target_service_seed` when applicable), and set + `hints=generate_hints("resolve", payload)` (wildcard → `hints: []` via suppression, + UC2c). On `success=False`: `hints=[]`, `resolved_identifier=None`. 2. **`mcp_hints.py`** — Extend `generate_hints` for `output_kind == "resolve"` with the four templates and rules in `propose/HINTS-V2-PROPOSE.md` Appendix A (120-char drop-on-overflow, wildcard suppression, seed suppression). Use `PRIORITY_META`. @@ -62,8 +63,9 @@ template strings (Appendix A). response extras; link v2 propose. 4. **`server.py`** — Minimal `resolve` tool description mention of advisory `hints`. 5. **Tests** — Implement every `test_*` name listed under **Tests for PR-A** in - `plans/PLAN-HINTS-V2.md` (verbatim names). Assert hint presence via substrings, not - full-string equality. + `plans/PLAN-HINTS-V2.md` (verbatim names). Reuse graph-discovery helpers in + `tests/test_mcp_hints.py` for round-trip (fail loud, no unconditional skip). Assert + hint presence via substrings, not full-string equality. ## Out of scope (do NOT touch) @@ -153,9 +155,11 @@ Read **Tests for PR-B** and propose §3.2 / Appendix A before coding. 3. **`README.md`** — Document neighbors fuzzy-strategy hint under MCP v2 extras. 4. **`server.py`** — Only if needed: brief mention that edge `attrs.strategy` indicates resolution quality. -5. **Tests** — Implement all `test_*` names under **Tests for PR-B** in the plan - (verbatim). Craft payloads for unit tests; round-trip via `neighbors_v2` + graph - (discover fuzzy edge with Cypher — avoid unconditional skip). +5. **Tests** — Implement all **new** `test_*` names under **Tests for PR-B** in the plan + (verbatim). Do **not** add a duplicate UC11 test — existing + `test_hints_neighbors_empty_with_edge_types_emits_kind_check` covers empty neighbors. + Craft payloads for unit tests; round-trip via `neighbors_v2` + graph using helpers in + `tests/test_mcp_hints.py` or Tier-2 `call_graph_smoke` (fail loud, no skip). ## Out of scope (do NOT touch) @@ -175,7 +179,7 @@ Read **Tests for PR-B** and propose §3.2 / Appendix A before coding. ```bash .venv/bin/ruff check java_ontology.py mcp_hints.py tests/test_mcp_hints.py -.venv/bin/python -m pytest tests/test_mcp_hints.py -v -k "neighbors and fuzzy or neighbors_empty" +.venv/bin/python -m pytest tests/test_mcp_hints.py -v -k "fuzzy_strategy or neighbors_empty" ``` Before PR open: diff --git a/plans/PLAN-HINTS-V2.md b/plans/PLAN-HINTS-V2.md index 9fe31c8..b1faef8 100644 --- a/plans/PLAN-HINTS-V2.md +++ b/plans/PLAN-HINTS-V2.md @@ -55,7 +55,7 @@ Depends on: **none** (v1 hints on `search` / `find` / `describe` / `neighbors` a | `resolved_identifier` | Set on every `success=True` response to post-validation trimmed identifier; `None` on `success=False`. | | Resolve hints fire on | `status: none` and `status: many` only; `status: one` → `hints: []`. | | `hint_kind` default | `None` → symbol branch (`search(query=…)` template). | -| Wildcards in identifier | `*` / `?` in `resolved_identifier` suppress resolve hints (UC2c); do not emit `search(query='*')`. | +| Wildcards in identifier | `*` / `?` in `resolved_identifier` suppress resolve hints (UC2c); do not emit `search(query='*')`. Unified success assembler sets echo + runs `generate_hints` on wildcard paths too. | | `FUZZY_STRATEGY_SET` location | `java_ontology.py`; `mcp_hints.py` imports it. Locked members per propose §3.2. | | `layer_b_ann` vs `layer_b_fqn` | `_ann` is primary (not in fuzzy set); `_fqn` is fuzzy. | | Truncated candidate cap | Hint says `{n} candidates` with `n = len(candidates)`; no `truncated` flag on output (§5 carve-out). | @@ -75,18 +75,24 @@ Depends on: **none** (v1 hints on `search` / `find` / `describe` / `neighbors` a `None` when `success=False`. - `hints: list[str] = Field(default_factory=list, description=MCP_HINTS_FIELD_DESCRIPTION)`. - Preserve `extra="forbid"`. -- Refactor success-path assembly so every `success=True` output sets - `resolved_identifier` to the trimmed request identifier (including `status: none` - from wildcards and no-match). -- After building the success output, compute hint payload and assign `hints`: +- **Refactor: unified success assembler** — Today `resolve_v2` wildcard identifiers + (`*` / `?`) early-return via `_resolve_build_output([])` and never set echo fields. + Replace scattered success returns with one helper (e.g. + `_resolve_finalize_success(trimmed, hint_kind, matches, graph) -> ResolveOutput`) that: + 1. Builds `status` / `node` / `candidates` / `message` (same semantics as today). + 2. Always sets `resolved_identifier=trimmed` on `success=True` (wildcard, empty-match, + one, many — all paths). + 3. Builds the hint payload and sets `hints=generate_hints("resolve", payload)`. + Wildcard `status: none` must **not** bypass the assembler; hints stay `[]` via + `generate_hints` suppression (UC2c), not a separate shortcut. +- **Hint payload on every success path** (including wildcard and empty-match): - Echo: `status`, `resolved_identifier`, `candidates`. - - Plumbed (not on model): `hint_kind`, optional `path_prefix_seed`, - `target_service_seed`. - - Seeds: reuse `_resolve_parse_route_method_path` (path from method+path identifier) - and `_resolve_parse_microservice_route` / client parsers consistent with - `_resolve_client_candidates` (microservice token or `target path` split) — match - propose §3.1.2 semantics; parser logic stays here, not in `mcp_hints.py`. - - Call `generate_hints("resolve", payload)` only when `success=True`. + - Plumbed (not on model): `hint_kind` (request param — today only used for search + routing), optional `path_prefix_seed`, `target_service_seed`. + - Seeds: compute on every success return using existing parsers + (`_resolve_parse_route_method_path`, `_resolve_parse_microservice_route`, and client + `target` / `target path` split consistent with `_resolve_client_candidates`) — match + propose §3.1.2; parser logic stays here, not in `mcp_hints.py`. - `success=False` paths (validation error, exceptions): `hints=[]`, `resolved_identifier=None` (Decision §7.15 — validation rejection gets no v2 hint). @@ -135,8 +141,11 @@ Integration: 12. `test_hints_resolve_v2_round_trip` — `resolve_v2` against `kuzu_graph`: assert `hints` present for a known `status: none` symbol identifier and empty for a known - `status: one`; assert `resolved_identifier` echoed on success; assert validation - failure yields `hints == []` and `resolved_identifier is None`. + `status: one`; assert `resolved_identifier` echoed on success (including wildcard + `status: none` with `hints == []`); assert validation failure yields `hints == []` + and `resolved_identifier is None`. Reuse existing discovery helpers in + `tests/test_mcp_hints.py` (`_route_id`, `_client_id`, symbol/route Cypher patterns) + — fail loud with `pytest.fail` if fixture data missing; no unconditional `skip`. Optional hygiene (same PR if quick): @@ -161,7 +170,7 @@ Optional hygiene (same PR if quick): | --- | --- | --- | --- | | 1 | Add model fields | `mcp_v2.py` | Pydantic schema validates | | 2 | Implement resolve branch + templates | `mcp_hints.py` | Unit tests 1–11 pass | -| 3 | Wire `resolve_v2` payload + `resolved_identifier` | `mcp_v2.py` | Round-trip test passes | +| 3 | Add unified success assembler; thread `trimmed`, `hint_kind`, seeds into payload on every `success=True` path (wildcard included) | `mcp_v2.py` | Wildcard + lookup paths echo `resolved_identifier`; round-trip passes | | 4 | Docs / server description | `README.md`, `server.py` | Copy matches behavior | | 5 | Add tests | `tests/test_mcp_hints.py` | All PR-A tests green | @@ -209,17 +218,19 @@ Pure `generate_hints("neighbors", …)` (craft `results` with `attrs.strategy`): 4. `test_hints_neighbors_declares_no_strategy_attrs_empty` — UC9 5. `test_hints_neighbors_multi_origin_fuzzy_emits_once` — UC10 (single hint string) 6. `test_hints_neighbors_layer_a_meta_no_fuzzy_hint` — UC17 -7. `test_hints_neighbors_empty_with_edge_types_still_emits_kind_check` — UC11 regression - (may already exist as `test_hints_neighbors_empty_with_edge_types_emits_kind_check` — - extend or duplicate only if PR-B changes branch ordering) + +**UC11 regression (existing v1 test — do not add a duplicate):** after PR-B, re-run +`test_hints_neighbors_empty_with_edge_types_emits_kind_check` unchanged; only touch it if +branch ordering breaks the empty-result path. Integration: -8. `test_hints_neighbors_fuzzy_strategy_neighbors_v2_round_trip` — call `neighbors_v2` - on a graph edge known to carry a fuzzy `strategy` (discover via Cypher helper over - `kuzu_graph` or Tier-2 `call_graph_smoke` session); assert fuzzy hint substring in - `out.hints`. Prefer **no** unconditional `pytest.skip` — use a helper that fails with - a clear message if the chosen fixture lacks fuzzy edges. +7. `test_hints_neighbors_fuzzy_strategy_neighbors_v2_round_trip` — call `neighbors_v2` + on a graph edge known to carry a fuzzy `strategy`. Reuse or extend helpers in + `tests/test_mcp_hints.py` (`_method_declares_client`, `_class_symbol_id`, Cypher + discovery for `e.strategy IN FUZZY_STRATEGY_SET`) or Tier-2 `call_graph_smoke` session + for `phantom` / `implicit_super` on `CALLS`. Fail loud if fixture lacks a fuzzy edge; + no unconditional `pytest.skip`. ## Definition of done (PR-B) @@ -235,8 +246,8 @@ Integration: | # | Step | File(s) | Done when | | --- | --- | --- | --- | | 1 | Add ontology set | `java_ontology.py` | Importable constant | -| 2 | Template + `_any_fuzzy_strategy` + neighbors branch | `mcp_hints.py` | Unit tests 1–7 pass | -| 3 | Round-trip neighbors test | `tests/test_mcp_hints.py` | Test 8 passes | +| 2 | Template + `_any_fuzzy_strategy` + neighbors branch | `mcp_hints.py` | Unit tests 1–6 pass | +| 3 | Round-trip neighbors test | `tests/test_mcp_hints.py` | Test 7 passes | | 4 | README / optional server | `README.md`, `server.py` | Docs match | --- @@ -246,7 +257,8 @@ Integration: | # | Risk | Severity | Mitigation | | --- | --- | --- | --- | | 1 | Resolve hint duplicates generic `message` prose | Low | Intentional dual channel; hint embeds verbatim `resolved_identifier` (Decision §7.22). Tests use substrings, not full equality. | -| 2 | Missing payload plumbing → silent `hints: []` | Medium | `test_hints_resolve_payload_missing_identifier_suppressed` + round-trip asserts `resolved_identifier` on success. | +| 2 | Missing payload plumbing → silent `hints: []` | Medium | Unified success assembler; `test_hints_resolve_payload_missing_identifier_suppressed` + round-trip asserts `resolved_identifier` on success (wildcard included). | +| 2b | Wildcard early-return bypasses assembler | Medium | PR-A refactor: no direct `_resolve_build_output([])` return without `resolved_identifier` + `generate_hints`. | | 3 | Fuzzy hint noise in brownfield-heavy repos | Low | Meta-tier drops first under cap; single terse template. | | 4 | `FUZZY_STRATEGY_SET` drifts from pipeline literals | Medium | Issue #147 follow-up; document locked set in ontology; code review checks new strategies. | | 5 | PR-B merged before PR-A | Low | Enforce landing order; PR-B does not touch `ResolveOutput`. | @@ -272,11 +284,14 @@ Integration: `attrs.strategy ∈ FUZZY_STRATEGY_SET`; ontology set is the single source of truth. 3. README documents v2 behavior; v1 hints on the original four tools are unchanged. 4. Default test suite green without heavy env vars. +5. `propose/HINTS-V2-PROPOSE.md` moved to `propose/completed/` (whole effort landed). # Tracking - `PR-A`: _pending_ - `PR-B`: _pending_ +- `#147` (strategy classification CI invariant): _pending_ chore — out of PR-A/B scope; + file after PR-B or in parallel. ## Cursor handoff diff --git a/propose/HINTS-V2-PROPOSE.md b/propose/HINTS-V2-PROPOSE.md index fa73459..9e26bc9 100644 --- a/propose/HINTS-V2-PROPOSE.md +++ b/propose/HINTS-V2-PROPOSE.md @@ -1,6 +1,6 @@ # HINTS-V2 — extend hints to `resolve` and to edge-attribute-driven `neighbors` signals -**Status**: draft +**Status**: approved (plan: [`plans/PLAN-HINTS-V2.md`](../plans/PLAN-HINTS-V2.md); move to `propose/completed/` when PR-A and PR-B land) **Author**: Dmitriy Teriaev + Perplexity Computer **Date**: 2026-05-16