Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
bdfe45d
feat(span): add SourceSpan type and helpers (issue #80)
AndresL230 May 12, 2026
7e3187f
docs(span): document exclusive-end semantics on SourceSpan (issue #80)
AndresL230 May 12, 2026
8016a21
docs(progress): mark B1 F1 complete (T1)
AndresL230 May 12, 2026
881b918
test(span): unit-test SourceSpan helpers (issue #80)
AndresL230 May 12, 2026
88c4ebb
docs(progress): mark B1 F2 complete (T2)
AndresL230 May 12, 2026
9fde142
feat(span): emit SourceSpan from AST call-visitor (issue #80)
AndresL230 May 12, 2026
1db3d79
feat(span): allow regex matchers to carry SourceSpan (issue #80)
AndresL230 May 12, 2026
94fc288
feat(span): add SourceSpan to ApiCallInput and EndpointCallSite (issu…
AndresL230 May 12, 2026
77ed156
merge(B1 T3): emit SourceSpan from AST call-visitor
AndresL230 May 12, 2026
a645082
docs(progress): mark B1 batch A complete (T3, T5, T6)
AndresL230 May 12, 2026
c346867
feat(span): propagate SourceSpan through AstCallMatch (issue #80)
AndresL230 May 12, 2026
901e5ae
test(span): populate span in python-waste-detector fixtures (issue #80)
AndresL230 May 12, 2026
6cbc4a7
test(span): populate span+confidence in AST waste-detector fixtures (…
AndresL230 May 12, 2026
508a4a4
docs(progress): mark B1 F3 complete (T4)
AndresL230 May 12, 2026
282f1b8
feat(span): populate SourceSpan in ApiCallInput from both scan paths …
AndresL230 May 12, 2026
0c0d8b1
docs(progress): mark B1 F4 complete (T7)
AndresL230 May 12, 2026
9003f4d
feat(span): wire SourceSpan into ApiCallNode and snapshot builder (is…
AndresL230 May 12, 2026
afc8f1b
feat(span): include SourceSpan on EndpointCallSite (issue #80)
AndresL230 May 12, 2026
c5a14c6
docs(progress): mark B1 batch B complete (T8, T9)
AndresL230 May 12, 2026
69ca79d
feat(span): reveal full call expression on click (issue #80)
AndresL230 May 12, 2026
371fd8e
docs(accuracy): mark B1 (span-based locations) shipped (issue #80)
AndresL230 May 12, 2026
4e511f5
docs(progress): B1 code-complete; T10/T11 pending manual EDH verifica…
AndresL230 May 12, 2026
a914fc6
fix(callers): await compressClusters in webview, CLI, extension activ…
AndresL230 May 12, 2026
59aa7f4
fix(tests): unblock CI — node-builtin filter, fingerprint count, fixt…
AndresL230 May 12, 2026
fba4295
feat(endpoint-id): URL template masker for stable IDs (issue #82)
AndresL230 May 12, 2026
a81fd02
feat(endpoint-id): enclosing-function extractor (issue #82)
AndresL230 May 12, 2026
a8f2be2
refactor(endpoint-id): clarify destructure null + nested-function tes…
AndresL230 May 12, 2026
694dc30
feat(endpoint-id): computeEndpointId hash function (issue #82)
AndresL230 May 12, 2026
d6b0feb
refactor(endpoint-id): reuse normalizeRepoPath, lock in ./ collapse (…
AndresL230 May 12, 2026
4799fcc
feat(endpoint-id): emit enclosingFunction on every match (issue #82)
AndresL230 May 12, 2026
b9e54be
docs(endpoint-id): clarify enclosingFunction asymmetry + 7d override …
AndresL230 May 12, 2026
7cea7b8
refactor(endpoint-id): use computeEndpointId in snapshot builder (iss…
AndresL230 May 12, 2026
2e6b3a8
refactor(endpoint-id): stable IDs for synthetic local endpoints (issu…
AndresL230 May 12, 2026
e8a8fee
refactor(endpoint-id): O(1) synthetic collision check via Set (issue …
AndresL230 May 12, 2026
0c7c707
feat(endpoint-id): drop persisted records with unrecognized IDs (issu…
AndresL230 May 12, 2026
6b8828b
fix(endpoint-id): guard prune against zero-endpoint scans (issue #82)
AndresL230 May 12, 2026
977e4f4
test(endpoint-id): stability under refactor (issue #82)
AndresL230 May 12, 2026
582c82e
docs(accuracy): mark B3 (stable endpoint IDs) shipped (issue #82)
AndresL230 May 12, 2026
c18c8c8
feat(parity): runner library + empty allowlist scaffolding (issue #76)
AndresL230 May 12, 2026
11c65e4
test(parity): basic + documented-divergence fixtures (issue #76)
AndresL230 May 12, 2026
36a3601
test(parity): wire parity-test entry into test:scanner (issue #76)
AndresL230 May 12, 2026
91fc235
fix(parity): host-attribute generic HTTP calls + drop wrong-method fa…
AndresL230 May 12, 2026
e6f2062
docs(parity): document multi-line AST-only divergences (issue #76)
AndresL230 May 12, 2026
101cea5
docs(accuracy): mark A4 (AST↔regex parity) shipped (issue #76)
AndresL230 May 12, 2026
b989281
Merge remote-tracking branch 'origin/main' into claude/foundation-par…
claude May 12, 2026
dc111c7
chore(test): drop unused assert import in parity.test.ts
claude May 12, 2026
8a1752f
fix(b1,b3,parity): address CodeRabbit review findings
claude May 12, 2026
0a44aaa
chore(ast-scanner): drop unused node binding from pass-9/10 destructures
claude May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/accuracy/PARITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# AST ↔ Regex Parity — Documented Divergences

Tracked under [issue #76](https://github.com/recost-dev/extension/issues/76). The
parity test in `src/test/parity.test.ts` runs both detection paths against
`src/test/fixtures/parity/`, normalises results to `(provider, method, line)`
tuples, and fails on any divergence not listed below.

## How to use this list

- Adding a fixture: place it under `src/test/fixtures/parity/`. If both paths
are expected to detect the same calls, no entry is needed.
- A divergence the test surfaces is either a bug (fix it) or a documented
intentional difference (add an entry below). The first option is preferred.
- Entries are parsed by `parseAllowlist()` in `src/test/parity.ts` from the
fenced YAML block. Keep that block as the single source of truth.

## Allowlist

```yaml
- file: wrapped-call.ts
reason: AST follows wrapper functions back to the SDK call; regex sees only the wrapper invocation by name.
astOnly: true
- file: fetch-known-host.ts
reason: Multi-line fetch with an options object on subsequent lines — regex is line-based and cannot stitch the method across lines, AST sees the full call expression structurally.
astOnly: true
- file: python-requests.py
reason: Multi-line requests.post() with URL on a separate line — regex requires URL on the same line as the call site, AST sees the full call expression structurally.
astOnly: true
```
8 changes: 5 additions & 3 deletions docs/accuracy/detection.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ import { client } from "../lib/clients"; // ← scanner must reach openai.ts

## A4. AST ↔ regex parity audit

✅ **Landed: 2026-05-12** — see [issue #76](https://github.com/recost-dev/extension/issues/76). Parity runner lives at `src/test/parity.ts`, test entry at `src/test/parity.test.ts`, intentional-divergence allowlist at `docs/accuracy/PARITY.md`. Wired into `test:scanner` in `package.json` and runs in CI via `.github/workflows/test.yml`. First audit surfaced one regex bug (now fixed: `generic-http.ts` now does host-based provider attribution + drops the wrong-method GET fallback for multi-line fetch) and two structural multi-line cases (documented as `astOnly` in the allowlist).

### Problem
The scanner has two detection paths for JS/TS/Python:
- AST scanner (`src/ast/ast-scanner.ts`)
Expand All @@ -166,9 +168,9 @@ Currently there's no answer to either question. Silent disagreements mean either
4. Fix bugs; document intentional divergences in a `PARITY.md` table; gate the test in CI.

### Acceptance criteria
- [ ] Parity test runs in CI on every PR.
- [ ] Every divergence the test produces is either fixed or annotated in `PARITY.md`.
- [ ] Same `line` reported by both paths for every JS/TS/Python file where both detect a call.
- [x] Parity test runs in CI on every PR.
- [x] Every divergence the test produces is either fixed or annotated in `PARITY.md`.
- [x] Same `line` reported by both paths for every JS/TS/Python file where both detect a call.

### Files
- New: `src/test/parity.test.ts`
Expand Down
48 changes: 26 additions & 22 deletions docs/accuracy/traceability.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ interface SourceSpan {
}
```

### Investigation steps
1. Tree-sitter nodes already expose `startPosition` / `endPosition`. Wire them through `AstCallMatch`.
2. Regex pattern scanners need a way to compute end position — easiest: re-scan with a more permissive regex that captures the full call expression, or use the source text + a balanced-paren walker.
3. Update `EndpointRecord` / `ApiCallNode` to carry a span field; keep `line` as a derived shortcut for back-compat.
4. Update the webview Endpoints + Graph views to highlight the span, not just the line.
### ~~Investigation steps~~ (resolved — see implementation plan `docs/superpowers/plans/2026-05-12-b1-span-based-source-locations.md`)
1. ~~Tree-sitter nodes already expose `startPosition` / `endPosition`. Wire them through `AstCallMatch`.~~ Done in T3/T4.
2. ~~Regex pattern scanners need a way to compute end position — easiest: re-scan with a more permissive regex that captures the full call expression, or use the source text + a balanced-paren walker.~~ Shipped with line-wide span as the documented compromise (T7); tight regex spans require a `matchLine` API change tracked as future work.
3. ~~Update `EndpointRecord` / `ApiCallNode` to carry a span field; keep `line` as a derived shortcut for back-compat.~~ `EndpointCallSite.span?` (T6/T9) and `ApiCallNode.span` (T8) both populated; `line` preserved alongside.
4. ~~Update the webview Endpoints + Graph views to highlight the span, not just the line.~~ Reveal-by-span landed in `webview-provider`'s `openFile` handler and `ResultsPage` (T10).

### Acceptance criteria
- [ ] `EndpointRecord` exposes `span: SourceSpan` with all four numbers populated.
- [ ] Multi-line calls (>3 lines) have `endLine > startLine`.
- [ ] Clicking a detection in the webview opens the editor with the span selected, not just the line scrolled into view.
- [ ] Existing tests assertions on `line` continue to work (derive from span).
- [x] `EndpointRecord` exposes `span: SourceSpan` with all four numbers populated. *(via `EndpointCallSite.span?` — optional only because legacy/synthetic inputs may omit it.)*
- [x] Multi-line calls (>3 lines) have `endLine > startLine`. *(AST path — verified by `src/test/ast-call-visitor.test.ts` "span: multi-line call has endLine > startLine".)*
- [ ] Clicking a detection in the webview opens the editor with the span selected, not just the line scrolled into view. *(Code landed in T10 commit `69ca79d`; **pending manual EDH verification** — F5 the dev host, scan a workspace with a multi-line OpenAI call, click the endpoint row, confirm full-call selection.)*
- [x] Existing tests assertions on `line` continue to work (derive from span). *(`line` is preserved alongside `span` everywhere; all affected unit tests pass.)*

### Files
- `src/ast/call-visitor.ts`
Expand All @@ -48,6 +48,8 @@ interface SourceSpan {
- `src/intelligence/types.ts` (ApiCallNode)
- `webview/src/components/ResultsPage.tsx` (open-file IPC)

✅ Landed: 2026-05-12 on branch `foundation-parser-accuracy`. Acceptance criteria 1, 2, 4 automated-verified; #3 awaiting manual EDH check.

---

## B2. Dual locations for cross-file resolved calls
Expand Down Expand Up @@ -121,26 +123,28 @@ Key properties:
- **Includes enclosing function name** — disambiguates two calls to the same method in the same file.
- **URL templates masked** — `/users/123` and `/users/456` get the same ID (mask numeric IDs, UUIDs, etc.).

### Investigation steps
1. Add an enclosing-function-name extractor in `call-visitor.ts` (walk parent nodes for `function_declaration`, `method_definition`, `arrow_function` parent var name).
2. Add `maskUrlDynamicParts(url)` in a util module — replaces numeric segments, UUIDs, and known ID patterns with `:id`.
3. Wire into a single `computeEndpointId()` function. Use it in `EndpointRecord` construction.
4. Migration: existing persisted state keyed by old IDs needs a fallback — log warning, ignore the old state, write new IDs on next scan.
### ~~Investigation steps~~ (resolved — see implementation plan `docs/superpowers/plans/2026-05-12-b3-stable-endpoint-ids.md`)
1. ~~Add an enclosing-function-name extractor in `call-visitor.ts` (walk parent nodes for `function_declaration`, `method_definition`, `arrow_function` parent var name).~~ Done in T2 (new module `src/ast/enclosing-function.ts`, used by both `endpoint-id.ts` and `ast-scanner.ts`).
2. ~~Add `maskUrlDynamicParts(url)` in a util module — replaces numeric segments, UUIDs, and known ID patterns with `:id`.~~ Done in T1 (`src/scanner/url-template.ts`).
3. ~~Wire into a single `computeEndpointId()` function. Use it in `EndpointRecord` construction.~~ Done in T3/T5/T6: `src/scanner/endpoint-id.ts` is the canonical hasher; `intelligence/builder.ts` and `scan-results.ts` both consume it; `webview-provider.ts`'s parallel synthetic-ID minter was migrated alongside (T7 scope expansion).
4. ~~Migration: existing persisted state keyed by old IDs needs a fallback — log warning, ignore the old state, write new IDs on next scan.~~ Done in T7: `pruneSavedScenariosAgainst` drops saved simulator scenarios whose referenced endpoint IDs are absent from the current scan and persists the cleaned list to `recost.simulatorScenarios`. Includes a zero-endpoint guard to avoid wiping all scenarios on misconfigured/empty scans.

### Acceptance criteria
- [ ] Endpoint IDs survive moving a call ±20 lines in the same file.
- [ ] Endpoint IDs survive renaming a containing variable but not the function.
- [ ] Two distinct calls to `openai.chat.completions.create` in the same file but different functions get distinct IDs.
- [ ] `/api/users/123` and `/api/users/456` get the same ID.
- [ ] Saved simulator scenarios and suppressed findings survive a scan after non-structural code changes.
- [x] Endpoint IDs survive moving a call ±20 lines in the same file. *(T3 + T8: `computeEndpointId` has no `line`/`column`/`span` input; verified by tests "ID survives ±20 line move" and "end-to-end: same call, moved 20 lines, gets the same ID".)*
- [x] Endpoint IDs survive renaming a containing variable but not the function. *(T3 test "ID survives renaming an unrelated containing variable"; T3 test "ID changes when enclosing function changes".)*
- [x] Two distinct calls to `openai.chat.completions.create` in the same file but different functions get distinct IDs. *(T8 test "end-to-end: two calls in same file but different functions diverge"; supported by collision-disambiguation fallback `_L<line>` in `builder.ts` for same-function same-URL repeats.)*
- [x] `/api/users/123` and `/api/users/456` get the same ID. *(T1 url-template masks numeric segments to `:id`; T3 test "URLs differing only by numeric ID produce the same endpoint ID".)*
- [~] Saved simulator scenarios and suppressed findings survive a scan after non-structural code changes. *(Code path verified: stable IDs mean a re-scan after non-structural change produces the same IDs, so `pruneSavedScenariosAgainst` keeps scenarios. T7 Step 4 — F5 EDH, save a scenario, edit an unrelated file, re-scan, confirm scenario still loads — is **pending manual verification**.)*

### Files
- New: `src/ast/enclosing-function.ts`
- New: `src/scanner/url-template.ts`
- `src/scanner/types.ts` (EndpointRecord id field)
- Wherever endpoint IDs are currently generated (search for `id:` in scan-results / webview-provider)
- New: `src/scanner/endpoint-id.ts` (+ test)
- Modified: `src/analysis/types.ts` (`ApiCallInput.enclosingFunction?`), `src/ast/ast-scanner.ts` (emit `enclosingFunction` on every match), `src/scanner/core-scanner.ts` (pipe through), `src/intelligence/builder.ts` (use `computeEndpointId` + `_L<line>` collision fallback), `src/scan-results.ts` (use `computeEndpointId` + Set-based collision check), `src/webview-provider.ts` (parallel synthetic-ID migration + `pruneSavedScenariosAgainst`).

### Depends on
- B1 (spans help identify the enclosing function reliably).
- B1 (spans help identify the enclosing function reliably). *(B1 landed first; B3's `enclosingFunctionName` walker uses `node.parent` so the dependency is documentary, not blocking.)*

✅ Landed: 2026-05-12 on branch `foundation-parser-accuracy`. Acceptance criteria 1–4 automated-verified across `src/test/url-template.test.ts`, `src/test/enclosing-function.test.ts`, and `src/test/endpoint-id.test.ts` (13 cases). Criterion #5 is code-complete but awaits manual EDH verification per T7 Step 4.

---
Loading