|
| 1 | +# Agent task prompts — Faster init/increment (PR-P1 → PR-P3) |
| 2 | + |
| 3 | +Status: **active**. One self-contained prompt per PR. Copy the prompt verbatim |
| 4 | +into the agent, attach the files in its `@-files` block, and let it execute. |
| 5 | + |
| 6 | +> **Revised after the PR #339 subagent review.** PRs are split by **write-function**, |
| 7 | +> not by path — the graph write helpers are shared between the full and incremental |
| 8 | +> paths, so converting one accelerates both. Sentinel greps were corrected (the |
| 9 | +> PR-P3 "must return zero" grep previously over-matched once-per-run sites). |
| 10 | +
|
| 11 | +**Workflow per PR:** |
| 12 | + |
| 13 | +1. Create the branch named in the prompt off the stated base. |
| 14 | +2. Read the cited plan section in full **before** writing code. |
| 15 | +3. Implement step-by-step; run the listed tests after each step. |
| 16 | +4. Run the sentinel greps — every "must return zero" line must be empty, every |
| 17 | + "must be non-zero" line must hit. |
| 18 | +5. Paste the manual-evidence output into the PR description. |
| 19 | +6. Open a PR with the exact title in the Definition of Done. |
| 20 | + |
| 21 | +**Universal rules for every prompt:** |
| 22 | + |
| 23 | +- Use only `.venv/bin/python` and `.venv/bin/pip` (never system python/pip). |
| 24 | +- `server.py` is stdio — never write to stdout from anything reachable by a tool handler. |
| 25 | +- Do not add a cocoindex dependency outside `java_index_flow_lancedb.py`. |
| 26 | +- The plan is the source of truth — if this prompt and the plan disagree, the plan wins. |
| 27 | +- Do not touch any file outside the prompt's `@-files` + the test files it names. If you think an adjacent file must change, **stop and ask**. |
| 28 | +- Do not loosen any existing test assertion to make it pass. |
| 29 | +- Breaking changes are allowed; no compatibility shims. |
| 30 | + |
| 31 | +--- |
| 32 | + |
| 33 | +## PR-P1 — Bulk `COPY FROM` for `_write_edges` (shared; the ~250s prize) |
| 34 | + |
| 35 | +**Branch:** `perf/bulk-graph-writes-p1` off `master`. |
| 36 | +**Base:** `master`. |
| 37 | +**Plan section:** `plans/active/PLAN-INIT-INCREMENT-PERF.md` § PR-P1 (read this first). |
| 38 | +**Estimated diff size:** medium (one module + tests + a fixture). |
| 39 | + |
| 40 | +**Attach (`@-files`):** |
| 41 | + |
| 42 | +- `@build_ast_graph.py` (focus: `_write_edges:3244`, `_node_row:2994`, `_SCHEMA_*:2812-2940`, `_callee_declaring_role_at_write:1647`, `seen_calls:3282`, `seen_ucs:3317`, `_populate_declares_rows:3189`) |
| 43 | +- `@propose/active/INIT-INCREMENT-PERF-PROPOSE.md` (design; on PR #338's branch — if absent, the staging invariants are inlined in the plan §PR-P1) |
| 44 | +- `@tests/test_ast_graph_build.py` (regression net + where new tests go) |
| 45 | +- `@tests/test_incremental_graph.py` (`_write_edges` is shared — incremental regression is binding) |
| 46 | +- `@tests/_builders.py` (graph-build helpers) |
| 47 | + |
| 48 | +**Prompt:** |
| 49 | + |
| 50 | +```` |
| 51 | +You are implementing PR-P1 from `plans/active/PLAN-INIT-INCREMENT-PERF.md`. |
| 52 | +
|
| 53 | +Read the **PR-P1** section in full. The plan wins if this prompt disagrees. |
| 54 | +
|
| 55 | +KEY FACT: `_write_edges` (build_ast_graph.py:3244) is a SHARED helper called by |
| 56 | +BOTH the full path (write_ladybug:3926) and the incremental path (:805). So |
| 57 | +converting it accelerates both — there is no "full-only" edge conversion. PR-P1 |
| 58 | +converts ONLY `_write_edges` (+ adds the `_bulk_copy` primitive). Nodes, routes, |
| 59 | +clients/producers, and GraphMeta are PR-P2. |
| 60 | +
|
| 61 | +## Scope |
| 62 | +
|
| 63 | +1. **Step-1 spike (first commit):** confirm the exact REL `COPY FROM` column |
| 64 | + naming + `pa.Table.from_pylist` typing with a throwaway 2-Symbol + 1-CALLS |
| 65 | + toy. Record the working incantation in the `_bulk_copy` docstring. |
| 66 | +2. Add `_bulk_copy(conn, table_name, columns, rows)` + the `_REL_*_COLUMNS` / |
| 67 | + column constants (REL tables list FROM/TO first; match `_SCHEMA_*` order). |
| 68 | +3. Convert `_write_edges` to per-edge-type row staging (apply the SAME |
| 69 | + `seen_calls`/`seen_ucs` dedup and SAME `_callee_declaring_role_at_write` |
| 70 | + lookup at staging, before appending), then `_bulk_copy` each REL table. |
| 71 | + Bulk-load UnresolvedCallSite NODE rows before UNRESOLVED_AT edges (Symbol |
| 72 | + nodes are already loaded by `_write_nodes`). |
| 73 | +4. Delete the dead module constants `_CREATE_EXT/IMPL/INJ/DECL/OVERRIDES/CALL` |
| 74 | + and the local `_CREATE_UNRESOLVED`/`_CREATE_UNRESOLVED_AT` (defined inside |
| 75 | + `_write_edges` at :3307/:3313 — removed with the rewrite). |
| 76 | +5. Generate + commit `tests/fixtures/graph_baseline_bank_chat.json` from the |
| 77 | + last per-row `_write_edges` build before removal. |
| 78 | +6. Add the four named tests. |
| 79 | +
|
| 80 | +## Out of scope (do NOT touch) |
| 81 | +
|
| 82 | +- Node writes (`_write_nodes`, `_write_nodes_impl`, `_write_nodes_merge`, |
| 83 | + `_CREATE_SYMBOL`, `_MERGE_SYMBOL`) — PR-P2. |
| 84 | +- Routes/clients/producers/calls (`_write_routes_and_exposes`, |
| 85 | + `_write_clients_producers_and_calls`, and their `_CREATE_*` constants |
| 86 | + including `_CREATE_CLIENT`/`_CREATE_PRODUCER`/`_CREATE_ROUTE` etc.) — PR-P2. |
| 87 | +- `_write_meta` / GraphMeta — leave the MERGE alone (PR-P2 also leaves it). |
| 88 | +- `java_index_flow_lancedb.py`, `path_filtering.py`, `server.py`. |
| 89 | +- Any schema/ontology/re-index change. CSV or Parquet-file staging. |
| 90 | +
|
| 91 | +If you find yourself wanting to touch any of the above, **stop and ask**. |
| 92 | +
|
| 93 | +## Deliverables |
| 94 | +
|
| 95 | +1. `_bulk_copy` helper + column-order constants. |
| 96 | +2. `_write_edges` stages per-type rows and bulk-loads (CALLS dedup + callee_declaring_role at staging; UnresolvedCallSite before UNRESOLVED_AT). |
| 97 | +3. Dead `_CREATE_EXT/IMPL/INJ/DECL/OVERRIDES/CALL` + locals removed. |
| 98 | +4. `tests/fixtures/graph_baseline_bank_chat.json` committed. |
| 99 | +5. Four new tests in `tests/test_ast_graph_build.py`. |
| 100 | +
|
| 101 | +## Tests |
| 102 | +
|
| 103 | +Run, all must pass: |
| 104 | +``` |
| 105 | +.venv/bin/python -m pytest tests/test_ast_graph_build.py tests/test_incremental_graph.py -v |
| 106 | +.venv/bin/python -m pytest tests/test_bank_chat_brownfield_integration.py tests/test_call_edges_e2e.py -q |
| 107 | +.venv/bin/ruff check . |
| 108 | +``` |
| 109 | +
|
| 110 | +Sentinel greps — **must return zero**: |
| 111 | +``` |
| 112 | +grep -nE "_CREATE_(EXT|IMPL|INJ|DECL|OVERRIDES|CALL|UNRESOLVED|UNRESOLVED_AT)\b" build_ast_graph.py |
| 113 | +``` |
| 114 | +
|
| 115 | +Sentinel greps — **must be non-zero** (guards against over-deletion; these belong to PR-P2 / are retained): |
| 116 | +``` |
| 117 | +grep -n "_MERGE_SYMBOL\b" build_ast_graph.py # node upsert, kept until PR-P2 |
| 118 | +grep -n "_CREATE_CLIENT\b" build_ast_graph.py # routes/clients, PR-P2 |
| 119 | +grep -n "MERGE (r:Route" build_ast_graph.py # pass5/6 dedup, kept |
| 120 | +grep -n "COPY .*FROM \$rows" build_ast_graph.py # bulk path present |
| 121 | +``` |
| 122 | +
|
| 123 | +## Manual evidence (paste in PR description) |
| 124 | +
|
| 125 | +```bash |
| 126 | +rm -rf /tmp/p1 && .venv/bin/python build_ast_graph.py \ |
| 127 | + --source-root tests/bank-chat-system \ |
| 128 | + --ladybug-path /tmp/p1/code_graph.lbug --verbose |
| 129 | +.venv/bin/java-codebase-rag meta --source-root tests/bank-chat-system --index-dir /tmp/p1 |
| 130 | +``` |
| 131 | +Expected: meta `counts_json` + node/edge counts identical to a pre-PR per-row |
| 132 | +build (paste both). Note the graph-write phase timing from `JCIRAG_PROGRESS` |
| 133 | +lines vs the pre-PR baseline. |
| 134 | +
|
| 135 | +## Definition of Done |
| 136 | +
|
| 137 | +- [ ] Step-1 spike result recorded in `_bulk_copy` docstring. |
| 138 | +- [ ] `_write_edges` stages per-type rows (CALLS dedup + callee_declaring_role at staging); UnresolvedCallSite bulk-loaded before UNRESOLVED_AT. |
| 139 | +- [ ] `_CREATE_EXT/IMPL/INJ/DECL/OVERRIDES/CALL` + local `_CREATE_UNRESOLVED/_UNRESOLVED_AT` deleted. |
| 140 | +- [ ] `test_bulk_write_edges_match_per_row_baseline`, `test_bulk_write_is_deterministic_double_build`, `test_bulk_write_preserves_calls_dedup_and_callee_declaring_role`, `test_bulk_write_empty_rel_table_is_noop` pass. |
| 141 | +- [ ] Full `test_ast_graph_build.py` + `test_incremental_graph.py` pass unchanged. |
| 142 | +- [ ] Sentinel greps: zero where required, non-zero where required. |
| 143 | +- [ ] `.venv/bin/ruff check .` clean; benchmark in PR description. |
| 144 | +- [ ] PR title: `perf(graph): bulk COPY FROM for _write_edges (PR-P1)`. |
| 145 | +```` |
| 146 | + |
| 147 | +--- |
| 148 | + |
| 149 | +## PR-P2 — Bulk write for nodes + routes/clients/producers/calls |
| 150 | + |
| 151 | +**Branch:** `perf/bulk-graph-writes-p2` off PR-P1's branch (or `master` if PR-P1 merged). |
| 152 | +**Base:** PR-P1 merged. |
| 153 | +**Plan section:** `plans/active/PLAN-INIT-INCREMENT-PERF.md` § PR-P2 (read this first). |
| 154 | +**Estimated diff size:** medium. |
| 155 | + |
| 156 | +**Attach (`@-files`):** |
| 157 | + |
| 158 | +- `@build_ast_graph.py` (`_write_nodes_impl:3029`, `_write_nodes:3096`, `_write_nodes_merge:817`, `_CREATE_SYMBOL`, `_MERGE_SYMBOL`, `_write_routes_and_exposes:3338`, `_write_clients_producers_and_calls:3810`, the Route `MERGE (r:Route` at `:3819`, `_write_meta:3421`, `_bulk_copy` + `_REL_*_COLUMNS` from PR-P1) |
| 159 | +- `@tests/test_incremental_graph.py` (regression; add to `TestIncrementalOrchestrator`) |
| 160 | + |
| 161 | +**Prompt:** |
| 162 | + |
| 163 | +```` |
| 164 | +You are implementing PR-P2 from `plans/active/PLAN-INIT-INCREMENT-PERF.md`. |
| 165 | +Read the **PR-P2** section in full. It reuses PR-P1's `_bulk_copy` + |
| 166 | +`_REL_*_COLUMNS`. The plan wins. |
| 167 | +
|
| 168 | +## Scope |
| 169 | +
|
| 170 | +1. Convert `_write_nodes_impl` (:3029, the shared workhorse called by |
| 171 | + `_write_nodes` full + `_write_nodes_merge` incremental) to stage Symbol rows |
| 172 | + then `_bulk_copy(conn, "Symbol", NODE_COLUMNS, rows)`. Delete `_CREATE_SYMBOL` |
| 173 | + and `_MERGE_SYMBOL` (both dead once the workhorse is bulk). Do the existing |
| 174 | + `resolve_role_and_capabilities` + `type_role_by_node_id` population before |
| 175 | + staging, unchanged. |
| 176 | +2. Convert `_write_routes_and_exposes` (:3338, shared) to per-table staging + |
| 177 | + `_bulk_copy` for Route/EXPOSES/Client/Producer/DECLARES_CLIENT/DECLARES_PRODUCER/ |
| 178 | + HTTP_CALLS/ASYNC_CALLS (keep the existing `_file_by_node_id`/`_file_by_client_id`/ |
| 179 | + `_file_by_producer_id` source_file resolution). Bulk-load Route/Client/Producer |
| 180 | + NODES before the EXPOSES/DECLARES_*/HTTP_CALLS/ASYNC_CALLS edges. Delete |
| 181 | + `_CREATE_ROUTE`/`_CREATE_EXPOSES`. |
| 182 | +3. Convert `_write_clients_producers_and_calls` (:3810, incremental-only global |
| 183 | + pass5/6) Client/Producer/edge writes to per-type staging + `_bulk_copy` (keep |
| 184 | + the `member_by_id`/`client_by_id`/`producer_by_id` resolution). **Retain the |
| 185 | + `MERGE (r:Route {id:$id}) …` dedup (:3819-3828) verbatim** + add a one-line |
| 186 | + comment it is intentionally kept. Now delete the 6 shared constants — |
| 187 | + `_CREATE_CLIENT`/`_CREATE_PRODUCER`/`_CREATE_DECLARES_CLIENT`/ |
| 188 | + `_CREATE_DECLARES_PRODUCER`/`_CREATE_HTTP_CALL`/`_CREATE_ASYNC_CALL` — which |
| 189 | + are dead only after BOTH routes/exposes and clients_producers functions convert. |
| 190 | +4. Leave `_write_meta` (:3421) and its `MERGE (m:GraphMeta …)` UNTOUCHED. |
| 191 | +5. Add the two named tests as methods of `TestIncrementalOrchestrator` in |
| 192 | + `tests/test_incremental_graph.py`. |
| 193 | +
|
| 194 | +## Out of scope (do NOT touch) |
| 195 | +
|
| 196 | +- `_write_edges` (done in PR-P1). |
| 197 | +- `_write_meta` / GraphMeta MERGE — leave it. |
| 198 | +- `_delete_file_scope`, `incremental_rebuild` algorithm, dependent-expansion, |
| 199 | + crash-marker logic. |
| 200 | +- Anything outside `build_ast_graph.py` + `tests/test_incremental_graph.py`. |
| 201 | +
|
| 202 | +If you find yourself wanting to touch any of the above, **stop and ask**. |
| 203 | +
|
| 204 | +## Deliverables |
| 205 | +
|
| 206 | +1. `_write_nodes_impl` bulk; `_CREATE_SYMBOL` + `_MERGE_SYMBOL` deleted. |
| 207 | +2. `_write_routes_and_exposes` bulk; `_CREATE_ROUTE`/`_CREATE_EXPOSES` deleted. |
| 208 | +3. `_write_clients_producers_and_calls` Client/Producer/edges bulk; `MERGE (r:Route)` retained + commented; 6 shared `_CREATE_*` deleted. |
| 209 | +4. `_write_meta` untouched. |
| 210 | +5. Two new tests in `TestIncrementalOrchestrator`. |
| 211 | +
|
| 212 | +## Tests |
| 213 | +
|
| 214 | +Run, all must pass: |
| 215 | +``` |
| 216 | +.venv/bin/python -m pytest tests/test_incremental_graph.py tests/test_ast_graph_build.py -v |
| 217 | +.venv/bin/ruff check . |
| 218 | +``` |
| 219 | +
|
| 220 | +Sentinel greps — **must return zero**: |
| 221 | +``` |
| 222 | +grep -nE "_CREATE_(SYMBOL|ROUTE|EXPOSES|CLIENT|PRODUCER|DECLARES_CLIENT|DECLARES_PRODUCER|HTTP_CALL|ASYNC_CALL)\b" build_ast_graph.py |
| 223 | +grep -nE "_MERGE_SYMBOL\b" build_ast_graph.py |
| 224 | +``` |
| 225 | +
|
| 226 | +Sentinel greps — **must be non-zero** (Route dedup + GraphMeta MERGE retained; bulk present): |
| 227 | +``` |
| 228 | +grep -n "MERGE (r:Route" build_ast_graph.py |
| 229 | +grep -n "MERGE (m:GraphMeta" build_ast_graph.py |
| 230 | +grep -n "COPY .*FROM \$rows" build_ast_graph.py |
| 231 | +``` |
| 232 | +
|
| 233 | +## Manual evidence (paste in PR description) |
| 234 | +
|
| 235 | +Single-file change equivalence: |
| 236 | +```bash |
| 237 | +# set up an index, touch one file, increment (bulk), then full-rebuild the same |
| 238 | +# state (bulk) and diff graphs (node count, per-type edge counts, GraphMeta). |
| 239 | +``` |
| 240 | +Expected: incremental(bulk) == full-rebuild(bulk) for that state. Paste side-by-side counts. |
| 241 | +
|
| 242 | +## Definition of Done |
| 243 | +
|
| 244 | +- [ ] `_write_nodes_impl` bulk; `_CREATE_SYMBOL` + `_MERGE_SYMBOL` deleted. |
| 245 | +- [ ] `_write_routes_and_exposes` bulk (Route/Client/Producer before edges); `_CREATE_ROUTE`/`_CREATE_EXPOSES` deleted. |
| 246 | +- [ ] `_write_clients_producers_and_calls` Client/Producer/edges bulk; `MERGE (r:Route)` retained + commented; 6 shared `_CREATE_*` deleted. |
| 247 | +- [ ] `_write_meta` untouched. |
| 248 | +- [ ] `test_incremental_bulk_write_equivalent_to_full_rebuild`, `test_incremental_route_merge_dedup_preserved` (both in `TestIncrementalOrchestrator`) pass; full `test_incremental_graph.py` + `test_ast_graph_build.py` green. |
| 249 | +- [ ] Sentinel greps pass. |
| 250 | +- [ ] `.venv/bin/ruff check .` clean. |
| 251 | +- [ ] PR title: `perf(graph): bulk COPY FROM for nodes, routes, clients/producers (PR-P2)`. |
| 252 | +```` |
| 253 | + |
| 254 | +--- |
| 255 | + |
| 256 | +## PR-P3 — Cached `LayeredIgnore` + `is_ignored` memo |
| 257 | + |
| 258 | +**Branch:** `perf/cached-ignore-p3` off `master`. |
| 259 | +**Base:** `master` (independent of PR-P1/P2). |
| 260 | +**Plan section:** `plans/active/PLAN-INIT-INCREMENT-PERF.md` § PR-P3 (read this first). |
| 261 | +**Estimated diff size:** small. |
| 262 | + |
| 263 | +**Attach (`@-files`):** |
| 264 | + |
| 265 | +- `@java_index_flow_lancedb.py` (`ContextKey` defs `:60-72`, `coco_lifespan` provide sites `:287-306`, `process_java_file:345`/`process_sql_file:417`/`process_yaml_file:465`) |
| 266 | +- `@path_filtering.py` (`LayeredIgnore`, `_mega:334`, `_mega_build_for_rel:193`, `is_ignored:345`, `diagnose_dict:377`) |
| 267 | +- `@tests/test_path_filtering.py` (where the two memo unit tests go) |
| 268 | +- `@tests/test_lancedb_e2e.py` (HEAVY once-per-flow test) |
| 269 | + |
| 270 | +**Prompt:** |
| 271 | + |
| 272 | +```` |
| 273 | +You are implementing PR-P3 from `plans/active/PLAN-INIT-INCREMENT-PERF.md`. |
| 274 | +
|
| 275 | +Read the **PR-P3** section in full. Independent of PR-P1/P2. The plan wins. |
| 276 | +
|
| 277 | +KEY FACT: `LayeredIgnore(project_root)` appears at FIVE sites in |
| 278 | +java_index_flow_lancedb.py — :177, :351, :423, :471, :569. PR-P3 converts ONLY |
| 279 | +the three `process_*_file` sites (:351/:423/:471). The other two (:177 in |
| 280 | +`_approximate_vectors_total`, :569 in the app_main pre-walk) call |
| 281 | +`cocoindex_excluded_patterns()` ONCE PER RUN — leave them alone. |
| 282 | +
|
| 283 | +## Scope |
| 284 | +
|
| 285 | +1. Define `IGNORE = coco.ContextKey[LayeredIgnore]("java_lance_layered_ignore")` |
| 286 | + alongside `PROJECT_ROOT`/`EMBEDDER`/`LANCE_DB` (:60-72), reusing the SAME |
| 287 | + `_ck_params` (`detect_change` vs `tracked`) detection block. |
| 288 | +2. In `coco_lifespan` (:287-306), add `builder.provide(IGNORE, LayeredIgnore(root))` |
| 289 | + — built ONCE per flow run. |
| 290 | +3. In `process_java_file`/`process_sql_file`/`process_yaml_file`: add |
| 291 | + `ignore = coco.use_context(IGNORE)` and replace |
| 292 | + `LayeredIgnore(project_root).is_ignored((project_root / file.file_path.path).resolve())` |
| 293 | + with `ignore.is_ignored((project_root / file.file_path.path).resolve())`. |
| 294 | + Keep `project_root`. DO NOT touch :177 or :569. |
| 295 | +4. In `path_filtering.py` `LayeredIgnore`: add `self._mega_cache` in `__init__` |
| 296 | + and memoize `_mega(rel)` keyed by `Path(rel_project).parent.as_posix()` |
| 297 | + (`_mega_build_for_rel` reads only `dir_parts`, so this is correct). |
| 298 | +5. Add the three named tests in the right files. |
| 299 | +
|
| 300 | +## Out of scope (do NOT touch) |
| 301 | +
|
| 302 | +- `build_ast_graph.py` and the graph write path (PR-P1/P2). |
| 303 | +- The ignore *decision* logic (`_mega_build_for_rel`, `_winning_row`, negation |
| 304 | + scanning) — only memoize. |
| 305 | +- Sites :177 and :569. |
| 306 | +- Any schema/ontology/re-index change. Loosening any existing test. |
| 307 | +
|
| 308 | +If you find yourself wanting to touch any of the above, **stop and ask**. |
| 309 | +
|
| 310 | +## Deliverables |
| 311 | +
|
| 312 | +1. `IGNORE` ContextKey (version-detected) + provided once in `coco_lifespan`. |
| 313 | +2. The three `process_*_file` consume it; :177/:569 untouched. |
| 314 | +3. `_mega` memoized by directory in `LayeredIgnore`. |
| 315 | +4. Three tests: two in `tests/test_path_filtering.py`, one (HEAVY) in `tests/test_lancedb_e2e.py`. |
| 316 | +
|
| 317 | +## Tests |
| 318 | +
|
| 319 | +Run, all must pass: |
| 320 | +``` |
| 321 | +.venv/bin/python -m pytest tests/test_path_filtering.py tests/test_lancedb_e2e.py -q |
| 322 | +.venv/bin/python -m pytest tests -q -k "ignore or path_filter or vectors_progress" |
| 323 | +.venv/bin/ruff check . |
| 324 | +``` |
| 325 | +Heavy (only if you can run cocoindex e2e locally): |
| 326 | +``` |
| 327 | +JAVA_CODEBASE_RAG_RUN_HEAVY=1 .venv/bin/python -m pytest tests/test_lancedb_e2e.py -q |
| 328 | +``` |
| 329 | +
|
| 330 | +Sentinel greps — **must return zero** (matches ONLY the 3 process sites; :177/:569 use the bare constructor + `cocoindex_excluded_patterns`, not `.is_ignored`): |
| 331 | +``` |
| 332 | +grep -nE "LayeredIgnore\(project_root\)\.is_ignored" java_index_flow_lancedb.py |
| 333 | +``` |
| 334 | +
|
| 335 | +Sentinel greps — **must be non-zero**: |
| 336 | +``` |
| 337 | +grep -n "coco.use_context(IGNORE)" java_index_flow_lancedb.py # 3 sites |
| 338 | +grep -n "_mega_cache" path_filtering.py # memo present |
| 339 | +``` |
| 340 | +
|
| 341 | +## Manual evidence (paste in PR description) |
| 342 | +
|
| 343 | +With `JAVA_CODEBASE_RAG_RUN_HEAVY=1`, run the flow over a small corpus and log |
| 344 | +`id(ignore)` per file (temporary instrumentation) — confirm a single object id |
| 345 | +across all files. Then micro-benchmark `is_ignored` over N files: same-directory |
| 346 | +files hit the `_mega` cache. |
| 347 | +
|
| 348 | +## Definition of Done |
| 349 | +
|
| 350 | +- [ ] `IGNORE` ContextKey (version-detected) + provided once in `coco_lifespan`. |
| 351 | +- [ ] The three `process_*_file` consume it; :177/:569 untouched. |
| 352 | +- [ ] `_mega` memoized by directory; `is_ignored`/`diagnose_dict` results unchanged. |
| 353 | +- [ ] `test_is_ignored_mega_caches_by_directory`, `test_layered_ignore_memo_preserves_decisions` (in `tests/test_path_filtering.py`), `test_layered_ignore_provided_once_per_flow` (in `tests/test_lancedb_e2e.py`, HEAVY) pass. |
| 354 | +- [ ] Existing ignore + vectors-progress tests pass unchanged. |
| 355 | +- [ ] Sentinel greps pass. |
| 356 | +- [ ] `.venv/bin/ruff check .` clean. |
| 357 | +- [ ] PR title: `perf(vectors): lifespan-cached LayeredIgnore + is_ignored memo (PR-P3)`. |
| 358 | +```` |
| 359 | + |
| 360 | +--- |
| 361 | + |
| 362 | +## Notes for the orchestrator |
| 363 | + |
| 364 | +- **Landing order:** PR-P1 → PR-P2 (P2 needs `_bulk_copy` + `_REL_*_COLUMNS`). |
| 365 | + PR-P3 is independent (can go first, last, or between). |
| 366 | +- **Shared-helper awareness:** `_write_edges`, `_write_routes_and_exposes`, |
| 367 | + `_write_nodes_impl`, `_write_meta` are each called by BOTH paths. Converting |
| 368 | + one accelerates both — so `test_incremental_graph.py` is a binding gate for |
| 369 | + PR-P1 and PR-P2, not just PR-P2. |
| 370 | +- **Review between PRs** (`superpowers:requesting-code-review`): the equivalence |
| 371 | + harness gates P1/P2; the memo-parity test gates P3. |
| 372 | +- **Sentinel greps are binding:** a non-empty "must return zero" grep = scope |
| 373 | + leak; an empty "must be non-zero" grep = over-deletion. Either blocks merge. |
0 commit comments