From 5e2d8ca9c45e21665a72e39b4c11b545099e7bb0 Mon Sep 17 00:00:00 2001 From: Dmitry Teryaev Date: Wed, 13 May 2026 16:35:23 +0300 Subject: [PATCH 1/3] add plan for describe override-axis rollup Co-authored-by: Cursor --- plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md | 159 +++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md diff --git a/plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md b/plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md new file mode 100644 index 0000000..ce59d07 --- /dev/null +++ b/plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md @@ -0,0 +1,159 @@ +# Plan: describe override-axis rollup (`edge_summary` virtual dispatch keys) + +Status: **active (planning)**. This plan implements +[`propose/DESCRIBE-OVERRIDE-ROLLUP-PROPOSE.md`](../propose/DESCRIBE-OVERRIDE-ROLLUP-PROPOSE.md). + +Depends on: **none** for graph or indexer work (read-path only). **Coordinate with landed PR-89:** [`plans/completed/PLAN-DESCRIBE-MEMBER-EDGE-ROLLUP.md`](completed/PLAN-DESCRIBE-MEMBER-EDGE-ROLLUP.md) — `_edge_summary_for_node` in `mcp_v2.py` already threads `kind` + `row` and merges type-side `member_edge_rollup_for`. This rollout adds a **disjoint** branch for **method** symbols only (not constructors — short-circuit). Type and method branches are mutually exclusive on `row["kind"]`. + +## Goal + +- When `describe` targets a **method** `Symbol` (`data.kind == "method"`), `edge_summary` may include up to four **additive** composed / virtual keys: `OVERRIDDEN_BY`, `OVERRIDDEN_BY.DECLARES_CLIENT`, `OVERRIDDEN_BY.EXPOSES`, `OVERRIDES` — semantics per the propose (dispatch-down / dispatch-up / brownfield projection counts). +- **Constructor** symbols: trigger set includes them in the propose, but the implementation **short-circuits** to `{}` (no Cypher) — constructors are not overridden in the Java sense (UC11). +- **Static** methods (`"static"` in `modifiers`): entire override rollup suppressed (UC8). +- **Omission rule:** omit each key when its relevant count is `0` (same convention as `edge_counts_for` / `member_edge_rollup_for`). +- **No** Kuzu schema change, **no** ontology bump, **no** re-index requirement, **no** new MCP tools or `describe` parameters. +- **Docs:** `docs/AGENT-GUIDE.md` — extend the `describe` section with one subsection on **override-axis** keys (walk recipes, `neighbors` rejection, edge-row counting note for `OVERRIDDEN_BY.DECLARES_CLIENT` / `OVERRIDDEN_BY.EXPOSES`). +- **Optional:** one README sentence near the `describe` row (operator visibility; same pattern as PR-89). + +## Principles (do not relitigate in review) + +- **Virtual rollup only:** keys are computed at `describe`-time from `IMPLEMENTS` / `EXTENDS` + `DECLARES` + `signature` equality; nothing is persisted. +- **Method Symbols only (practical):** type / route / client / field behaviour unchanged. **Constructors** hit the branch but return `{}` immediately in Python (zero extra queries). +- **Naming:** standalone `OVERRIDDEN_BY` / `OVERRIDES` name the virtual dispatch relation; composed keys `OVERRIDDEN_BY.` use the same dot convention as PR-89’s `DECLARES.` — but the parent axis is **virtual**, not a stored `DECLARES` hop off the described node. +- **Directions:** every override-axis entry uses `{"in": 0, "out": N}`; omit when `N == 0`. +- **Signature match:** `mover.signature = m.signature` (and same for `decl_m`) — the Kuzu `Symbol.signature` column is the only method-identity field at graph level; aligns with name+arity semantics of `_lookup_method_candidates` (see propose appendix B). +- **Depth:** exactly one `IMPLEMENTS`/`EXTENDS` hop per direction (UC13 — no transitive closure in rollup). +- **Static filter:** suppress all override-axis keys when the described method lists `"static"` in `modifiers` (populate via AST today). Prefer **`NOT list_contains(m.modifiers, 'static')`** in Cypher if the query anchors on `m`, matching repo `STRING[]` style (`kuzu_queries.py` uses `list_contains` elsewhere); alternatively gate in `mcp_v2` before calling the helper when `"static" in (row.get("modifiers") or [])`. +- **No** `OVERRIDDEN_BY.HTTP_CALLS` / `OVERRIDDEN_BY.ASYNC_CALLS` in this rollout (deferred with PR-89’s `DECLARES.HTTP_CALLS` deferral). + +## PR breakdown — overview + +| PR | Scope | Ontology bump | Files touched (approx) | Test buckets | Independent of | +| --- | --- | --- | --- | --- | --- | +| PR-1 | Read-path override rollup + docs + model field text | **none** | `kuzu_queries.py`, `mcp_v2.py`, `docs/AGENT-GUIDE.md`, optional `README.md`, `tests/test_mcp_v2_compose.py` (+ possible one-line assertion fix in existing describe test) | five new tests (exact names below) | PR-89 already on `master` | + +Landing order: **PR-1 only**. + +## Resolved design decisions + +| Topic | Decision | +| --- | --- | +| Surface | Extend `edge_summary` dict only (`has_overrides` field — out of scope; propose decision #18 deferred). | +| Trigger | `record.kind == "symbol"` and `data["kind"] == "method"` for rollup work; `constructor` → no-op without queries. | +| Query placement | New `KuzuGraph.override_axis_rollup_for(self, method_id: str) -> dict[str, dict[str, int]]` in `kuzu_queries.py` (adjacent to `member_edge_rollup_for`). | +| `OVERRIDDEN_BY` count | `len` of **distinct** override method node ids from dispatch-down query (`collect(DISTINCT mover.id)` pattern). | +| Brownfield counts | **Edge rows** outgoing `DECLARES_CLIENT` / `EXPOSES` from collected override method ids (same “edge rows not distinct methods” note as PR-89). Implement via `UNWIND $ids AS mid MATCH (x:Symbol {id: mid})-[e:REL]->() RETURN count(e)` (or two queries) — prefer one helper used by both rels to avoid drift. | +| Merge point | `_edge_summary_for_node`: after `edge_counts_for`, `elif` **method** branch: `summary.update(graph.override_axis_rollup_for(node_id))`. **Do not** run type rollup and method rollup for the same node (disjoint `kind` sets). | +| `neighbors` | Keys remain invalid `EdgeType` literals; existing Pydantic adapter rejects unknown strings. | +| README / re-index | No “Re-index required” callout; optional README one-liner. | + +--- + +# PR-1 — Override-axis synthetic keys for method Symbols + +## File-by-file changes + +### 1. `kuzu_queries.py` + +- Add `KuzuGraph.override_axis_rollup_for(self, method_id: str) -> dict[str, dict[str, int]]`: + - **Dispatch-down:** Cypher as in propose §3.3 / appendix A: `(m)<-[:DECLARES]-(t)`, `(impl)-[:IMPLEMENTS|EXTENDS]->(t)`, `(impl)-[:DECLARES]->(mover)` with `mover.signature = m.signature`, `mover.id <> m.id`, and **not** static on **m** (the described declaration / default method). + - Return `collect(DISTINCT mover.id)` (or equivalent) into Python; if non-empty, set `OVERRIDDEN_BY` to `{"in": 0, "out": len(impl_ids)}` (use **distinct id count**, matching propose appendix which uses `len(impls)` after distinct collect). + - **Brownfield:** from that id list, count all outgoing `DECLARES_CLIENT` and `EXPOSES` edges (separate aggregates); emit `OVERRIDDEN_BY.DECLARES_CLIENT` / `OVERRIDDEN_BY.EXPOSES` only when counts > 0. + - **Dispatch-up:** symmetric walk `(m)<-[:DECLARES]-(impl)`, `(impl)-[:IMPLEMENTS|EXTENDS]->(parent)`, `(parent)-[:DECLARES]->(decl_m)` with `decl_m.signature = m.signature`, `decl_m.id <> m.id`. Count **distinct** `decl_m.id` for `OVERRIDES` (UC7 allows `out: 2` — no dedup across declarations). + - **Static on described method:** if using Cypher-only gating, anchor `WHERE NOT list_contains(COALESCE(m.modifiers, []), 'static')` (verify `COALESCE` / empty-list behaviour against Kuzu version used in CI) for both directions; or skip helper entirely from `mcp_v2` when modifiers contain `static`. +- **Dispatch-up for static m:** UC8 requires rollup silent; ensure the dispatch-up branch does not emit `OVERRIDES` for static interface methods when that would contradict the table — gating the **whole** helper on static `m` matches propose UC8 “all omitted”. + +### 2. `mcp_v2.py` + +- Add `_METHOD_SYMBOL_KINDS_FOR_OVERRIDE_ROLLUP = frozenset({"method"})` (constructors handled by absence from this set, or explicit `if sym_kind == "constructor": pass`). +- In `_edge_summary_for_node`, after the existing type rollup `if`: + - `elif kind == "symbol" and str(row.get("kind") or "") in _METHOD_SYMBOL_KINDS_FOR_OVERRIDE_ROLLUP:` → `summary.update(graph.override_axis_rollup_for(node_id))`. +- Extend **`NodeRecord.edge_summary`** `Field(description=...)` to mention override-axis keys (`OVERRIDDEN_BY`, `OVERRIDDEN_BY.DECLARES_CLIENT`, `OVERRIDDEN_BY.EXPOSES`, `OVERRIDES`) for **method** symbols, still stressing that dotted / virtual keys are not `EdgeType` literals for `neighbors`. + +### 3. `docs/AGENT-GUIDE.md` + +- Under `#### describe`, after the existing **Composed `edge_summary` keys (type Symbols)** block, add **Override-axis keys (method Symbols)** using propose §3.4 text (walk pattern, Pydantic rejection, counting semantics for composed brownfield keys). + +### 4. `README.md` (optional) + +- One sentence: `describe`’s `edge_summary` may include override-axis virtual keys on method symbols; pointer to AGENT-GUIDE. + +### 5. `tests/test_mcp_v2_compose.py` + +Add **exactly** these five tests (propose §6): + +1. `test_describe_interface_method_with_annotated_impl_emits_rollup` +2. `test_describe_concrete_override_emits_overrides_rollup` +3. `test_describe_method_no_overrides_silent` +4. `test_describe_abstract_method_with_route_override_emits_exposes` +5. `test_describe_interface_method_diamond_override_counts_once_per_upstream` + +**Fixture strategy:** + +- Prefer **oracle Cypher** on the session `kuzu_graph` (same style as existing `test_describe_class_with_brownfield_clients_emits_composed_key`) to locate `(interface_method)-…-(impl_method)` chains with `signature` match and optional `DECLARES_CLIENT` / `EXPOSES` on the impl side; assert `describe_v2`’s `edge_summary` matches oracle counts for `OVERRIDDEN_BY` and composed keys. +- If `tests/bank-chat-system` lacks a required shape (e.g. clean **diamond** UC7, or abstract + partial route override UC5), add a **minimal** extra fixture tree under `tests/fixtures/` (not special-cased in production code) and a session-local or builder-backed graph — do **not** weaken tests to vacuous passes (see `tests/README.md`). +- **UC10 / signature erasure:** during implementation, confirm `Symbol.signature` stores erased form consistent between interface and impl; if a fixture mismatch appears, fix fixture or document one-line limitation in AGENT-GUIDE — no schema change in this PR. + +### 6. Regression guard: existing `test_describe_method_symbol_no_composed_keys` + +- That test proves **type-rolloup** `DECLARES.*` keys stay off method nodes. After this PR, the same method may legitimately show `OVERRIDDEN_BY` / `OVERRIDES`. +- **Amend assertions** to only require absence of keys `DECLARES.DECLARES_CLIENT` and `DECLARES.EXPOSES` (unchanged intent). Do not require zero dot-keys globally. + +## Tests for PR-1 + +1. `test_describe_interface_method_with_annotated_impl_emits_rollup` +2. `test_describe_concrete_override_emits_overrides_rollup` +3. `test_describe_method_no_overrides_silent` +4. `test_describe_abstract_method_with_route_override_emits_exposes` +5. `test_describe_interface_method_diamond_override_counts_once_per_upstream` + +## Definition of done (PR-1) + +- [ ] `override_axis_rollup_for` exists; returns only positive-count keys; static described methods yield `{}`. +- [ ] `describe_v2` merges rollup for eligible **method** symbols; constructors unchanged from pre-rollout **direct** edge counts (no override keys). +- [ ] `neighbors_v2(..., edge_types=["OVERRIDDEN_BY"])` still fails validation at the Pydantic boundary. +- [ ] Five tests above pass; existing type-rollup tests still pass; `test_describe_method_symbol_no_composed_keys` assertion scope updated as above. +- [ ] `.venv/bin/ruff check .` clean. +- [ ] `.venv/bin/python -m pytest tests -v` green (no heavy gate). +- [ ] AGENT-GUIDE updated; README updated if the optional bullet is taken. + +## Implementation step list + +| # | Step | File(s) | Done when | +| --- | --- | --- | --- | +| 1 | Implement `override_axis_rollup_for` (dispatch-down, dispatch-up, brownfield counts) | `kuzu_queries.py` | Manual `describe` on a known interface method from bank-chat matches oracle | +| 2 | Wire `elif` method branch in `_edge_summary_for_node`; extend `NodeRecord` description | `mcp_v2.py` | Interface + impl scenario shows expected keys | +| 3 | AGENT-GUIDE + optional README | docs | Text matches propose §3.4 | +| 4 | Five new tests + fix `test_describe_method_symbol_no_composed_keys` | `tests/test_mcp_v2_compose.py` | `pytest` green | + +--- + +## Cross-PR risks and mitigations + +| # | Risk | Severity | Mitigation | +| --- | --- | --- | --- | +| 1 | `test_describe_method_symbol_no_composed_keys` starts failing or mis-documents intent | Medium | Restrict assertions to `DECLARES.*` absence only (see above). | +| 2 | Kuzu `IN` vs `list_contains` for `STRING[]` modifiers | Low | Use `list_contains` consistently with `kuzu_queries.py` / `mcp_v2` filters. | +| 3 | `signature` mismatch generic erasure (UC10) | Medium | Verify on fixture; adjust test oracle or document — no silent wrong counts without investigation. | +| 4 | Wide interfaces (`Runnable`-like fanout) | Low | Accept one bounded query per describe; micro-check latency on bank-chat if concerned (propose §8). | +| 5 | Diamond / multi-interface semantics surprise operators | Low | AGENT-GUIDE documents `OVERRIDES` counts upstream declarations (UC7). | + +## Out of scope + +- Persisted `OVERRIDES` / `OVERRIDDEN_BY` relationship types in Kuzu. +- Changing `CALLS` targets or call-graph resolution. +- Scalar columns on `Symbol` rows for override counts. +- Transitive multi-hop override closure inside one `describe`. +- `OVERRIDDEN_BY.HTTP_CALLS` / `OVERRIDDEN_BY.ASYNC_CALLS`. +- Rollup on Field / Class / Route / Client nodes. +- `has_overrides` boolean (deferred with PR-89 member-predicate pattern). + +## Whole-plan done definition + +1. Propose §3 surface behaviour matches `describe_v2` output on covered scenarios (UC1, UC2, UC5–UC7, UC6 silent). +2. Documentation and Pydantic field descriptions tell agents not to pass virtual keys to `neighbors`. +3. All default `pytest tests` pass; propose moved to `propose/completed/` only **after** the PR merges (repo convention). + +## Tracking + +- `PR-1`: _pending_ From 955d1f6d33555d20c0e705d2145386001677622d Mon Sep 17 00:00:00 2001 From: Dmitry Teryaev Date: Wed, 13 May 2026 16:38:16 +0300 Subject: [PATCH 2/3] docs: add plan for reprocess split rollout --- plans/PLAN-REPROCESS-SPLIT.md | 167 ++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 plans/PLAN-REPROCESS-SPLIT.md diff --git a/plans/PLAN-REPROCESS-SPLIT.md b/plans/PLAN-REPROCESS-SPLIT.md new file mode 100644 index 0000000..94a39c8 --- /dev/null +++ b/plans/PLAN-REPROCESS-SPLIT.md @@ -0,0 +1,167 @@ +# Plan: reprocess selective phase rebuild (`--vectors-only` / `--graph-only`) + +Status: **active (planning)**. This plan implements +[`propose/REPROCESS-SPLIT-PROPOSE.md`](../propose/REPROCESS-SPLIT-PROPOSE.md). + +Depends on: **none**. + +## Goal + +- Add selective rebuild control to `java-codebase-rag reprocess` via + mutually-exclusive flags: `--vectors-only` and `--graph-only`. +- Keep no-flag behavior aligned to current lifecycle semantics: vectors first, + graph second, with no new subcommands. +- Make partial rebuilds explicit and operable by emitting a drift warning on + stderr while preserving a binary success/failure exit contract. +- Extend JSON output additively with `phases_run` so automation can + disambiguate "skipped" vs "not executed due to failure". + +## Principles (do not relitigate in review) + +- **Single verb:** scope stays on `reprocess`; no new lifecycle command names. +- **Default path stability:** `reprocess` with no selective flag still runs both + phases in current order and remains the recommended coherence operation. +- **Strict mutual exclusion:** parser rejects `--vectors-only --graph-only` + before any subprocess work. +- **Drift is warned, not blocked:** partial runs print one stderr warning naming + the non-rebuilt store; no hard refusal and no drift-specific exit code. +- **Stage isolation:** vectors-only must not spawn graph builder; graph-only + must not spawn cocoindex. +- **Exit semantics by requested phase:** exit `1` for any requested-phase build + failure, exit `2` only for usage/setup failures (invalid args, missing binary, + phase never spawned). + +## PR breakdown - overview + +| PR | Scope | Ontology bump | Files touched (approx) | Test buckets | Independent of | +| --- | --- | --- | --- | --- | --- | +| PR-RS1 | Add selective `reprocess` flags, payload field, drift warnings, docs, and CLI tests | none | 5-6 | CLI unit + lifecycle integration regression | yes | + +Landing order: **RS1**. + +## Resolved design decisions + +| Topic | Decision | +| --- | --- | +| Flag shape | Use `--vectors-only` and `--graph-only` as argparse mutually-exclusive options on `reprocess`. | +| Output model | Add `phases_run: list[Literal["vectors","graph"]]` to `RefreshIndexOutput` (additive contract). | +| No-flag path | Continue using `server.run_refresh_pipeline(...)` to preserve orchestration; add `phases_run=["vectors","graph"]` in that path. | +| Selective path wiring | Use `java_codebase_rag.pipeline.run_cocoindex_update` and `run_build_ast_graph` directly from `cli.py`; skip lazy `import server` for selective paths. | +| Drift communication | Emit one stderr line after successful partial run; `--quiet` does not suppress this warning. | +| Exit mapping rewrite | Replace current `return 2 if payload.get("exit_code") is None else 1` logic with requested-phase-aware mapping. | +| Pretty output | Keep existing pretty/JSON auto-emission behavior; rely on payload + warning line without adding a new renderer mode. | + +--- + +# PR-RS1 - selective reprocess phases and contract-safe output + +## File-by-file changes + +### 1. `java_codebase_rag/cli.py` + +- Extend `reprocess` subparser: + - add mutually-exclusive group with `--vectors-only` and `--graph-only`; + - refresh description text to reflect full-or-selective behavior. +- Refactor `_cmd_reprocess(args)`: + - branch into three explicit flows: default both, vectors-only, graph-only; + - for vectors-only, run `run_cocoindex_update(..., full_reprocess=True, quiet=...)`; + - for graph-only, run `run_build_ast_graph(..., verbose=not quiet, env=...)`; + - emit payload with `phases_run` reflecting actually requested/ran phases; + - print partial-run drift warning to stderr only after successful selective run; + - rewrite final CLI return mapping so graph-only non-zero builder exit returns `1`, not `2`. +- Keep `refresh` alias behavior unchanged (still warns + rewrites to `reprocess`). + +### 2. `server.py` + +- Extend `RefreshIndexOutput` model with additive `phases_run` field. +- Populate `phases_run=["vectors","graph"]` on no-flag refresh path success/failure + where phases are invoked (and `[]` for early pre-spawn failures where appropriate). + +### 3. `docs/JAVA-CODEBASE-RAG-CLI.md` + +- Update `reprocess` section with new selective invocations and warning behavior. +- Document mutually-exclusive usage error shape and expected exit behavior for + selective build failures vs preflight failures. + +### 4. `README.md` + +- Update lifecycle command summary row for `reprocess` to mention selective flags. +- Keep wording consistent with operator guidance already referencing reprocess. + +### 5. `tests/test_java_codebase_rag_cli.py` + +- Add focused CLI tests for selective reprocess: + 1. `test_reprocess_vectors_only_skips_graph` + 2. `test_reprocess_graph_only_skips_vectors` + 3. `test_reprocess_mutually_exclusive_flags_rejected` + 4. `test_reprocess_graph_only_build_failure_returns_exit_1` + 5. `test_reprocess_vectors_only_emits_graph_stale_warning` + 6. `test_reprocess_graph_only_emits_vectors_stale_warning` +- Preserve and keep green existing integration anchor: + - `test_cli_reprocess_builds_kuzu_path` (no-flag regression coverage). + +## Tests for PR-RS1 + +1. `test_reprocess_vectors_only_skips_graph` +2. `test_reprocess_graph_only_skips_vectors` +3. `test_reprocess_mutually_exclusive_flags_rejected` +4. `test_reprocess_graph_only_build_failure_returns_exit_1` +5. `test_reprocess_vectors_only_emits_graph_stale_warning` +6. `test_reprocess_graph_only_emits_vectors_stale_warning` +7. `test_cli_reprocess_builds_kuzu_path` (existing regression anchor) + +## Definition of done (PR-RS1) + +- [ ] `reprocess --help` shows mutually-exclusive selective flags. +- [ ] `--vectors-only` runs only cocoindex full reprocess path and never invokes graph builder. +- [ ] `--graph-only` runs only graph builder and never invokes cocoindex. +- [ ] Selective success emits one drift warning to stderr naming the non-rebuilt store. +- [ ] JSON payload includes `phases_run` with values matching requested execution. +- [ ] Graph-only build failure exits with code `1` (not `2`). +- [ ] No-flag `reprocess` lifecycle test remains green. +- [ ] `python -m pytest tests/test_java_codebase_rag_cli.py -q` passes. + +## Implementation step list + +| # | Step | File(s) | Done when | +| - | - | - | - | +| 1 | Add parser flags + update help text | `java_codebase_rag/cli.py` | `reprocess --help` shows `[--vectors-only | --graph-only]` | +| 2 | Refactor `_cmd_reprocess` flow split | `java_codebase_rag/cli.py` | Three explicit modes work with correct payload + return mapping | +| 3 | Extend refresh payload model | `server.py` | `RefreshIndexOutput` serializes `phases_run` consistently | +| 4 | Add/adjust CLI docs | `docs/JAVA-CODEBASE-RAG-CLI.md`, `README.md` | Docs match new CLI behavior and warnings | +| 5 | Add selective CLI tests | `tests/test_java_codebase_rag_cli.py` | New tests pass locally and anchor exit-code semantics | + +--- + +# Cross-PR risks and mitigations + +| # | Risk | Severity | Mitigation | +| --- | --- | --- | --- | +| 1 | Exit code regression for graph-only failure (`None` mistakenly mapped to `2`) | High | Add dedicated failing-graph selective test and branch-aware exit logic. | +| 2 | JSON consumer ambiguity when `graph_exit_code` or `exit_code` is `None` | Medium | Require `phases_run` in payload and document consumer branching pattern. | +| 3 | Selective paths accidentally pull heavy `server` imports | Medium | Keep selective branches on lightweight `pipeline` helpers only. | +| 4 | Drift warning becomes noisy or suppressible unexpectedly | Low | Emit exactly one deterministic line and keep independent from `--quiet`. | +| 5 | Help/docs mismatch with parser behavior | Low | Update parser description and CLI docs in same PR with usage test coverage. | + +# Out of scope + +- Splitting `init` into selective modes. +- Splitting `increment` into selective modes. +- Introducing drift detection (`--detect-drift`) or a drift gate (`--allow-drift`). +- Parallelizing vectors and graph rebuild phases. +- Adding new lifecycle verbs or `--phases=` syntax. +- Changing MCP tool surface (`search`, `find`, `describe`, `neighbors`). + +# Whole-plan done definition + +1. Selective reprocess flags are available and mutually exclusive, with phase + isolation enforced. +2. Exit semantics are correct for requested-phase failures and setup/usage errors. +3. Payload contract is additive (`phases_run`) and documented for operators. +4. CLI docs and README lifecycle summary reflect selective reprocess behavior. +5. CLI test suite includes selective coverage and keeps existing no-flag + regression anchor green. + +# Tracking + +- `PR-RS1`: _pending_ From 830e3e95f53509a31346a1b0c1d224c70762d3e0 Mon Sep 17 00:00:00 2001 From: Dmitry Teryaev Date: Wed, 13 May 2026 16:39:16 +0300 Subject: [PATCH 3/3] address pr 107 review notes on plan and propose Co-authored-by: Cursor --- plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md | 5 ++--- propose/DESCRIBE-OVERRIDE-ROLLUP-PROPOSE.md | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md b/plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md index ce59d07..badba80 100644 --- a/plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md +++ b/plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md @@ -60,8 +60,7 @@ Landing order: **PR-1 only**. - Return `collect(DISTINCT mover.id)` (or equivalent) into Python; if non-empty, set `OVERRIDDEN_BY` to `{"in": 0, "out": len(impl_ids)}` (use **distinct id count**, matching propose appendix which uses `len(impls)` after distinct collect). - **Brownfield:** from that id list, count all outgoing `DECLARES_CLIENT` and `EXPOSES` edges (separate aggregates); emit `OVERRIDDEN_BY.DECLARES_CLIENT` / `OVERRIDDEN_BY.EXPOSES` only when counts > 0. - **Dispatch-up:** symmetric walk `(m)<-[:DECLARES]-(impl)`, `(impl)-[:IMPLEMENTS|EXTENDS]->(parent)`, `(parent)-[:DECLARES]->(decl_m)` with `decl_m.signature = m.signature`, `decl_m.id <> m.id`. Count **distinct** `decl_m.id` for `OVERRIDES` (UC7 allows `out: 2` — no dedup across declarations). - - **Static on described method:** if using Cypher-only gating, anchor `WHERE NOT list_contains(COALESCE(m.modifiers, []), 'static')` (verify `COALESCE` / empty-list behaviour against Kuzu version used in CI) for both directions; or skip helper entirely from `mcp_v2` when modifiers contain `static`. -- **Dispatch-up for static m:** UC8 requires rollup silent; ensure the dispatch-up branch does not emit `OVERRIDES` for static interface methods when that would contradict the table — gating the **whole** helper on static `m` matches propose UC8 “all omitted”. + - **Static on described method (UC8):** use one **whole-helper** gate so static `m` returns `{}` with no queries (Python: bail out before Cypher) or a single top-level `MATCH (m:Symbol {id: $id}) WHERE NOT list_contains(COALESCE(m.modifiers, []), 'static')` wrapper — never run dispatch-down, brownfield counts, or dispatch-up alone. That keeps `OVERRIDES` off static interface methods as well as the declaration-side keys. ### 2. `mcp_v2.py` @@ -96,7 +95,7 @@ Add **exactly** these five tests (propose §6): ### 6. Regression guard: existing `test_describe_method_symbol_no_composed_keys` -- That test proves **type-rolloup** `DECLARES.*` keys stay off method nodes. After this PR, the same method may legitimately show `OVERRIDDEN_BY` / `OVERRIDES`. +- That test proves **type-rollup** `DECLARES.*` keys stay off method nodes. After this PR, the same method may legitimately show `OVERRIDDEN_BY` / `OVERRIDES`. - **Amend assertions** to only require absence of keys `DECLARES.DECLARES_CLIENT` and `DECLARES.EXPOSES` (unchanged intent). Do not require zero dot-keys globally. ## Tests for PR-1 diff --git a/propose/DESCRIBE-OVERRIDE-ROLLUP-PROPOSE.md b/propose/DESCRIBE-OVERRIDE-ROLLUP-PROPOSE.md index ddd66d5..0be69f4 100644 --- a/propose/DESCRIBE-OVERRIDE-ROLLUP-PROPOSE.md +++ b/propose/DESCRIBE-OVERRIDE-ROLLUP-PROPOSE.md @@ -191,9 +191,9 @@ In `docs/AGENT-GUIDE.md` under `describe`: 2. Run the dispatch-down walk (declarations → impls) and dispatch-up walk (override → declarations) using `signature` equality. 3. Count `DECLARES_CLIENT` / `EXPOSES` outgoing edges on the collected override method ids. 4. Emit non-zero keys only. - - In `mcp_v2._edge_summary_for_node` (or `describe_v2`), check `kind == "symbol"` AND `data.kind in {"method", "constructor"}`. If so, call the rollup helper and merge non-zero results. + - In `mcp_v2._edge_summary_for_node` (or `describe_v2`), check `kind == "symbol"` AND `data.kind == "method"`, then merge non-zero rollup results (constructors: no helper call — see [`plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md`](../plans/PLAN-DESCRIBE-OVERRIDE-ROLLUP.md) §PR-1). - Update `DescribeOutput` documentation / pydantic field-level description to mention method-side rollup keys. -- **Test summary**: 5 new tests in `tests/test_mcp_v2.py`: +- **Test summary**: 5 new tests in `tests/test_mcp_v2_compose.py` (same module as PR-89 describe `edge_summary` tests; avoids splitting describe rollups across files): - `test_describe_interface_method_with_annotated_impl_emits_rollup` — UC1 fixture. - `test_describe_concrete_override_emits_overrides_rollup` — UC2 fixture. - `test_describe_method_no_overrides_silent` — UC6 fixture (no upstream, no impls).