Skip to content

Commit 5ac85ec

Browse files
committed
feat(apply): rename-preview barrel/reference rows; Phase B apply tests
Extend rename-preview with barrel_import_rows and reference_rows (deduped vs imports/calls/definitions). Golden rename-preview-product-card; apply harness map; cmd-apply --rows and replace-marker-kind disk e2e.
1 parent 5b4e9b2 commit 5ac85ec

7 files changed

Lines changed: 240 additions & 47 deletions

File tree

docs/plans/substrate-apply-utilization.md

Lines changed: 42 additions & 44 deletions
Large diffs are not rendered by default.

docs/testing-coverage.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
| **Agent eval** | `bun run test:agent-eval` | Probe arms vs golden ids (MCP-on vs glob/read). |
1717
| **Integration (git)** | `src/application/run-index.test.ts` | `runCodemapIndex` incremental paths: heritage + calls re-resolution, delete/reindex. |
1818
| **CLI e2e** | `src/cli/cmd-test-bench-e2e.test.ts`, `src/cli/cmd-cli-parity-e2e.test.ts` | Spawned CLI on `fixtures/minimal` (bench smoke + resource parity). |
19+
| **Apply CLI e2e** | `src/cli/cmd-apply.test.ts` | Temp project + full index: recipe dry-run/apply, `--rows`, second recipe disk apply. |
1920
| **Check** | `bun run check` | build + lint + unit + scripts + golden + agent-eval. |
2021

2122
Refresh Tier A goldens after intentional fixture or schema changes:
@@ -30,6 +31,17 @@ bun scripts/query-golden.ts --update
3031

3132
Every `templates/recipes/<id>.sql` has **≥1** scenario in `fixtures/golden/scenarios.json` with `"recipe": "<id>"`. Enforced by `query-golden-coverage-matrix.test.mjs`. List ids: `codemap query --recipes-json` or `ls templates/recipes/*.sql`.
3233

34+
### Apply-shaped recipes (diff row contract)
35+
36+
| Recipe id | Golden scenario(s) | CLI e2e (`cmd-apply.test.ts`) |
37+
| ----------------------- | -------------------------------------------------------------------------------- | -------------------------------------------- |
38+
| `rename-preview` | `rename-preview`, `rename-preview-product-card` (barrel + re-export + reference) | dry-run, `--yes` disk apply, Q6/Q7 |
39+
| `migrate-import-source` | `migrate-import-source` | dry-run |
40+
| `replace-marker-kind` | `replace-marker-kind` | `--yes` disk apply (temp project) |
41+
| `add-jsdoc-deprecated` | `add-jsdoc-deprecated` | — (query golden only; writes need `--force`) |
42+
43+
**Input modes:** recipe id (above); `--rows` JSON file (e2e); `--diff-input` (`apply-diff-input.test.ts` unit). MCP `apply_rows` shares the rows path — no separate e2e yet.
44+
3345
---
3446

3547
## Substrate tables — SQL pin-down scenarios
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[
2+
{
3+
"file_path": "src/components/shop/ProductCard.tsx",
4+
"line_start": 10,
5+
"line_end": 22,
6+
"before_pattern": "ProductCard",
7+
"after_pattern": "ProductCardNext",
8+
"location_kind": "definition",
9+
"chain_depth": 0
10+
},
11+
{
12+
"file_path": "src/components/shop/index.ts",
13+
"line_start": 4,
14+
"line_end": 4,
15+
"before_pattern": "ProductCard",
16+
"after_pattern": "ProductCardNext",
17+
"location_kind": "re_export",
18+
"chain_depth": 1
19+
},
20+
{
21+
"file_path": "src/consumer.ts",
22+
"line_start": 3,
23+
"line_end": 3,
24+
"before_pattern": "ProductCard",
25+
"after_pattern": "ProductCardNext",
26+
"location_kind": "barrel_import_specifier",
27+
"chain_depth": 1
28+
},
29+
{
30+
"file_path": "src/consumer.ts",
31+
"line_start": 26,
32+
"line_end": 26,
33+
"before_pattern": "ProductCard",
34+
"after_pattern": "ProductCardNext",
35+
"location_kind": "reference",
36+
"chain_depth": 0
37+
}
38+
]

fixtures/golden/scenarios.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@
2929
"kind": "function"
3030
}
3131
},
32+
{
33+
"id": "rename-preview-product-card",
34+
"prompt": "Rename ProductCard: barrel consumer import, re-export, and reference sites.",
35+
"recipe": "rename-preview",
36+
"params": {
37+
"old": "ProductCard",
38+
"new": "ProductCardNext",
39+
"kind": "function"
40+
}
41+
},
3242
{
3343
"id": "index-summary",
3444
"prompt": "Row counts for main tables (same SQL as --recipe index-summary)",

src/cli/cmd-apply.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,63 @@ describe("codemap apply <recipe-id> — CLI integration", () => {
273273
});
274274
});
275275

276+
describe("--rows", () => {
277+
it("applies caller-supplied rows from a JSON file", async () => {
278+
const rowsPath = join(projectRoot, "apply-rows.json");
279+
writeFileSync(
280+
rowsPath,
281+
JSON.stringify([
282+
{
283+
file_path: "src/helper.ts",
284+
line_start: 1,
285+
before_pattern: "helper",
286+
after_pattern: "worker",
287+
},
288+
]),
289+
"utf8",
290+
);
291+
const r = await runCli(["apply", "--rows", rowsPath, "--yes", "--json"], {
292+
CODEMAP_ROOT: projectRoot,
293+
});
294+
expect(r.exitCode).toBe(0);
295+
const env = JSON.parse(r.out);
296+
expect(env.mode).toBe("apply");
297+
expect(env.applied).toBe(true);
298+
expect(readFile("src/helper.ts")).toContain("function worker(");
299+
});
300+
});
301+
302+
describe("replace-marker-kind", () => {
303+
beforeEach(async () => {
304+
writeFileSync(
305+
join(projectRoot, "src", "notes.md"),
306+
"# Notes\n\nTODO: fix later\n",
307+
"utf8",
308+
);
309+
const idx = await runCli(["--full"], { CODEMAP_ROOT: projectRoot });
310+
expect(idx.exitCode).toBe(0);
311+
});
312+
313+
it("writes FIXME on disk when applied with --yes", async () => {
314+
const r = await runCli(
315+
[
316+
"apply",
317+
"replace-marker-kind",
318+
"--params",
319+
"from_kind=TODO,to_kind=FIXME",
320+
"--yes",
321+
"--json",
322+
],
323+
{ CODEMAP_ROOT: projectRoot },
324+
);
325+
expect(r.exitCode).toBe(0);
326+
const env = JSON.parse(r.out);
327+
expect(env.applied).toBe(true);
328+
expect(readFile("src/notes.md")).toContain("FIXME:");
329+
expect(readFile("src/notes.md")).not.toMatch(/\bTODO:/);
330+
});
331+
});
332+
276333
describe("migrate-import-source", () => {
277334
beforeEach(async () => {
278335
mkdirSync(join(projectRoot, "src", "api"), { recursive: true });

templates/recipes/rename-preview.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ actions:
3535

3636
# Rename preview
3737

38-
Read-only diff preview for symbol renames (definitions, imports, call sites, optional re-exports).
38+
Read-only diff preview for symbol renames (definitions, imports, call sites, binding references, barrel consumer imports, optional re-exports).
3939

4040
```bash
4141
codemap query --recipe rename-preview \
@@ -49,13 +49,15 @@ codemap query --recipe rename-preview \
4949
- Direct named import specifiers from `imports.specifiers` when `imports.resolved_path` points at the target symbol file.
5050
- AST call sites from `calls` where `callee_name` matches (`provenance` ast-only).
5151
- Single-hop barrel re-export lines via `re_export_chains` when `include_re_exports` is true (default).
52+
- Barrel **consumer** import specifiers (`barrel_import_rows`) when `resolved_path` is the barrel file and `re_export_chains` links the specifier to the target symbol.
53+
- Binding-resolved identifier sites (`reference_rows`) from `bindings` × `references`, excluding definition spans and AST call lines already in other CTEs.
5254

5355
## What v1 does not cover
5456

5557
- String literals, comments, dynamic dispatch (`obj[name]`), template-literal property access.
5658
- JSX component tag renames (use dedicated JSX recipes).
5759
- Default-import binding shapes beyond direct named specifiers.
58-
- Imports through barrel files (consumer imports the barrel, not the defining module).
60+
- Multi-hop barrel chains beyond `re_export_chains` materialisation.
5961
- Same-line ambiguity when `before_pattern` appears twice on one line (first match only).
6062

61-
Use `rg oldName` for literals/comments. Pair with `find-symbol-references` for binding sites the rename CTEs skip.
63+
Use `rg oldName` for literals/comments. Pair with `find-symbol-references` for audit-only views of binding sites.

templates/recipes/rename-preview.sql

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,76 @@ re_export_rows AS (
8686
p.include_tests
8787
OR (e.file_path NOT LIKE '%test.%' AND e.file_path NOT LIKE '%spec.%')
8888
)
89+
),
90+
-- Imports that resolve to a barrel re-exporting the target (consumer names the
91+
-- symbol in specifiers but resolved_path is the barrel file, not the defining module).
92+
barrel_import_rows AS (
93+
SELECT DISTINCT
94+
i.file_path,
95+
i.line_number AS line_start,
96+
i.line_number AS line_end,
97+
p.old_name AS before_pattern,
98+
p.new_name AS after_pattern,
99+
'barrel_import_specifier' AS location_kind,
100+
rec.hops AS chain_depth
101+
FROM imports i
102+
JOIN json_each(i.specifiers) spec
103+
JOIN re_export_chains rec
104+
ON rec.from_file = i.resolved_path AND rec.from_name = spec.value
105+
JOIN target_symbols s
106+
ON rec.to_file = s.file_path AND rec.to_name = s.name
107+
CROSS JOIN params p
108+
WHERE spec.value = p.old_name
109+
AND (p.include_re_exports IS NULL OR p.include_re_exports != 0)
110+
AND i.resolved_path IS NOT NULL
111+
AND i.resolved_path != s.file_path
112+
AND (p.in_file IS NULL OR i.file_path LIKE p.in_file || '%')
113+
AND (
114+
p.include_tests
115+
OR (i.file_path NOT LIKE '%test.%' AND i.file_path NOT LIKE '%spec.%')
116+
)
117+
),
118+
-- Binding-resolved identifier sites not already covered by definition / call CTEs.
119+
reference_rows AS (
120+
SELECT DISTINCT
121+
r.file_path,
122+
r.line_start,
123+
r.line_start AS line_end,
124+
p.old_name AS before_pattern,
125+
p.new_name AS after_pattern,
126+
'reference' AS location_kind,
127+
0 AS chain_depth
128+
FROM bindings b
129+
JOIN "references" r ON r.id = b.reference_id
130+
JOIN target_symbols s ON s.id = b.resolved_symbol_id
131+
CROSS JOIN params p
132+
WHERE (p.in_file IS NULL OR r.file_path LIKE p.in_file || '%')
133+
AND (
134+
p.include_tests
135+
OR (r.file_path NOT LIKE '%test.%' AND r.file_path NOT LIKE '%spec.%')
136+
)
137+
AND NOT EXISTS (
138+
SELECT 1
139+
FROM target_symbols s2
140+
WHERE s2.id = s.id
141+
AND s2.file_path = r.file_path
142+
AND r.line_start BETWEEN s2.line_start AND s2.line_end
143+
)
144+
AND NOT EXISTS (
145+
SELECT 1
146+
FROM calls c
147+
WHERE c.file_path = r.file_path
148+
AND c.line_start = r.line_start
149+
AND c.callee_name = p.old_name
150+
AND (c.provenance IS NULL OR c.provenance = 'ast')
151+
)
152+
AND NOT EXISTS (
153+
SELECT 1
154+
FROM imports i2
155+
JOIN json_each(i2.specifiers) spec2 ON spec2.value = p.old_name
156+
WHERE i2.file_path = r.file_path
157+
AND i2.line_number = r.line_start
158+
)
89159
)
90160
SELECT *
91161
FROM definition_rows
@@ -98,4 +168,10 @@ FROM call_rows
98168
UNION ALL
99169
SELECT *
100170
FROM re_export_rows
171+
UNION ALL
172+
SELECT *
173+
FROM barrel_import_rows
174+
UNION ALL
175+
SELECT *
176+
FROM reference_rows
101177
ORDER BY file_path, line_start, location_kind;

0 commit comments

Comments
 (0)