diff --git a/docs/PROPOSES-ORDER.md b/docs/PROPOSES-ORDER.md index f1751ff..3c71fbb 100644 --- a/docs/PROPOSES-ORDER.md +++ b/docs/PROPOSES-ORDER.md @@ -11,9 +11,9 @@ 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` (propose merged via [#151](https://github.com/HumanBean17/java-codebase-rag/pull/151); treat as locked for sequencing) +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` ([#154](https://github.com/HumanBean17/java-codebase-rag/pull/154), draft) +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)) - Implementation = SCHEMA-V2 PR-D (same PR). No other proposes are in flight. @@ -25,29 +25,29 @@ No other proposes are in flight. ``` SCHEMA-V2-PROPOSE.md [merged #151 — locked for code sequence] ↓ -HINTS-V3-PROPOSE.md [draft PR #154 — SCHEMA-V2 Decision 30] +HINTS-V3-PROPOSE.md [merged #154 — implementing in PR-D] ``` **Decision 30 (SCHEMA-V2)**: `HINTS-V3-PROPOSE.md` must exist as a **merged draft propose** before SCHEMA-V2 **PR-A** implementation starts. That unblocks the four-code-PR sequence; it does **not** require HINTS-V3 to be `Status: locked` before PR-A. -**HINTS-V3 lock**: `Status: locked` is required before SCHEMA-V2 **PR-D** merges (see Phase 3). +**HINTS-V3 lock**: `Status: locked` before SCHEMA-V2 **PR-D** code merges (satisfied while Phase 3 is in flight; see propose headers). ### Phase 2 — plan + cursor-prompt artefacts ``` -plans/PLAN-SCHEMA-V2.md [not started] +plans/PLAN-SCHEMA-V2.md [landed #155] plans/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**. +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`). -By analogy: `PLAN-HINTS-V3.md` + `CURSOR-PROMPTS-HINTS-V3.md` are merge gates for **PR-D**. +By analogy: `PLAN-HINTS-V3.md` + `CURSOR-PROMPTS-HINTS-V3.md` are merge gates for **PR-D** (same PR). -Plans and prompts may be drafted in parallel with each other; each pair must land before its code PR. +Plans and prompts may be drafted in parallel with each other; each pair must land before its code PR. **Code PRs (Phase 3) are not started** until Phase 2 is on `master`. -### Phase 3 — code PRs (merge order) +### Phase 3 — code PRs (merge order) — **implementing** ``` PR-A feat(schema): EDGE_SCHEMA + docs/EDGE-NAVIGATION.md + ontology v14 diff --git a/plans/CURSOR-PROMPTS-HINTS-V3.md b/plans/CURSOR-PROMPTS-HINTS-V3.md new file mode 100644 index 0000000..20e1f59 --- /dev/null +++ b/plans/CURSOR-PROMPTS-HINTS-V3.md @@ -0,0 +1,119 @@ +# Cursor task prompts — HINTS-V3 + +Status: **active (implementing)**. Plan: +[`plans/PLAN-HINTS-V3.md`](./PLAN-HINTS-V3.md). Propose: +[`propose/HINTS-V3-PROPOSE.md`](../propose/HINTS-V3-PROPOSE.md). + +**Depends on:** SCHEMA-V2 **PR-A, PR-B, PR-C** merged to `master`. +**Propose lock:** Set `HINTS-V3-PROPOSE.md` `Status: locked` before opening the code PR. + +One prompt: **PR-D** (= SCHEMA-V2 PR-D in sequence doc). + +**Universal rules:** + +- Use `.venv/bin/python` and `.venv/bin/ruff` only. +- No stdout from MCP handlers. +- Do not expand scope beyond the plan. +- Do not push git unless the user asked. + +--- + +## PR-HINTS-V3-D — EDGE_SCHEMA-driven empty `neighbors` hints + +**Branch:** `feat/hints-v3-neighbors-empty` off `master` **after PR-SCHEMA-V2-C merged**. +**Base:** `master` at merge commit of PR-C. +**Plan section:** [`plans/PLAN-HINTS-V3.md`](./PLAN-HINTS-V3.md) § PR-D. +**PR title:** `feat(hints): kind- and direction-aware empty-result hints driven by EDGE_SCHEMA` + +**Attach (`@-files`):** + +- `@plans/PLAN-HINTS-V3.md` +- `@propose/HINTS-V3-PROPOSE.md` (§3–§4, §6, Decisions §7) +- `@propose/SCHEMA-V2-PROPOSE.md` (§3.12 preview — read only) +- `@java_ontology.py` (`EDGE_SCHEMA`, `FUZZY_STRATEGY_SET`) +- `@mcp_hints.py` +- `@mcp_v2.py` (`neighbors_v2`, `_load_node_record`) +- `@propose/completed/HINTS-V2-PROPOSE.md` (fuzzy hint — unchanged) +- `@propose/completed/HINTS-ROAD-SIGNS-PROPOSE.md` (priority cap context) +- `@README.md` +- `@server.py` (optional neighbors description) +- `@tests/test_mcp_hints.py` + +**Prompt:** + +```` +You are implementing PR-HINTS-V3-D from `plans/PLAN-HINTS-V3.md` (**PR-D**). + +SCHEMA-V2 PR-A/B/C are on `master`: post-flip `HTTP_CALLS` (Client→Route), `ASYNC_CALLS` (Producer→Route), `EDGE_SCHEMA` with 11 edges. + +Confirm `propose/HINTS-V3-PROPOSE.md` is **Status: locked** before merge. + +## Scope + +1. **`mcp_hints.py`** + - Delete `TPL_NEIGHBORS_EMPTY_KIND_CHECK`. + - Add four templates from propose §3.1 (verbatim strings). + - Implement `neighbors_empty_hints(...)` and `typical_traversal_for(...)` per propose §3.2–3.3. + - Import `EDGE_SCHEMA` from `java_ontology` — no edge-shape literals in this file (except tests). + - Wire `generate_hints("neighbors", …)`: empty `results` + non-empty `requested_edge_types` → structural hints; non-empty → keep v2 fuzzy path only. + - Post-filter: no dot-key edge labels in rendered hints. +2. **`mcp_v2.py`** — Extend neighbors hint payload: `requested_direction`, `origin_id`, `subject_record` from `_load_node_record` (§3.6). Multi-id: use first origin only. +3. **`java_ontology.py`** — Only if `member_only` missing from PR-A: add field + flags per propose §3.4. +4. **`README.md`** / **`server.py`** — Minimal neighbors-hints documentation. +5. **Tests** — Implement every `test_hints_hv*` name listed under **Tests for PR-D** in `plans/PLAN-HINTS-V3.md`, including **`test_hints_neighbors_v2_empty_post_flip_method_http_calls`** (required — session graph must be post-flip). Update/remove `test_hints_neighbors_empty_with_edge_types_emits_kind_check` to reflect v3 (rename if needed per plan). + +## Out of scope (do NOT touch) + +- `build_ast_graph.py`, graph DDL, pass5/6, `ONTOLOGY_VERSION`. +- `EDGE_SCHEMA` endpoint changes (already flipped in SCHEMA PRs). +- `RouteCaller`, `find_route_callers`, producer find beyond what tests need. +- v1 `describe`/`find`/`resolve` catalog rows (except neighbors empty branch). +- New MCP tool parameters. +- Per-row neighbors hints. + +## Deliverables + +1. Wrong-kind / wrong-direction / type-level empty queries emit schema-driven hints (HV table). +2. Brownfield-resolver absence hint on empty results when `brownfield_resolver_sourced` (HV4, HV13, HV14). +3. v2 fuzzy hint still fires on non-empty fuzzy edges (HV16). +4. HV19 coverage test for all `EDGE_SCHEMA` edges. +5. `TPL_NEIGHBORS_EMPTY_KIND_CHECK` fully removed. + +## Tests to run + +```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 "hints_hv or neighbors" +``` + +Before PR open: + +```bash +.venv/bin/ruff check . +.venv/bin/python -m pytest tests -v +``` + +## Sentinel checks (`git diff master..HEAD` — zero matches) + +- `ONTOLOGY_VERSION` changes +- `CREATE NODE TABLE Producer` / `HTTP_CALLS(FROM Symbol` in `build_ast_graph.py` +- `CallerInfo` reintroduction +- `TPL_NEIGHBORS_EMPTY_KIND_CHECK` (must be deleted, not kept) + +## Manual evidence (optional) + +On a graph built after SCHEMA PR-C: + +```bash +.venv/bin/python -c " +# Document one neighbors_v2 empty call returning WRONG_SUBJECT_KIND for method+HTTP_CALLS — paste JSON hints in PR body. +" +``` + +## Definition of Done + +- [ ] PR-D plan definition of done satisfied. +- [ ] HINTS-V3 propose **locked**. +- [ ] PR title: `feat(hints): kind- and direction-aware empty-result hints driven by EDGE_SCHEMA` +- [ ] PR body: scope, plan + propose links, test commands, **no re-index** (query-time only). +```` diff --git a/plans/CURSOR-PROMPTS-SCHEMA-V2.md b/plans/CURSOR-PROMPTS-SCHEMA-V2.md new file mode 100644 index 0000000..e095364 --- /dev/null +++ b/plans/CURSOR-PROMPTS-SCHEMA-V2.md @@ -0,0 +1,261 @@ +# Cursor task prompts — SCHEMA-V2 + +Status: **active (implementing)**. Plan: +[`plans/PLAN-SCHEMA-V2.md`](./PLAN-SCHEMA-V2.md). Propose: +[`propose/SCHEMA-V2-PROPOSE.md`](../propose/SCHEMA-V2-PROPOSE.md). Sequence: +[`docs/PROPOSES-ORDER.md`](../docs/PROPOSES-ORDER.md). + +One prompt per code PR (**PR-A / PR-B / PR-C**). PR-D (hints) is in +[`plans/CURSOR-PROMPTS-HINTS-V3.md`](./CURSOR-PROMPTS-HINTS-V3.md). + +**Landing order:** PR-SCHEMA-V2-A → PR-SCHEMA-V2-B → PR-SCHEMA-V2-C. Do not start the next PR until the previous is merged to `master`. + +**Universal rules:** + +- Use `.venv/bin/python` and `.venv/bin/ruff` only. +- Nothing reachable from MCP tool handlers may write to **stdout**. +- If ambiguous versus the plan, stop and ask — do not expand scope. +- Do not push git unless the user explicitly asked. +- Confirm [`propose/HINTS-V3-PROPOSE.md`](../propose/HINTS-V3-PROPOSE.md) is on `master` before starting **PR-A** implementation. + +--- + +## PR-SCHEMA-V2-A — `EDGE_SCHEMA` + v14 (no flips) + +**Branch:** `feat/schema-v2-edge-schema` off `master`. +**Base:** `master` (with HINTS-V3 propose merged). +**Plan section:** [`plans/PLAN-SCHEMA-V2.md`](./PLAN-SCHEMA-V2.md) § PR-A. +**PR title:** `feat(schema): add EDGE_SCHEMA to java_ontology, generate docs/EDGE-NAVIGATION.md, bump ontology to v14` + +**Attach (`@-files`):** + +- `@plans/PLAN-SCHEMA-V2.md` (PR-A only) +- `@propose/SCHEMA-V2-PROPOSE.md` (§3.1, §3.5–§3.6, §6 PR-A, Appendix A, Decisions 6–9, 28–29, 31) +- `@propose/HINTS-V3-PROPOSE.md` (§3.4–§3.5 `member_only` / `typical_traversals` — read only) +- `@java_ontology.py` +- `@build_ast_graph.py` (DDL constants — do not flip endpoints) +- `@ast_java.py` +- `@kuzu_queries.py` (version gate ~326) +- `@README.md` (Re-index section) +- `@docs/AGENT-GUIDE.md` + +**Prompt:** + +```` +You are implementing PR-SCHEMA-V2-A from `plans/PLAN-SCHEMA-V2.md` (the **PR-A** section). + +Read PR-A **File-by-file changes** and **Tests for PR-A** before coding. Plan wins over this prompt; propose supplies locked shapes. + +## Scope + +1. **`java_ontology.py`** — `EdgeAttr`, `EdgeSpec`, `EDGE_SCHEMA` for **10 edges** with **pre-flip** `HTTP_CALLS`/`ASYNC_CALLS` (`Symbol→Route`). Include `brownfield_resolver_sourced`; **`typical_traversals: dict[str, str]`** per HINTS-V3 §3.5 (SCHEMA propose Appendix A tuples are illustrative only). **`member_only`:** set `True` on `DECLARES_CLIENT`, `EXPOSES`, `OVERRIDES`, `CALLS` only — **not** `DECLARES_PRODUCER` (PR-C). Add `BROWNFIELD_RESOLVER_STRATEGY_SET` (union of `FUZZY_STRATEGY_SET` + resolver strategies used on edges today); **enumerate every member in the PR body**. +2. **`scripts/generate_edge_navigation.py`** + committed **`docs/EDGE-NAVIGATION.md`** with `--check` mode. +3. **`ast_java.py`** — `ONTOLOGY_VERSION = 14`. +4. **`README.md`** + **`docs/AGENT-GUIDE.md`** — v14 re-index callout (PR-B/C consequences one sentence each). +5. **Tests** — Every `test_*` name under **Tests for PR-A** in the plan (verbatim). New files: `tests/test_schema_consistency.py`, `tests/test_edge_navigation_doc.py`. +6. **CI** — Run generator `--check` in `.github/workflows/test.yml` (or equivalent documented hook). + +## Out of scope (do NOT touch) + +- Flipping `HTTP_CALLS` / `ASYNC_CALLS` endpoints; `Producer` node; `DECLARES_PRODUCER`. +- `mcp_hints.py`, `neighbors_empty_hints`, hints v3 templates. +- `CallerInfo` / `RouteCaller`, `find_route_callers` Cypher changes. +- `HttpCallRow` / pass6 emit changes. +- `mcp_v2.py` producer `kind` (PR-C). +- Drive-by refactors outside listed files. + +## Deliverables + +1. `EDGE_SCHEMA` is source of truth for 10 edges; DDL consistency tests pass. +2. Generated edge navigation doc + CI check. +3. Ontology 14 documented; v13 graphs refused by gate test. +4. All PR-A named tests pass. + +## Tests to run + +```bash +.venv/bin/ruff check java_ontology.py build_ast_graph.py scripts/generate_edge_navigation.py tests/test_schema_consistency.py tests/test_edge_navigation_doc.py +.venv/bin/python -m pytest tests/test_schema_consistency.py tests/test_edge_navigation_doc.py tests/test_kuzu_queries.py -v -k "ontology_version or stale" +.venv/bin/python scripts/generate_edge_navigation.py --check +``` + +Before PR open: + +```bash +.venv/bin/ruff check . +.venv/bin/python -m pytest tests -v +``` + +## Sentinel checks (`git diff master..HEAD` — zero matches outside PR-A scope) + +- `Producer` / `DECLARES_PRODUCER` / `FROM Producer` in `build_ast_graph.py` +- `RouteCaller` / `caller_node_kind` +- `TPL_NEIGHBORS_WRONG_SUBJECT_KIND` (hints PR-D) +- `neighbors_empty_hints` +- `FROM Client TO Route` on `HTTP_CALLS` DDL (flip is PR-B) + +## Definition of Done + +- [ ] PR-A plan definition of done satisfied. +- [ ] PR title matches plan. +- [ ] PR body: scope, links to plan + propose, test commands, **re-index required (v14)**. +```` + +--- + +## PR-SCHEMA-V2-B — `HTTP_CALLS` Client → Route + +**Branch:** `feat/schema-v2-http-calls-client-route` off `master` **after PR-A merged**. +**Base:** `master` at merge commit of PR-A. +**Plan section:** [`plans/PLAN-SCHEMA-V2.md`](./PLAN-SCHEMA-V2.md) § PR-B. +**PR title:** `feat(schema): HTTP_CALLS originates from Client, not Symbol` + +**Attach (`@-files`):** + +- `@plans/PLAN-SCHEMA-V2.md` (PR-B) +- `@propose/SCHEMA-V2-PROPOSE.md` (§3.3–§3.4, §3.7, §3.10, §4 HTTP UCs, PR-B §6) +- `@java_ontology.py` +- `@build_ast_graph.py` +- `@kuzu_queries.py` +- `@pr_analysis.py` +- `@mcp_v2.py` +- `@server.py` +- `@tests/test_call_edges_e2e.py` +- `@tests/test_kuzu_queries.py` +- `@tests/test_pr_analysis.py` +- `@tests/test_brownfield_clients.py` +- `@tests/test_mcp_v2.py` +- `@tests/test_mcp_v2_compose.py` +- `@tests/test_client_hint_recovery.py` + +**Prompt:** + +```` +You are implementing PR-SCHEMA-V2-B from `plans/PLAN-SCHEMA-V2.md` (**PR-B**). + +PR-A is on `master` (`EDGE_SCHEMA`, ontology 14, doc generator). Do not re-land PR-A work. + +## Scope + +1. Flip **`HTTP_CALLS`** to `Client→Route` in `EDGE_SCHEMA`, Kuzu DDL, pass5/6 emission, and **every** Python `grep HTTP_CALLS` site. +2. **`HttpCallRow`**: key edges by `client_id`, not `symbol_id`. +3. **`kuzu_queries.py`**: remove `CallerInfo`; add **`RouteCaller`**; reshape `find_route_callers`, `trace_request_flow` inbound, impact-analysis expansion (§3.7). +4. **`pr_analysis.py`**: two-hop HTTP route reachability. +5. **`mcp_v2.py` / `server.py`**: output types and descriptions aligned with `RouteCaller`. +6. **HTTP doc sweep** — `README.md`, `docs/AGENT-GUIDE.md`, `docs/skills/java-codebase-explore.md`; regenerate `docs/EDGE-NAVIGATION.md`. +7. **Tests** — All **Tests for PR-B** names in the plan (verbatim). + +## Out of scope (do NOT touch) + +- `Producer`, `ASYNC_CALLS` flip, `DECLARES_PRODUCER` (PR-C). +- `mcp_hints.py` / hints v3 (PR-D). +- `find(kind="producer")` / `resolve(hint_kind="producer")` (PR-C). +- Second ontology bump. +- `TPL_NEIGHBORS_*` template changes. + +## PR body requirement + +Paste `grep -rn 'HTTP_CALLS' --include='*.py' --include='*.md' .` and account for every hit. + +## Tests to run + +```bash +.venv/bin/ruff check build_ast_graph.py kuzu_queries.py pr_analysis.py mcp_v2.py +.venv/bin/python -m pytest tests/test_call_edges_e2e.py tests/test_kuzu_queries.py tests/test_pr_analysis.py tests/test_client_hint_recovery.py -v +.venv/bin/python scripts/generate_edge_navigation.py --check +``` + +Before PR open: `.venv/bin/ruff check .` and `.venv/bin/python -m pytest tests -v`. + +## Sentinel checks (zero on diff) + +- `CallerInfo` (use `RouteCaller` only) +- `Symbol)-[e:HTTP_CALLS]` / `Symbol)-[:HTTP_CALLS]` in production queries +- `Producer` / `DECLARES_PRODUCER` +- `neighbors_empty_hints` + +## Definition of Done + +- [ ] PR-B plan DoD + grep enumeration in PR description. +- [ ] PR title: `feat(schema): HTTP_CALLS originates from Client, not Symbol` +```` + +--- + +## PR-SCHEMA-V2-C — Producer + `ASYNC_CALLS` flip + +**Branch:** `feat/schema-v2-producer-async-calls` off `master` **after PR-B merged**. +**Base:** `master` at merge commit of PR-B. +**Plan section:** [`plans/PLAN-SCHEMA-V2.md`](./PLAN-SCHEMA-V2.md) § PR-C. +**PR title:** `feat(schema): introduce Producer node and route ASYNC_CALLS through it` + +**Attach (`@-files`):** + +- `@plans/PLAN-SCHEMA-V2.md` (PR-C) +- `@propose/SCHEMA-V2-PROPOSE.md` (§3.2, §3.3–§3.4, §3.6–§3.9, §4 async UCs, PR-C §6) +- `@graph_enrich.py` (`AsyncProducerHint`) +- `@java_ontology.py` +- `@build_ast_graph.py` +- `@kuzu_queries.py` +- `@pr_analysis.py` +- `@mcp_v2.py` +- `@server.py` +- `@tests/test_call_edges_e2e.py` +- `@tests/test_brownfield_clients.py` +- `@tests/test_mcp_v2.py` +- `@tests/test_ast_graph_build.py` +- `@tests/test_client_node_extraction.py` + +**Prompt:** + +```` +You are implementing PR-SCHEMA-V2-C from `plans/PLAN-SCHEMA-V2.md` (**PR-C**). + +PR-B is on `master` (HTTP_CALLS from Client). Do not revert HTTP shape. + +## Scope + +1. **`Producer` node** + **`DECLARES_PRODUCER`** + **`ASYNC_CALLS` `Producer→Route`** in schema, DDL, pass5 materialization, pass6 emission. +2. **`AsyncCallRow.producer_id`**; Producer fields from `AsyncProducerHint` / dispatch metadata (propose §3.2 table — no HTTP-only copy-paste). +3. **`GraphMeta`**: `producers_total`, `declares_producer_total`; wire `server.py` meta output if applicable. +4. **`kuzu_queries.py`**: async two-hop in `find_route_callers`, `trace_request_flow`, impact analysis; producer branch on `RouteCaller`. +5. **`pr_analysis.py`**: async two-hop route reachability (`DECLARES_PRODUCER` + `ASYNC_CALLS`; HTTP already two-hop from PR-B). +6. **`mcp_v2.py`**: `find(kind="producer")`, `resolve(hint_kind="producer")`, `_load_node_record` for Producer. +7. **`java_ontology.py`**: `DECLARES_PRODUCER` in `EDGE_SCHEMA` with `member_only=True`. +8. **Type-level `describe` rollups**: `DECLARES.DECLARES_PRODUCER`, `OVERRIDDEN_BY.DECLARES_PRODUCER`. +9. **Async doc sweep** + regenerate `docs/EDGE-NAVIGATION.md` (11 edges). +10. **Tests** — All **Tests for PR-C** in the plan (verbatim). + +## Out of scope (do NOT touch) + +- `mcp_hints.py` / PR-D hints v3. +- `Consumer` node. +- Ontology 15 / second re-index bump. +- Ranking or incremental index proposes. + +## PR body requirement + +`grep -rn 'ASYNC_CALLS\|Producer\|DECLARES_PRODUCER' --include='*.py' --include='*.md' .` — account for every hit. + +## Tests to run + +```bash +.venv/bin/ruff check build_ast_graph.py kuzu_queries.py mcp_v2.py +.venv/bin/python -m pytest tests/test_call_edges_e2e.py tests/test_brownfield_clients.py tests/test_mcp_v2.py tests/test_ast_graph_build.py -v +rm -rf /tmp/schema-v2-c && .venv/bin/python build_ast_graph.py --source-root tests/fixtures/http_caller_smoke --kuzu-path /tmp/schema-v2-c --verbose +``` + +Before PR open: `.venv/bin/ruff check .` and `.venv/bin/python -m pytest tests -v`. + +## Sentinel checks (zero on diff) + +- `TPL_NEIGHBORS_WRONG_SUBJECT_KIND` / `neighbors_empty_hints` +- `Symbol)-[e:ASYNC_CALLS]` in production emit/query paths +- `ONTOLOGY_VERSION = 15` + +## Definition of Done + +- [ ] PR-C plan DoD + async grep in PR description. +- [ ] PR title matches plan. +- [ ] Note: **PR-D (hints)** is next — do not implement hints in this PR. +```` diff --git a/plans/PLAN-HINTS-V3.md b/plans/PLAN-HINTS-V3.md new file mode 100644 index 0000000..b416465 --- /dev/null +++ b/plans/PLAN-HINTS-V3.md @@ -0,0 +1,195 @@ +# Plan: HINTS-V3 (EDGE_SCHEMA-driven empty `neighbors` hints) + +Status: **active (implementing)**. This plan implements +[`propose/HINTS-V3-PROPOSE.md`](../propose/HINTS-V3-PROPOSE.md). + +Depends on: + +- [`propose/SCHEMA-V2-PROPOSE.md`](../propose/SCHEMA-V2-PROPOSE.md) — `EDGE_SCHEMA`, post-flip endpoints, `brownfield_resolver_sourced`, `typical_traversals`, `BROWNFIELD_RESOLVER_STRATEGY_SET` (PR-A–C on `master`). +- **Code PR-D** runs only after SCHEMA-V2 **PR-A, PR-B, PR-C** are merged. +- **Propose gate:** `HINTS-V3-PROPOSE.md` merged to `master` before SCHEMA-V2 **PR-A** starts (may stay GitHub `draft`). +- **Lock gate:** `HINTS-V3-PROPOSE.md` `Status: locked` before **PR-D** merges. + +Sequence reference: [`docs/PROPOSES-ORDER.md`](../docs/PROPOSES-ORDER.md). + +## Goal + +- **PR-D (single code PR):** Delete `TPL_NEIGHBORS_EMPTY_KIND_CHECK`; add four EDGE_SCHEMA-driven empty-result templates; implement `neighbors_empty_hints()` + `typical_traversal_for()`; extend `neighbors_v2` hint payload with `subject_record` and `requested_direction`; wire `generate_hints("neighbors", …)` empty branch; keep v2 fuzzy hint on non-empty results unchanged. +- Agents holding the wrong node kind or direction for `HTTP_CALLS` / `ASYNC_CALLS` (post-flip) get actionable traversals sourced from `EDGE_SCHEMA`, not a generic kind check. + +## Principles (do not relitigate in review) + +- **`EDGE_SCHEMA` is the only edge-shape knowledge in `mcp_hints.py`.** No edge-name or Client→Route literals outside tests. +- **One template per mismatch dimension:** alien kind, wrong direction, type-level requery, brownfield-resolver absence (four templates). +- **Per-edge evaluation order:** alien kind → wrong direction → type-level (`member_only`); first match wins for rows 1–3. +- **Brownfield row 4** may co-fire with structural rows; deduped once per output across edges. +- **Fuzzy vs brownfield empty hints are disjoint:** v2 `TPL_NEIGHBORS_FUZZY_STRATEGY` only on **non-empty** results; row 4 only on **empty** when `brownfield_resolver_sourced`. +- **`PRIORITY_META` for all new templates** — same tier as deleted v1 empty template. +- **No dot-key edge labels in hint text** — post-filter + test (v2 invariant). +- **No ontology bump** — query-time only; re-index already required by SCHEMA v14. +- **Multi-id `neighbors`:** hints use **`origins[0]`** only (document in `MCP_HINTS_FIELD_DESCRIPTION` if needed). + +## PR breakdown — overview + +| PR | Scope | Ontology bump | Areas of concern | Test buckets | Independent of | +| --- | --- | --- | --- | --- | --- | +| PR-D | `mcp_hints.py` + `mcp_v2.py` neighbors payload + tests + minimal README | **No** | Subject node label resolution; `member_only` coverage; traversal placeholder `{id}`; cap interaction with multi-edge requests | `tests/test_mcp_hints.py` HV1–HV20 | Nothing — requires SCHEMA PR-C | + +**Landing order:** **PR-D** after SCHEMA **PR-C** on `master`. + +**Merge gates:** This file + [`plans/CURSOR-PROMPTS-HINTS-V3.md`](./CURSOR-PROMPTS-HINTS-V3.md) before PR-D code merges (by analogy with SCHEMA Decision 29). + +## Resolved design decisions + +| Topic | Decision | +| --- | --- | +| Implementation PR | Same as SCHEMA-V2 PR-D — one PR titled `feat(hints): kind- and direction-aware empty-result hints driven by EDGE_SCHEMA`. | +| Deleted template | `TPL_NEIGHBORS_EMPTY_KIND_CHECK` — no alias. | +| Subject kind source | Node label from `subject_record` (`Symbol`, `Client`, `Route`, `Producer`), not `symbol_kind` alone. | +| Type-level requery | `Symbol` with `symbol_kind ∈ _TYPE_SYMBOL_KINDS` and `EdgeSpec.member_only=True`. | +| `member_only` | Prefer landed in SCHEMA PR-A on `EdgeSpec`; PR-D adds only if PR-A omitted. | +| Wrong direction rule | Subject matches **opposite** endpoint for requested `in`/`out`. | +| Coverage test HV19 | ∃ synthetic `(edge, subject_label, direction)` per `EDGE_SCHEMA` edge triggering row 1–3 or row 4 — not ∀ empty queries. | +| v1/v2 catalogs | Unchanged except neighbors empty branch. | + +--- + +# PR-D — kind/direction-aware empty-result hints + +## File-by-file changes + +### 1. `mcp_hints.py` + +- Delete `TPL_NEIGHBORS_EMPTY_KIND_CHECK`. +- Add verbatim templates from propose §3.1: + - `TPL_NEIGHBORS_WRONG_SUBJECT_KIND` + - `TPL_NEIGHBORS_WRONG_DIRECTION` + - `TPL_NEIGHBORS_TYPE_LEVEL_REQUERY` + - `TPL_NEIGHBORS_BROWNFIELD_RESOLVED_MAYBE_UNRESOLVED` +- Import `EDGE_SCHEMA` from `java_ontology` (not a copy). +- Add helpers: + - `_subject_node_label(subject_record: dict) -> str` — map row to `Symbol`/`Client`/`Route`/`Producer`. + - `typical_traversal_for(edge: str, role_key: str, *, subject_id: str, direction: str) -> str` — select from `EdgeSpec.typical_traversals`, substitute `{id}`, `{direction}`, `{edge}` placeholders without embedding edge literals beyond schema output. + - `neighbors_empty_hints(subject_record, requested_edge_types, requested_direction) -> list[tuple[int, str]]` per propose §3.2–3.3. +- Update `generate_hints` `neighbors` branch: + - When `results` empty and `requested_edge_types` non-empty: call `neighbors_empty_hints`, merge pairs before fuzzy check. + - When `results` non-empty: existing fuzzy path unchanged (`TPL_NEIGHBORS_FUZZY_STRATEGY`). +- Post-filter rendered hints: reject any hint containing a dot-key edge label pattern used for composed rollups (reuse v2 approach / test). +- Module docstring: reference HINTS-V3 propose. + +### 2. `mcp_v2.py` + +- In `neighbors_v2`, build hint payload per propose §3.6: + - `requested_direction`: echo `direction` param. + - `origin_id`: first origin when `ids` is a list. + - `subject_record`: `_load_node_record(g, origin_id, kind)` using resolved kind from id prefix / `_resolve_node_kind`. +- Pass payload into `generate_hints("neighbors", neigh_payload)`. + +### 3. `java_ontology.py` (only if PR-A skipped `member_only`) + +- Add `member_only: bool = False` on `EdgeSpec` and set flags per propose §3.4. + +### 4. `README.md` (minimal) + +- MCP v2 hints paragraph: neighbors empty results may emit EDGE_SCHEMA-driven structural hints; link HINTS-V3 propose. + +### 5. `server.py` (optional, minimal) + +- One line on `neighbors` tool description: empty results may include traversal hints (no new parameters). + +### 6. `tests/test_mcp_hints.py` + +- Pure `generate_hints("neighbors", …)` tests with crafted `subject_record` + empty `results`. +- HV19: parametrized over `EDGE_SCHEMA` keys. +- HV16: non-empty Client + fuzzy strategy — `neighbors_empty_hints` not invoked. +- Dot-key invariant test on rendered output. +- Optional `neighbors_v2` round-trip on post-flip graph (session `kuzu_graph` after SCHEMA PR-C) for HV2/HV6 — fail loud if fixture lacks post-flip shape. + +## Tests for PR-D + +Name tests `test_hints_hv{N}_*` matching propose §6 / §4 rows: + +1. `test_hints_hv1_type_level_declares_client_requery` +2. `test_hints_hv2_method_http_calls_wrong_subject_kind` +3. `test_hints_hv3_method_async_calls_wrong_subject_kind` +4. `test_hints_hv4_producer_empty_async_out_brownfield_only` +5. `test_hints_hv5_producer_async_calls_wrong_direction` +6. `test_hints_hv6_client_http_calls_wrong_direction` +7. `test_hints_hv7_route_http_calls_wrong_direction` +8. `test_hints_hv8_method_exposes_empty_no_structural_hint` +9. `test_hints_hv9_method_declares_client_empty_no_structural_hint` +10. `test_hints_hv10_class_http_calls_wrong_subject_kind` +11. `test_hints_hv11_method_overrides_empty_no_structural_hint` +12. `test_hints_hv12_annotation_extends_empty_no_structural_hint` — assert per PR-A `EXTENDS` / `member_only` lock +13. `test_hints_hv13_client_empty_http_brownfield_only` +14. `test_hints_hv14_producer_empty_async_brownfield_only` +15. `test_hints_hv15_multi_edge_http_only_wrong_kind_for_http` +16. `test_hints_hv16_client_nonempty_http_fuzzy_hint_unchanged` +17. `test_hints_hv17_class_exposes_type_level_requery` +18. `test_hints_hv18_route_declares_wrong_subject_kind` +19. `test_hints_hv19_edge_schema_coverage_exists_trigger_per_edge` +20. `test_hints_hv20_no_dotkey_edge_labels_in_rendered_neighbors_hints` +21. `test_hints_neighbors_empty_kind_check_template_removed` — grep/template absent +22. `test_hints_neighbors_v2_empty_post_flip_method_http_calls` — integration round-trip on post-flip graph (**required** once SCHEMA PR-C is on `master`; fail loud if session fixture lacks Client→Route shape) + +**Regression:** `test_hints_neighbors_fuzzy_strategy_*` and v1 neighbors tests still pass; update `test_hints_neighbors_empty_with_edge_types_emits_kind_check` → expect new template family (rename to reflect v3 behavior). + +## Definition of done (PR-D) + +- [ ] `TPL_NEIGHBORS_EMPTY_KIND_CHECK` deleted; four v3 templates wired. +- [ ] `neighbors_empty_hints` follows fixed evaluation order; row 4 deduped. +- [ ] `neighbors_v2` passes `subject_record` + `requested_direction`. +- [ ] All named HV tests pass; HV19 coverage holds. +- [ ] HINTS-V3 propose `Status: locked` before merge. +- [ ] `.venv/bin/ruff check .` and `.venv/bin/python -m pytest tests -v` green. +- [ ] No `ONTOLOGY_VERSION` change. + +## Implementation step list + +| # | Step | File(s) | Done when | +| --- | --- | --- | --- | +| 1 | Templates + helpers + `neighbors_empty_hints` | `mcp_hints.py` | Unit tests HV1–HV18 pass | +| 2 | Wire `generate_hints` empty branch | `mcp_hints.py` | Empty payloads emit hints | +| 3 | Neighbors payload | `mcp_v2.py` | subject_record loaded | +| 4 | `member_only` if missing | `java_ontology.py` | HV1/HV17 pass | +| 5 | HV19 + dot-key + regression | `tests/test_mcp_hints.py` | Full PR-D tests green | +| 6 | Docs | `README.md`, `server.py` | Copy matches behavior | + +--- + +# Cross-PR risks and mitigations + +| # | Risk | Severity | Mitigation | +| --- | --- | --- | --- | +| 1 | PR-D merged before post-flip schema | High | Block on SCHEMA PR-C; HV2/HV3 integration tests | +| 2 | Missing `typical_traversals` key | Medium | HV19; PR-A contract | +| 3 | False-positive structural hints | Medium | HV8, HV9, HV11; three-step order | +| 4 | Hint noise under 5-cap | Low | Meta tier; HV15 bounded | +| 5 | `member_only` wrong on HTTP/ASYNC | Medium | Never True on Client/Producer endpoint edges | +| 6 | Fuzzy + brownfield duplicate | Low | Principle 4; HV4 vs HV16 | + +# Out of scope + +- New MCP tools or `neighbors` parameters (`direction` stays `in`|`out` only). +- Hints on `find` / `resolve` / `describe` (existing families stay). +- Per-row neighbors hints; confidence-based hints. +- Localization; hint caching. +- `EDGE_SCHEMA` / graph builder changes (except `member_only` backfill). +- Ontology version bump or re-index. + +# Whole-plan done definition + +1. Empty `neighbors` with wrong kind/direction/type-level subject emits EDGE_SCHEMA-driven hints per HV table. +2. `TPL_NEIGHBORS_EMPTY_KIND_CHECK` gone; v2 fuzzy hint unchanged on non-empty. +3. `neighbors_v2` supplies `subject_record` and `requested_direction` to hint generator. +4. HV19 passes; no dot-key labels in recommendations. +5. `propose/HINTS-V3-PROPOSE.md` moved to `propose/completed/` when PR-D merges. + +# Tracking + +- Artefacts: _pending_ +- `PR-D`: _pending_ (blocked on SCHEMA PR-C + HINTS-V3 propose **locked**) + +## Cursor handoff + +[`plans/CURSOR-PROMPTS-HINTS-V3.md`](./CURSOR-PROMPTS-HINTS-V3.md) diff --git a/plans/PLAN-SCHEMA-V2.md b/plans/PLAN-SCHEMA-V2.md new file mode 100644 index 0000000..eda4649 --- /dev/null +++ b/plans/PLAN-SCHEMA-V2.md @@ -0,0 +1,387 @@ +# Plan: SCHEMA-V2 (edge navigation schema, HTTP/ASYNC caller-side flips, Producer node) + +Status: **active (implementing)**. This plan implements +[`propose/SCHEMA-V2-PROPOSE.md`](../propose/SCHEMA-V2-PROPOSE.md). + +Depends on: + +- [`propose/HINTS-V3-PROPOSE.md`](../propose/HINTS-V3-PROPOSE.md) **merged to `master`** before **PR-A** implementation starts (SCHEMA-V2 Decision 30; GitHub PR may stay `draft`). +- [`docs/PROPOSES-ORDER.md`](../docs/PROPOSES-ORDER.md) for lock/merge sequence across proposes and code PRs. +- **PR-D (hints v3)** is specified in [`plans/PLAN-HINTS-V3.md`](./PLAN-HINTS-V3.md) — not repeated here beyond overview/tracking. + +## Goal + +- **PR-A:** Introduce `EDGE_SCHEMA` / `EdgeSpec` / `EdgeAttr` in `java_ontology.py` for all **10 current** edges (pre-flip endpoints); add `BROWNFIELD_RESOLVER_STRATEGY_SET`; generate `docs/EDGE-NAVIGATION.md` with CI `--check`; DDL↔schema invariant tests; bump `ONTOLOGY_VERSION` **13 → 14** and document re-index (no graph endpoint flips yet). +- **PR-B:** Flip `HTTP_CALLS` to `Client → Route` in schema, DDL, pass5/pass6, and every Python consumer; reshape `find_route_callers` / `trace_request_flow` / impact-analysis expansion to two-hop caller-side traversals (`RouteCaller` replaces `CallerInfo`); HTTP doc sweep. +- **PR-C:** Add `Producer` node + `DECLARES_PRODUCER`; flip `ASYNC_CALLS` to `Producer → Route`; `GraphMeta` producer counters; MCP `find`/`resolve` producer parity; type-level `describe` `DECLARES_PRODUCER` rollups; async doc sweep. +- After **PR-C**, agents can navigate caller-side HTTP/async without Symbol-bypass edges; **PR-D** (separate plan) restores empty-`neighbors` guidance. + +## Principles (do not relitigate in review) + +- **Edges connect the nodes whose data the edge is about.** Caller-side metadata lives on `Client` / `Producer`; traversals go `Symbol -[:DECLARES_*]-> caller_node -[:HTTP_CALLS|ASYNC_CALLS]-> Route`. +- **Single replacement, no dual edges.** No `HTTP_CALLS_LEGACY`, no Symbol→Route coexistence. +- **`EDGE_SCHEMA` in `java_ontology.py` is canonical** for endpoints, attrs metadata, `typical_traversals`, and `brownfield_resolver_sourced`. DDL strings in `build_ast_graph.py` are checked against it (src/dst only in CI v1 of invariant). +- **Generated docs, not hand-written.** `docs/EDGE-NAVIGATION.md` is produced by `scripts/generate_edge_navigation.py`; `--check` in CI. +- **Breaking API reshape is allowed.** `CallerInfo` → `RouteCaller`; MCP/query outputs return caller-side node ids. No back-compat aliases. +- **One ontology bump, one re-index.** v14 lands in PR-A; README + `docs/AGENT-GUIDE.md` updated in PR-A; graph built at v14 must be fully reprocessed after the sequence completes. +- **Hints v3 is not optional for the sequence.** HINTS-V3 propose on `master` before PR-A; PR-D locked propose before PR-D merges (see HINTS plan). +- **Brownfield composition unchanged** except caller-side edge anchoring: HTTP/ASYNC brownfield layers still compose per Tier-1B option-(b) replacement on methods. + +## PR breakdown — overview + +| PR | Scope | Ontology bump | Areas of concern | Test buckets | Independent of | +| --- | --- | --- | --- | --- | --- | +| PR-A | `EDGE_SCHEMA`, doc generator, CI invariants, `BROWNFIELD_RESOLVER_STRATEGY_SET`, v14 bump (pre-flip DDL) | **13 → 14** | `typical_traversals` map shape (hints PR-A contract); strategy-set union completeness; v13 refusal gate timing | `test_schema_consistency.py`, `test_edge_navigation_doc.py`, ontology gate | PR-B/C/D | +| PR-B | `HTTP_CALLS` flip + downstream Cypher/API + HTTP docs | Uses v14 (no second bump) | `grep` completeness for `HTTP_CALLS`; `HttpCallRow` key change; MCP/PR-analysis two-hop queries | `test_call_edges_e2e.py`, `test_kuzu_queries.py`, `test_pr_analysis.py`, brownfield HTTP | PR-C/D | +| PR-C | `Producer`, `DECLARES_PRODUCER`, `ASYNC_CALLS` flip, GraphMeta, MCP producer parity, describe rollups, async docs | Uses v14 | Producer field grounding vs `Client` copy-paste; pass5 materialization order; `find`/`resolve` kind union expansion | `test_call_edges_e2e.py`, `test_brownfield_clients.py`, `test_mcp_v2.py`, `test_client_node_extraction.py`, describe rollups | PR-D | +| PR-D | Hints v3 (empty `neighbors`) | **No** (query-time) | See [`plans/PLAN-HINTS-V3.md`](./PLAN-HINTS-V3.md) | `test_mcp_hints.py` HV* | PR-A/B/C | + +**Landing order:** **PR-A → PR-B → PR-C → PR-D** (PR-D plan is separate; no parallel code PRs). + +**Merge gates (artefacts):** This file + [`plans/CURSOR-PROMPTS-SCHEMA-V2.md`](./CURSOR-PROMPTS-SCHEMA-V2.md) must land before **PR-A** code merges (SCHEMA-V2 Decision 29). + +## Resolved design decisions + +| Topic | Decision | +| --- | --- | +| `EDGE_SCHEMA` size in v2 | **11** entries after PR-C (`DECLARES_PRODUCER` added); PR-A ships **10** (no Producer table yet). | +| `typical_traversals` | **PR-A contract:** role-keyed `dict[str, str]` per HINTS-V3 §3.5 (`type_subject`, `member_subject`, `alien_subject`, …). SCHEMA-V2 propose Appendix A `tuple[str, …]` examples are **illustrative only** — implementation and hints v3 use the dict shape. | +| `member_only` on `EdgeSpec` | Hint-only (not in DDL CI). **PR-A:** `True` on `DECLARES_CLIENT`, `EXPOSES`, `OVERRIDES`, `CALLS` only (10-edge schema). **PR-C:** add `DECLARES_PRODUCER` to `EDGE_SCHEMA` and set `member_only=True` when that edge lands. If PR-A omits the field entirely, PR-D may add it. | +| `brownfield_resolver_sourced` | Renamed from `brownfield_sourced`; True iff edge carries resolver `strategy` ∈ `BROWNFIELD_RESOLVER_STRATEGY_SET`. | +| `BROWNFIELD_RESOLVER_STRATEGY_SET` | `FUZZY_STRATEGY_SET` ∪ non-fuzzy resolver strategies used on edges today (`codebase_route`, `codebase_client`, `codebase_producer`, `layer_*`, pass5/6 HTTP/async strategy literals — lock members in PR-A from `grep` of `strategy=` / ontology sets). | +| `HttpCallRow` / `AsyncCallRow` | PR-B: `client_id` replaces `symbol_id` on HTTP rows. PR-C: `producer_id` replaces `symbol_id` on async rows. | +| Producer `id` | `p:` parallel to Client `c:`; exact hash inputs locked in PR-C implementation (member + kind + topic + dispatch site). | +| `find_route_callers` | Returns `list[RouteCaller]`; two-hop Cypher; includes `declaring_symbol_id` back-ref. | +| Type-level `describe` | PR-C adds `DECLARES.DECLARES_PRODUCER` + `OVERRIDDEN_BY.DECLARES_PRODUCER`; **no** composed `DECLARES.HTTP_CALLS` rollups. | +| Consumer node | **Out of scope** (Decision 21). | +| `NODE_SCHEMA` / DDL codegen | Out of scope; manual DDL + CI check only. | + +--- + +# PR-A — `EDGE_SCHEMA` + doc generator + ontology v14 (no flips) + +## File-by-file changes + +### 1. `java_ontology.py` + +- Add `NodeKind`, `Cardinality`, `EdgeAttr`, `EdgeSpec` dataclasses. +- Add `EDGE_SCHEMA: dict[str, EdgeSpec]` populated for **10** edges with **current** endpoints: + - `HTTP_CALLS`: `Symbol → Route` (pre-flip) + - `ASYNC_CALLS`: `Symbol → Route` (pre-flip) + - No `DECLARES_PRODUCER` entry yet (PR-C). +- Add `brownfield_resolver_sourced: bool` on `EdgeSpec`. +- Add `member_only: bool = False` on `EdgeSpec` (default False). **PR-A only:** set `True` on `DECLARES_CLIENT`, `EXPOSES`, `OVERRIDES`, `CALLS` (not `DECLARES_PRODUCER` — that edge does not exist until PR-C). +- Add `typical_traversals: dict[str, str]` per edge (role keys per HINTS-V3 §3.5 — **not** the tuple shape in SCHEMA propose Appendix A). Post-flip traversals for HTTP/ASYNC may describe target shape; update strings again in PR-B/C when endpoints flip. +- Add `BROWNFIELD_RESOLVER_STRATEGY_SET: frozenset[str]` (Decision 28). +- Export new symbols in `__all__`. + +### 2. `build_ast_graph.py` + +- **No endpoint flips** in PR-A. +- Optionally refactor existing `_SCHEMA_*` DDL constants to sit beside schema imports; ensure strings remain `Symbol→Route` for HTTP/ASYNC. +- No `Producer` node table. + +### 3. `scripts/generate_edge_navigation.py` (new) + +- Read `EDGE_SCHEMA`; write `docs/EDGE-NAVIGATION.md` (banner: generated — do not edit). +- CLI: default write; `--check` compares to committed file (nonzero exit on drift). +- Stable key ordering: `EDGE_SCHEMA` declaration order, not dict iteration order. + +### 4. `docs/EDGE-NAVIGATION.md` (new, generated) + +- Commit generator output. + +### 5. `ast_java.py` + +- `ONTOLOGY_VERSION = 14`. + +### 6. `kuzu_queries.py` + +- Keep version gate at `ast_java.ONTOLOGY_VERSION` (already imports it). +- Ensure error message mentions full reprocess when graph `<` required version. + +### 7. `README.md` + +- Update "Re-index required" current version to **14**. +- Add callout: v14 introduces `EDGE_SCHEMA`; PR-B flips `HTTP_CALLS`; PR-C adds `Producer` and flips `ASYNC_CALLS` (one full reprocess after upgrading). + +### 8. `docs/AGENT-GUIDE.md` + +- Bump ontology version sentence (line ~15 area). + +### 9. `.github/workflows/test.yml` (if no existing hook) + +- Add step: `.venv/bin/python scripts/generate_edge_navigation.py --check` (or document running via pytest that shells out — prefer explicit CI step). + +### 10. `tests/test_schema_consistency.py` (new) + +- Parse `CREATE REL TABLE` DDL in `build_ast_graph.py` for `(src_label, dst_label)` per edge. +- Assert `EDGE_SCHEMA[name].src/dst` matches DDL for every edge present in both. +- Assert `EDGE_SCHEMA` keys match the DDL edge set (10 edges in PR-A). + +### 11. `tests/test_edge_navigation_doc.py` (new) + +- Generator output matches committed `docs/EDGE-NAVIGATION.md`. +- `--check` fails when doc is stale (temp write + compare). + +### 12. `tests/test_kuzu_queries.py` + +- Extend or add stale-graph refusal test: graph with `ontology_version=13` refuses open when `ONTOLOGY_VERSION==14`. + +## Tests for PR-A + +1. `test_schema_consistency_all_ddl_endpoints_match_edge_schema` +2. `test_schema_consistency_http_calls_pre_flip_symbol_to_route` +3. `test_schema_consistency_async_calls_pre_flip_symbol_to_route` +4. `test_edge_navigation_doc_matches_generator_output` +5. `test_edge_navigation_doc_check_mode_detects_drift` +6. `test_kuzu_graph_refuses_ontology_version_below_required` +7. `test_edge_schema_member_only_flags_on_method_level_edges` — `DECLARES_CLIENT`, `EXPOSES`, `OVERRIDES`, `CALLS` True; no `DECLARES_PRODUCER` key in PR-A schema; `HTTP_CALLS`/`ASYNC_CALLS` False at pre-flip. + +## Definition of done (PR-A) + +- [ ] `EDGE_SCHEMA` + `BROWNFIELD_RESOLVER_STRATEGY_SET` in `java_ontology.py`. +- [ ] `ONTOLOGY_VERSION` is **14**; README + AGENT-GUIDE call out v14 + re-index. +- [ ] `docs/EDGE-NAVIGATION.md` generated; CI/check passes. +- [ ] DDL↔schema tests green; no HTTP/ASYNC endpoint flips. +- [ ] `.venv/bin/ruff check .` and `.venv/bin/python -m pytest tests -v` green (no heavy gate). + +## Implementation step list + +| # | Step | File(s) | Done when | +| --- | --- | --- | --- | +| 1 | Dataclasses + `EDGE_SCHEMA` (10 edges, pre-flip) | `java_ontology.py` | Importable; member_only + traversals populated | +| 2 | `BROWNFIELD_RESOLVER_STRATEGY_SET` | `java_ontology.py` | Superset of `FUZZY_STRATEGY_SET` + resolver literals | +| 3 | Doc generator + committed doc | `scripts/…`, `docs/EDGE-NAVIGATION.md` | `--check` passes | +| 4 | Ontology bump + docs | `ast_java.py`, `README.md`, `docs/AGENT-GUIDE.md` | Version 14 documented | +| 5 | Consistency + doc tests | `tests/test_schema_consistency.py`, `tests/test_edge_navigation_doc.py`, `tests/test_kuzu_queries.py` | All PR-A tests pass | +| 6 | CI hook | `.github/workflows/test.yml` | Generator check runs on PRs | + +--- + +# PR-B — flip `HTTP_CALLS` to `Client → Route` + downstream API + +## File-by-file changes + +### 1. `java_ontology.py` + +- Update `EDGE_SCHEMA["HTTP_CALLS"]`: `src="Client"`, `dst="Route"`. +- Update `typical_traversals` for HTTP_CALLS to post-flip canonical strings. + +### 2. `build_ast_graph.py` + +- DDL: `HTTP_CALLS(FROM Client TO Route, …)`. +- `HttpCallRow`: rename `symbol_id` → `client_id` (or replace field — no compat). +- `pass5_imperative_edges` / `pass6_match_edges`: emit `HTTP_CALLS` from **Client** id, not Symbol. +- Ensure `DECLARES_CLIENT` emitted when Client rows materialized (if not already paired). + +### 3. `kuzu_queries.py` + +- Replace `CallerInfo` with `RouteCaller` dataclass: + - `caller_node_id`, `caller_node_kind` (`Literal["client","producer"]` — producer branch wired in PR-C), + - `caller_microservice`, `declaring_symbol_id`, `confidence`, `match`, + - HTTP fields from Client node: `target_service`, `raw_uri` / path fields as applicable. +- `find_route_callers`: two-hop Cypher + `MATCH (s:Symbol)-[:DECLARES_CLIENT]->(c:Client)-[e:HTTP_CALLS]->(r:Route …)`. +- `trace_request_flow` inbound: two-hop via Client; output rows include `caller_node_id`, `caller_node_kind`, `declaring_symbol_id`, `declaring_symbol_fqn`. +- Impact-analysis expansion in `KuzuGraph` cross-service flow stage (Cypher using `HTTP_CALLS|ASYNC_CALLS` on changed symbols): three-hop through Client; surface caller node id on impacted route rows. Locate via `grep HTTP_CALLS|ASYNC_CALLS` in `kuzu_queries.py` — do not rely on line numbers. +- Remove all `Symbol-[HTTP_CALLS]->Route` patterns. + +### 4. `pr_analysis.py` + +- Route reachability query (~436): two-hop via `DECLARES_CLIENT` + `HTTP_CALLS` (keep `ASYNC_CALLS` direct until PR-C). + +### 5. `mcp_v2.py` + +- Update types wrapping `find_route_callers` / flow trace outputs. +- Any hard-coded neighbor examples in docstrings referencing Symbol→Route HTTP. + +### 6. `server.py` + +- Tool descriptions if they mention Symbol-level HTTP_CALLS. + +### 7. `search_lancedb.py` / graph expansion (if any) + +- `grep HTTP_CALLS` — update every Python match (PR description lists all paths). + +### 8. Docs (HTTP sweep) + +- `README.md`, `docs/AGENT-GUIDE.md`, `docs/skills/java-codebase-explore.md` — HTTP_CALLS traversal examples only. +- Regenerate `docs/EDGE-NAVIGATION.md` via script after schema change. + +### 9. Tests / fixtures + +- Update expectations in `tests/test_call_edges_e2e.py`, `tests/test_brownfield_clients.py`, `tests/test_pr_analysis.py`, `tests/test_mcp_v2.py`, `tests/test_mcp_v2_compose.py`, `tests/test_client_hint_recovery.py`, `tests/fixtures/**` only where HTTP caller shape requires it. + +## Tests for PR-B + +1. `test_call_edges_client_outbound_http_calls_returns_routes` — SCHEMA UC1 +2. `test_call_edges_method_two_http_clients_two_routes` — UC5 +3. `test_call_edges_cross_service_http_four_hop` — UC8 (DECLARES_CLIENT → HTTP_CALLS → EXPOSES) +4. `test_pr_analysis_changed_methods_finds_routes_via_declares_client` — UC9 HTTP leg +5. `test_find_route_callers_returns_route_caller_client_node` — `RouteCaller.caller_node_kind == "client"` +6. `test_trace_request_flow_inbound_includes_caller_node_id` +7. `test_schema_consistency_http_calls_post_flip_client_to_route` +8. `test_describe_client_edge_summary_includes_http_calls_out` — UC10 + +**PR-B PR description must include** output of: +`grep -rn 'HTTP_CALLS' --include='*.py' --include='*.md' .` +with every hit accounted for (fixed or justified). + +## Definition of done (PR-B) + +- [ ] No `Symbol-[HTTP_CALLS]->Route` in production Python. +- [ ] `CallerInfo` removed; `RouteCaller` used everywhere. +- [ ] Named tests pass; schema consistency test updated for Client→Route. +- [ ] HTTP doc sweep complete; EDGE-NAVIGATION regenerated. +- [ ] Default pytest + ruff green. + +## Implementation step list + +| # | Step | File(s) | Done when | +| --- | --- | --- | --- | +| 1 | Schema + DDL flip | `java_ontology.py`, `build_ast_graph.py` | pass6 emits Client-anchored edges | +| 2 | Row type + pass5/6 | `build_ast_graph.py` | `HttpCallRow.client_id` | +| 3 | Query/API reshape | `kuzu_queries.py`, `pr_analysis.py` | Two-hop HTTP only | +| 4 | MCP surface | `mcp_v2.py`, `server.py` | Outputs match RouteCaller | +| 5 | Tests + fixtures | `tests/**` | PR-B tests green | +| 6 | Docs + regen | `README.md`, `docs/*`, `docs/EDGE-NAVIGATION.md` | Grep enumeration in PR body | + +--- + +# PR-C — `Producer` node + `ASYNC_CALLS` flip + GraphMeta + MCP parity + +## File-by-file changes + +### 1. `java_ontology.py` + +- Add `Producer` to `NodeKind`. +- Add `EDGE_SCHEMA["DECLARES_PRODUCER"]`, update `ASYNC_CALLS` to `Producer → Route`. +- Set `member_only=True` on `DECLARES_PRODUCER` (edge lands in this PR). +- `typical_traversals` for async edges/post-flip producer traversals. + +### 2. `build_ast_graph.py` + +- `CREATE NODE TABLE Producer(...)` per propose §3.2 (fields grounded in `AsyncProducerHint` / `AsyncCallRow`). +- `CREATE REL TABLE DECLARES_PRODUCER(FROM Symbol TO Producer, …)`. +- DDL: `ASYNC_CALLS(FROM Producer TO Route, …)`. +- `ProducerRow` dataclass + `tables.producer_rows`; materialize in `pass5_imperative_edges` alongside clients. +- `AsyncCallRow`: `producer_id` replaces `symbol_id`. +- pass6: emit async edges from Producer ids. +- `GraphMeta` / client_stats parallel: `producers_total`, `declares_producer_total` on node + DDL + insert payload. +- Regenerate meta JSON fields in `server.py` mapping if needed. + +### 3. `kuzu_queries.py` + +- `find_route_callers` / `trace_request_flow` / impact analysis: include `DECLARES_PRODUCER` + `ASYNC_CALLS` two-hop branch; `caller_node_kind="producer"` with `topic`/`broker` from node. +- `find` producers query helper (parallel `find_clients`). + +### 4. `pr_analysis.py` + +- Extend route reachability for changed symbols: async leg uses `DECLARES_PRODUCER` + `ASYNC_CALLS` two-hop (HTTP leg already two-hop from PR-B). + +### 5. `mcp_v2.py` + +- Extend `Literal` unions: `"producer"` on `find`, `resolve`, `_node_kind_from_id`, filters. +- `find_v2(kind="producer")`, `resolve(..., hint_kind="producer")` using `VALID_PRODUCER_KINDS`. +- `_load_node_record` for Producer. + +### 6. `server.py` + +- `GraphMetaOutput`: optional `producers_total` / `declares_producer_total` if exposed in meta tool (match `build_ast_graph` counters). + +### 7. `kuzu_queries.py` — `describe` rollups (type-level `edge_summary` path) + +- Add `("DECLARES.DECLARES_PRODUCER", "DECLARES_PRODUCER")` to type rollup set. +- Add `OVERRIDDEN_BY.DECLARES_PRODUCER` parallel to client axis. + +### 8. Docs (async sweep) + +- `README.md`, `docs/AGENT-GUIDE.md`, exploration skill — ASYNC_CALLS / Producer navigation. +- Regenerate `docs/EDGE-NAVIGATION.md` (11 edges). + +### 9. Tests / fixtures + +- `tests/test_call_edges_e2e.py`, `tests/test_brownfield_clients.py` (producer stubs), `tests/test_mcp_v2.py`, `tests/test_ast_graph_build.py`, `tests/test_client_node_extraction.py` (meta counters pattern). + +## Tests for PR-C + +1. `test_call_edges_declares_producer_then_async_calls_to_topic` — UC12 +2. `test_call_edges_topic_inbound_async_calls_lists_producers` — UC13 +3. `test_call_edges_method_two_producers_two_topics` — UC14 +4. `test_call_edges_unresolved_producer_empty_async_out` — UC15 +5. `test_call_edges_cross_service_async_four_hop` — UC16 +6. `test_call_edges_method_mixed_http_client_and_async_producer` — UC18 +7. `test_find_kind_producer_returns_producer_nodes` +8. `test_resolve_hint_kind_producer` +9. `test_describe_type_rollups_include_declares_producer` +10. `test_graph_meta_counts_producers_and_declares_producer` +11. `test_schema_consistency_async_calls_post_flip_producer_to_route` +12. `test_find_route_callers_includes_producer_callers` — async branch on `RouteCaller` + +**PR-C PR description must include** grep for `ASYNC_CALLS` / `Producer` in `*.md` and remaining `*.py` hits. + +## Definition of done (PR-C) + +- [ ] `EDGE_SCHEMA` has **11** edges; Producer table live; ASYNC_CALLS from Producer. +- [ ] GraphMeta counters wired; MCP find/resolve producer parity. +- [ ] Type-level describe rollups include DECLARES_PRODUCER axis. +- [ ] Named tests pass; EDGE-NAVIGATION regenerated. +- [ ] Default pytest + ruff green. + +## Implementation step list + +| # | Step | File(s) | Done when | +| --- | --- | --- | --- | +| 1 | Producer DDL + rows + DECLARES_PRODUCER | `build_ast_graph.py` | Producers in graph | +| 2 | ASYNC_CALLS flip + AsyncCallRow | `build_ast_graph.py`, `java_ontology.py` | pass6 from Producer | +| 3 | Queries + RouteCaller async | `kuzu_queries.py`, `pr_analysis.py` | Full two-hop async | +| 4 | MCP find/resolve + meta | `mcp_v2.py`, `server.py` | producer kind works | +| 5 | Describe rollups | `kuzu_queries.py` | DECLARES_PRODUCER rollups | +| 6 | Tests + docs | `tests/**`, `docs/**` | PR-C tests + async grep | + +--- + +# Cross-PR risks and mitigations + +| # | Risk | Severity | Mitigation | +| --- | --- | --- | --- | +| 1 | Missed `HTTP_CALLS`/`ASYNC_CALLS` Cypher site | High | PR-B/C grep enumeration in PR bodies; schema consistency tests | +| 2 | Agents need extra hop (UC8/UC16) | Medium | Document 4-hop traces; PR-D hints suggest canonical traversals | +| 3 | v14 index opened mid-sequence with mixed code | Medium | PR-A gate refuses v13; ship B+C quickly; README says full reprocess once at end | +| 4 | `typical_traversals` stale after flip | Medium | PR-B/C update schema + regen doc; HV19 in hints plan | +| 5 | Producer fields wrong vs async reality | Medium | PR-C review against `AsyncProducerHint`; no HTTP-only fields | +| 6 | `RouteCaller` consumers missed | Medium | `grep CallerInfo` sentinel in PR-B | +| 7 | PLAN/prompts not landed before code | Medium | Decision 29 — artefact PR first | + +# Out of scope + +- `Consumer` node, `NODE_SCHEMA`, DDL codegen from `EDGE_SCHEMA`. +- Multi-target Client/Producer nodes; materialized composite edges. +- Hints v3 implementation (PR-D — [`plans/PLAN-HINTS-V3.md`](./PLAN-HINTS-V3.md)). +- Ontology **15** or second re-index. +- Ranking / incremental rebuild proposes (`RANKING-MICROSERVICE`, `TIER2-INCREMENTAL-REBUILD`). +- Special-casing `tests/bank-chat-system/` in production code. + +# Whole-plan done definition + +1. Graph at ontology **14** with `HTTP_CALLS: Client→Route`, `ASYNC_CALLS: Producer→Route`, `DECLARES_PRODUCER` populated. +2. `EDGE_SCHEMA`, generated `docs/EDGE-NAVIGATION.md`, and DDL pass CI invariants. +3. `find_route_callers` / `trace_request_flow` / PR-analysis / impact analysis use caller-side two-hop traversals. +4. MCP `find`/`resolve` support `producer`; type-level `describe` exposes DECLARES_PRODUCER rollups. +5. PR-D (hints v3) landed per HINTS plan — empty wrong-kind `neighbors` queries are guided. +6. `propose/SCHEMA-V2-PROPOSE.md` moved to `propose/completed/` when **PR-D** merges (whole user-visible effort done). + +# Tracking + +- Artefacts (`PLAN-SCHEMA-V2`, `CURSOR-PROMPTS-SCHEMA-V2`, `PLAN-HINTS-V3`, `CURSOR-PROMPTS-HINTS-V3`): _pending_ +- `PR-A`: _pending_ +- `PR-B`: _pending_ +- `PR-C`: _pending_ +- `PR-D`: _see PLAN-HINTS-V3_ + +## Cursor handoff + +[`plans/CURSOR-PROMPTS-SCHEMA-V2.md`](./CURSOR-PROMPTS-SCHEMA-V2.md) — PR-A/B/C only. + +[`plans/CURSOR-PROMPTS-HINTS-V3.md`](./CURSOR-PROMPTS-HINTS-V3.md) — PR-D after PR-C on `master`. diff --git a/propose/HINTS-V3-PROPOSE.md b/propose/HINTS-V3-PROPOSE.md index b2acc7a..ea2dad3 100644 --- a/propose/HINTS-V3-PROPOSE.md +++ b/propose/HINTS-V3-PROPOSE.md @@ -1,6 +1,6 @@ # HINTS-V3 — kind- and direction-aware empty-result hints driven by EDGE_SCHEMA -**Status**: draft +**Status**: locked — implementing via SCHEMA-V2 PR-D (plan: [`plans/PLAN-HINTS-V3.md`](../plans/PLAN-HINTS-V3.md); move to `propose/completed/` when PR-D lands) **Author**: Dmitriy Teriaev **Date**: 2026-05-16 @@ -9,7 +9,7 @@ - Replace the single generic empty-neighbors template `TPL_NEIGHBORS_EMPTY_KIND_CHECK = "0 results — check if the requested edge_types apply to this kind"` with a small family of kind- and direction-aware templates driven by `EDGE_SCHEMA` (introduced in `propose/SCHEMA-V2-PROPOSE.md` §3.4). - Each template fires by inspecting the subject node kind, the requested `direction`, and the requested `edge_types` against `EDGE_SCHEMA[edge].src` / `.dst` / `.typical_traversals` — no hardcoded edge-shape literals in `mcp_hints.py`. - New emit-side input: hints v3 reads `EdgeSpec.brownfield_resolver_sourced` (backed by `BROWNFIELD_RESOLVER_STRATEGY_SET` from SCHEMA-V2 PR-A) to fire a distinct *"absence may mean unresolved, not absent"* hint on empty results. That complements (does not replace) the v2 `TPL_NEIGHBORS_FUZZY_STRATEGY` hint on non-empty results. -- **Propose gate** (SCHEMA-V2 Decision 30): this file must be **merged to `master`** before SCHEMA-V2 PR-A starts (GitHub PR status may stay `draft` until locked). **Implementation gate**: `Status: locked` before SCHEMA-V2 PR-D merges. PR-D runs after PR-A, PR-B, and PR-C are in `master`. +- **Propose gate** (SCHEMA-V2 Decision 30): merged to `master` ([#154](https://github.com/HumanBean17/java-codebase-rag/pull/154)) before SCHEMA-V2 PR-A. **Code** ships in SCHEMA-V2 PR-D after PR-A–C are on `master`. - Re-index is already required by SCHEMA-V2 (`ONTOLOGY_VERSION` 13 → 14); HINTS-V3 does not bump it again. - Goes away: `TPL_NEIGHBORS_EMPTY_KIND_CHECK` (deleted). Stays: every existing v1/v2 template (DESCRIBE rollups, FIND, RESOLVE, fuzzy-strategy hint). - Non-obvious constraint: hints v3 must never recommend a dot-key edge label as a `neighbors()` argument (carry-over from v2 propose §7.x). All template recommendations are checked against the canonical edge list. diff --git a/propose/SCHEMA-V2-PROPOSE.md b/propose/SCHEMA-V2-PROPOSE.md index 32aff46..2f4c628 100644 --- a/propose/SCHEMA-V2-PROPOSE.md +++ b/propose/SCHEMA-V2-PROPOSE.md @@ -1,6 +1,6 @@ # SCHEMA-V2 — edges connect the nodes the edge is about (HTTP_CALLS, ASYNC_CALLS, Producer node, canonical Edge Navigation Schema) -**Status**: draft +**Status**: locked — implementing (plan: [`plans/PLAN-SCHEMA-V2.md`](../plans/PLAN-SCHEMA-V2.md); code PR-A–D in flight; move to `propose/completed/` when PR-D lands) **Author**: Dmitriy Teriaev + Computer **Date**: 2026-05-16