Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 18 additions & 7 deletions docs/accuracy/traceability.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,31 @@ interface PropagatedLocation {
The UI offers both as click targets. Default click lands on `callSite` (where the user worked); a "Show underlying call" affordance jumps to `resolvedSite`.

### Acceptance criteria
- [ ] Propagated detections carry both spans + hop count.
- [ ] Direct (non-propagated) detections have `hops = 0` and the two spans equal.
- [ ] Webview shows both locations with clear labels.
- [ ] Stable IDs (B3) hash includes only one of the two spans (suggest: `resolvedSite`) so refactors that move the call site don't reset state.
- [x] Propagated detections carry both spans + hop count. *(`CallTrace { callSite, resolvedSite, hops }` on `AstCallMatch.trace` → `ApiCallInput.callTrace` → `EndpointCallSite.callTrace` + `ApiCallNode.callTrace`.)*
- [x] Direct (non-propagated) detections have `hops = 0` and the two spans equal. *(`directTrace()` fallback applied at the three call-site construction sites in `scan-results.ts`.)*
- [~] Webview shows both locations with clear labels. *(Code landed: `ResultsPage.tsx` Endpoints view shows the call site by default plus a "↳ underlying call" link when `hops > 0`. **Pending manual EDH verification** — F5 the dev host, scan a workspace where a helper wraps an SDK call, confirm both links navigate correctly.)*
- [x] Stable IDs hash is call-site-stable. *(Satisfied by B3 design — `computeEndpointId` excludes line/column/span, so moving a call site cannot reset the ID; locked in by `endpoint-id.test.ts` "B2/AC-4". The hash is intentionally **not** re-keyed on `resolvedSite.file`: doing so would collapse distinct callers into one endpoint and risk a benchmark detection-metric regression.)*

> **#113 (corpus fixtures) — blocked here.** The 7 barrel/factory/DI fixtures live in
> `recost-dev/extension-benchmark` (separate repo). Once they land, refresh the baseline:
> `npm run benchmark -- --fixtures ../extension-benchmark --update-baseline`, then commit the
> regenerated `benchmark/baseline.json`. No `extension`-repo code change is required for #113.

### Files
- `src/scanner/call-trace.ts` (new — `CallTrace`/`ResolvedLocation`/`directTrace`)
- `src/ast/ast-scanner.ts` (`AstCallMatch.trace`)
- `src/ast/cross-file-resolver.ts`
- `src/intelligence/types.ts`
- `webview/src/components/ResultsPage.tsx`
- `src/analysis/types.ts` (`ApiCallInput.callTrace`, `EndpointCallSite.callTrace`)
- `src/scanner/core-scanner.ts`, `src/scan-results.ts`
- `src/intelligence/types.ts` (`ApiCallNode.callTrace`), `src/intelligence/builder.ts`
- `webview/src/types.ts`, `webview/src/components/ResultsPage.tsx`

### Depends on
- B1 (spans must exist first).
- B1 (spans must exist first). *(Landed.)*
- A1 (wrapper depth — once hops can be >1, this becomes more useful).

✅ Landed: 2026-05-30 on branch `claude/recent-pr-explanation-C7Xcw`. Acceptance criteria 1, 2, 4 automated-verified (`npm run test:scanner` green incl. 7 new B2 cases; `npm run build` clean); #3 awaits manual EDH check. Benchmark gate runs in CI only (fixtures repo not accessible locally); Δ expected +0.00pp — B2 is additive metadata and changes no detection/inclusion/finding logic.

---

## B3. Stable endpoint IDs across scans
Expand Down
737 changes: 737 additions & 0 deletions docs/superpowers/plans/2026-05-30-wave2-b2-dual-locations.md

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion docs/superpowers/plans/PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ Post-foundation work organized into 10 waves + 2 standalones, tracked via `wave/
| **5** | Housekeeping (accuracy) | #118, #119 | 🟢 | Both closed 2026-05-27 (benchmark runner sort + D1 CI gate verified). |
| **3** | Resolver follow-ups (accuracy) | #114, #115, #116 | 🟢 | Shipped via PR [#126](https://github.com/recost-dev/extension/pull/126). Follow-ups #127 (detection threading) + #128 (dashboard badge) landed on `feat/wave3-followups`. |
| **1** | Findings quality (accuracy) | #84, #85, #112 | 🟢 | All closed 2026-05-28 (C2 dedupe, C3 confidence, CACHE/BATCH_GUARD tightening). |
| **2** | Traceability (accuracy) | #81, #113 | | Corpus expansion + dual locations. Next wave. |
| **2** | Traceability (accuracy) | #81, #113 | 🟡 | B2 dual locations (#81) code-complete on `claude/recent-pr-explanation-C7Xcw`. #113 corpus fixtures blocked (extension-benchmark repo). |
| **4** | Recall recovery (accuracy) | #117 | ⬜ | Risky — depended on Wave 3 (#116), now unblocked. |

## Wave 9 — Local-mode IPC architecture (CLOSED)
Expand Down Expand Up @@ -232,6 +232,7 @@ Post-foundation work organized into 10 waves + 2 standalones, tracked via `wave/

> Append `YYYY-MM-DD HH:MM — <one-line update>`. Newest at top.

- 2026-05-30 — **Wave 2 / B2 (#81) code-complete.** `CallTrace { callSite, resolvedSite, hops }` threaded `AstCallMatch.trace` → `ApiCallInput.callTrace` → `EndpointCallSite.callTrace` + `ApiCallNode.callTrace` (new shared type `src/scanner/call-trace.ts` + `directTrace()` degenerate fallback). Cross-file resolver populates the trace for propagated/middleware/factory matches (relative-path resolved); direct calls get `hops=0` via `directTrace` at the three `scan-results.ts` call-site sites. Webview Endpoints view (`ResultsPage.tsx`) now offers the user's call site by default + a "↳ underlying call" link when `hops > 0` (resolved SDK site); `webview/src/types.ts` mirrors `CallTrace`. AC-4 satisfied by B3 design (computeEndpointId excludes positions) + `endpoint-id.test.ts` regression; hash deliberately not re-keyed on resolvedSite.file (would collapse callers / risk benchmark). Subagent-driven (8 impl tasks, per-commit spec/scope verification; Task 4's out-of-scope `isHighConfidenceEndpointUrl` change caught + reverted). Gates: `npm run build` clean, `npm run test:scanner` exit 0 (7 new B2 cases green). **Pending:** manual EDH (dual click targets); CI benchmark (Δ expected +0.00pp — additive metadata, fixtures not accessible locally); #113 corpus fixtures (blocked — extension-benchmark repo). Plan: `docs/superpowers/plans/2026-05-30-wave2-b2-dual-locations.md`.
- 2026-05-29 — **Wave 3 follow-ups #127 + #128 landed** on `feat/wave3-followups`. #127 (`a1972bf`): threaded `inlineParallelCapable` through the regex pattern path (`openai-compatible.ts` emit, registry-only), the regex-only `local-waste-detector.ts` (new inline-parallel finding with the n/count suggestion, distinct `inline_parallel` id), the pattern dedup key (`utils.ts`), and the intelligence graph (`ApiCallNode` + `builder.ts`). #128 (`e9d7d7e`): `EndpointRecord` in both UI type files + `Endpoints.tsx` inline-parallel chip + `scan-results.ts` population at all 3 endpoint-construction sites — restores the indicator DALL·E lost when #116 reclassified `images.generate` off `batchCapable`. Gates: full `test:scanner` green (3 new tests), `build` (webview+ext) clean, benchmark Δ +0.00pp on all 5 metrics. **Caveat:** the web dashboard reads endpoints from the API, whose schema has no `inline_parallel` column — dashboard chip needs an api-repo migration to light up; VS Code webview badge works now. Next: accuracy Wave 2 (#113 → #81).
- 2026-05-29 — **Tracker reconciliation.** Waves 5 (#118/#119, closed 05-27) and 1 (#84/#85/#112, closed 05-28) marked 🟢 — both had merged but were never reflected here. Remaining accuracy work is now Wave 2 → 4.
- 2026-05-28 — **Wave 1 (findings quality) code-complete** on `claude/superpowers-plugins-skills-1Yaij` (plan `docs/superpowers/plans/2026-05-28-wave1-findings-quality.md`). #112: comment-stripped guard window stops the `CACHE_GUARD`/`BATCH_GUARD` literal-word leak (URL-safe `//` lookbehind). #85: detectors carry a structural `riskScore`; single `deriveSeverity()` (hybrid floor+amplifier, not pure confidence×cost) + `computeCostImpact()` applied at all 5 `Suggestion`-construction sites; `costImpactUsd` internal-only; confidence filter in the sidebar. #84: `collapseSuggestions()` dedupes by `type::file::endpoint|line-bucket`, unions `sources`, max confidence, AI-preferred description; `mergeAiSuggestions` collapses instead of dropping; "detected by N sources" badge. Subagent-driven (impl + spec + code-quality review per unit). Gates: full `test:scanner` green, full benchmark Δ +0.00pp on all 5 metrics. **Pending:** (1) manual EDH check of the two UI bits; (2) follow-ups — pre-existing `scope === "internal"` guard divergence between the two `buildAggressiveSuggestions` copies (scan-results.ts lacks it), and dead `SourceBadge` + duplicated badge inline-style in `ResultsPage.tsx` (extract a shared secondary-badge).
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@
"build:webview": "cd webview && npm run build",
"build:dashboard": "cd dashboard && npm run build && rm -rf ../dashboard-dist && cp -r dist ../dashboard-dist",
"test": "npm run test:scanner",
"test:scanner": "tsc -p tsconfig.scanner-tests.json && tsc -p tsconfig.benchmark.json && node dist-test/test/scanner-patterns.test.js && node dist-test/test/workspace-scanner.test.js && node dist-test/test/workspace-file-access.test.js && node dist-test/test/endpoint-classification.test.js && node dist-test/test/local-waste-detector.test.js && node dist-test/test/chat-providers.test.js && node dist-test/test/fingerprint-registry.test.js && node dist-test/test/pricing-sync.test.js && node dist-test/test/ast-parser-loader.test.js && node dist-test/test/ast-call-visitor.test.js && node dist-test/test/ast-import-resolver.test.js && node dist-test/test/ast-scanner.test.js && node dist-test/test/ast-python.test.js && node dist-test/test/ast-frequency-analyzer.test.js && node dist-test/test/ast-cache-detector.test.js && node dist-test/test/ast-batch-detector.test.js && node dist-test/test/ast-concurrency-detector.test.js && node dist-test/test/ast-cross-file-resolver.test.js && node dist-test/test/a1-multi-hop-wrappers.test.js && node dist-test/intelligence/__tests__/builder.test.js && node dist-test/intelligence/__tests__/clusters.test.js && node dist-test/intelligence/__tests__/compression.test.js && node dist-test/intelligence/__tests__/export.test.js && node dist-test/test/api-client.test.js && node dist-test/test/key-management.test.js && node dist-test/test/ast-parser-loader-fallback.test.js && node dist-test/intelligence/__tests__/cost-utils.test.js && node dist-test/test/intelligence-compression-async.test.js && node dist-test/test/webview-provider-dispatch.test.js && node dist-test/test/extension-activation.test.js && node dist-test/test/source-span.test.js && node dist-test/test/url-template.test.js && node dist-test/test/enclosing-function.test.js && node dist-test/test/endpoint-id.test.js && node dist-test/test/parity.test.js && node dist-test/test/a6-object-literal-fps.test.js && node dist-test/test/a2-const-fold.test.js && node dist-test/test/a7-url-path-fallback.test.js && node dist-test/test/c1-pr2-cache-tightening.test.js && node dist-test/test/c1-pr3-batch-tightening.test.js && node dist-test/src/test/benchmark-schema.test.js && node dist-test/src/test/benchmark-metrics.test.js && node dist-test/src/test/benchmark-baseline-sort.test.js && node dist-test/test/c1-pr4-rate-limit-tightening.test.js && node dist-test/test/c1-pr4-batch-residual.test.js && node dist-test/test/pre-a-scanfiles-resolution.test.js && node dist-test/test/pre-b-export-const-tracking.test.js && node dist-test/test/a3-barrel-reexports.test.js && node dist-test/test/a5-factory-di-aliased.test.js && node dist-test/test/wave6-pr1-submit-filter.test.js && node dist-test/test/scan-publishing-handler.test.js && node dist-test/test/config.test.js && node dist-test/test/scan-id.test.js && node dist-test/test/a3-default-import-threading.test.js && node dist-test/test/factory-with-args.test.js && node dist-test/test/ast-inline-parallel.test.js && node dist-test/test/scan-results.test.js && node dist-test/test/chat-handler-merge.test.js",
"test:scanner": "tsc -p tsconfig.scanner-tests.json && tsc -p tsconfig.benchmark.json && node dist-test/test/scanner-patterns.test.js && node dist-test/test/workspace-scanner.test.js && node dist-test/test/workspace-file-access.test.js && node dist-test/test/endpoint-classification.test.js && node dist-test/test/local-waste-detector.test.js && node dist-test/test/chat-providers.test.js && node dist-test/test/fingerprint-registry.test.js && node dist-test/test/pricing-sync.test.js && node dist-test/test/ast-parser-loader.test.js && node dist-test/test/ast-call-visitor.test.js && node dist-test/test/ast-import-resolver.test.js && node dist-test/test/ast-scanner.test.js && node dist-test/test/ast-python.test.js && node dist-test/test/ast-frequency-analyzer.test.js && node dist-test/test/ast-cache-detector.test.js && node dist-test/test/ast-batch-detector.test.js && node dist-test/test/ast-concurrency-detector.test.js && node dist-test/test/ast-cross-file-resolver.test.js && node dist-test/test/a1-multi-hop-wrappers.test.js && node dist-test/intelligence/__tests__/builder.test.js && node dist-test/intelligence/__tests__/clusters.test.js && node dist-test/intelligence/__tests__/compression.test.js && node dist-test/intelligence/__tests__/export.test.js && node dist-test/test/api-client.test.js && node dist-test/test/key-management.test.js && node dist-test/test/ast-parser-loader-fallback.test.js && node dist-test/intelligence/__tests__/cost-utils.test.js && node dist-test/test/intelligence-compression-async.test.js && node dist-test/test/webview-provider-dispatch.test.js && node dist-test/test/extension-activation.test.js && node dist-test/test/source-span.test.js && node dist-test/test/url-template.test.js && node dist-test/test/enclosing-function.test.js && node dist-test/test/endpoint-id.test.js && node dist-test/test/parity.test.js && node dist-test/test/a6-object-literal-fps.test.js && node dist-test/test/a2-const-fold.test.js && node dist-test/test/a7-url-path-fallback.test.js && node dist-test/test/c1-pr2-cache-tightening.test.js && node dist-test/test/c1-pr3-batch-tightening.test.js && node dist-test/src/test/benchmark-schema.test.js && node dist-test/src/test/benchmark-metrics.test.js && node dist-test/src/test/benchmark-baseline-sort.test.js && node dist-test/test/c1-pr4-rate-limit-tightening.test.js && node dist-test/test/c1-pr4-batch-residual.test.js && node dist-test/test/pre-a-scanfiles-resolution.test.js && node dist-test/test/pre-b-export-const-tracking.test.js && node dist-test/test/a3-barrel-reexports.test.js && node dist-test/test/a5-factory-di-aliased.test.js && node dist-test/test/wave6-pr1-submit-filter.test.js && node dist-test/test/scan-publishing-handler.test.js && node dist-test/test/config.test.js && node dist-test/test/scan-id.test.js && node dist-test/test/a3-default-import-threading.test.js && node dist-test/test/factory-with-args.test.js && node dist-test/test/ast-inline-parallel.test.js && node dist-test/test/scan-results.test.js && node dist-test/test/chat-handler-merge.test.js && node dist-test/test/call-trace.test.js",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Split test:scanner into a runner script to avoid shell command-length failures.

This single chained command is long enough to be fragile on Windows shells (command-length limits) and hard to maintain. Move the test list into a Node runner (or a generated manifest) and keep package.json script short.

Proposed refactor
-    "test:scanner": "tsc -p tsconfig.scanner-tests.json && tsc -p tsconfig.benchmark.json && node dist-test/test/scanner-patterns.test.js && ... && node dist-test/test/call-trace.test.js",
+    "test:scanner": "tsc -p tsconfig.scanner-tests.json && tsc -p tsconfig.benchmark.json && node dist-test/test/run-scanner-tests.js",
// src/test/run-scanner-tests.ts (compiled to dist-test/test/run-scanner-tests.js)
import { spawnSync } from "node:child_process";

const tests = [
  "dist-test/test/scanner-patterns.test.js",
  // ...all other test files...
  "dist-test/test/call-trace.test.js",
];

for (const t of tests) {
  const r = spawnSync(process.execPath, [t], { stdio: "inherit" });
  if (r.status !== 0) process.exit(r.status ?? 1);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 201, The long chained command in the "test:scanner"
script is fragile and must be replaced by a Node runner; create a new runner
(e.g., src/test/run-scanner-tests.ts compiled to
dist-test/test/run-scanner-tests.js) that exports or runs a list/array named
tests containing each compiled test path (e.g.,
"dist-test/test/scanner-patterns.test.js", ...,
"dist-test/test/call-trace.test.js"), iterate that array and spawn each test
with spawnSync(process.execPath, [testPath], { stdio: "inherit" }) and exit with
the failing status if any test returns non-zero, then update package.json
"test:scanner" to first run the two tsc builds and then call node
dist-test/test/run-scanner-tests.js (keeping the package script short).

"calibrate-detectors": "tsc -p tsconfig.scanner-tests.json && node dist-test/test/waste-calibration.js",
"watch:ext": "node esbuild.mjs --watch",
"watch:webview": "cd webview && npm run build -- --watch",
Expand Down
4 changes: 4 additions & 0 deletions src/analysis/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export interface ApiCallInput {
streaming?: boolean;
isMiddleware?: boolean;
crossFileOrigin?: { file: string; functionName: string } | null;
/** Dual-location trace. Set by the AST path for cross-file calls; undefined otherwise. */
callTrace?: import("../scanner/call-trace").CallTrace;
}

export interface ScanSummary {
Expand Down Expand Up @@ -74,6 +76,8 @@ export interface EndpointCallSite {
// Enriched fields from AST engine
frequencyClass?: string;
crossFileOrigin?: { file: string; functionName: string } | null;
/** Dual-location trace for this call site. Degenerate (hops=0) for direct calls. */
callTrace?: import("../scanner/call-trace").CallTrace;
}

export type SuggestionType =
Expand Down
5 changes: 5 additions & 0 deletions src/ast/ast-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ export interface AstCallMatch {
crossFile?: boolean;
/** Absolute path of the file where the API call actually lives (for cross-file matches). */
sourceFile?: string;
/**
* Dual-location trace, set ONLY for cross-file-propagated matches. Absent for
* direct matches (downstream defaults to a degenerate `directTrace`).
*/
trace?: import("../scanner/call-trace").CallTrace;
}

/** Per-class metadata collected during scanning (for cross-file use in 3.5). */
Expand Down
Loading
Loading