RELEASE NOTES

It keeps getting
better.

Every version. What changed, what we fixed,
and the measurements behind it.

Changelog on GitHub
CURRENT VERSION

v0.9.0

See the latest benchmark results

Measured on 49 benchmark files in full agent sessions with Claude, Copilot and Codex: this release finds as many bugs as an agent reading the files itself, or more, with 32–69% fewer tokens, and matches v0.8.2’s accuracy with Claude at 78% fewer tokens. With Copilot, v0.8.2 found two more bugs over two sessions at 2.5 times the tokens. See the results.

Added

  • check_types returns selected JavaScript/TypeScript files’ compiler diagnostics without model calls. It preserves configured project declarations and options, reports incomplete context and limits, and bundles TypeScript’s standard libraries. Compiler diagnostics are separate from model confidence and runtime predictions.
  • Imported definitions now include CommonJS: require() bindings and exports from module.exports/exports. When a required name provably is not exported (the module’s exports are one complete object literal), the model is told so; in any less certain shape nothing is claimed. Not yet benchmarked.
  • Replies to predict_failures include providerVersion, the CLI version that produced the verdicts, and cached: true for a reused verdict (below).
  • npm run bench:canary -- --provider=<id> checks an installed CLI against 12 held-out benchmark files in about a minute and reports PASS or DRIFT.

Changed

  • Small files share model calls: up to eight per call, with a bounded prompt, each keeping its own verdict and line validation. Large files still run alone. A file whose group verdict names a defect with a score under 0.80, or that the group calls clean although it reads, awaits and writes back shared state, is reviewed again on its own, and that verdict replaces the group’s. Missing or malformed group verdicts are unavailable, never clean.
  • Scores are anchored to a trigger the shown code permits and a result the shown code contradicts, and the expected behavior must come from the code, not from the model’s own assumptions.
  • uncertain results are no longer something to ignore: replies containing one add check, asking the calling agent to read the cited lines and confirm or dismiss it.
  • Re-reviewing a set after editing one file no longer pays for the unchanged ones. Within a server session, identical review input returns the earlier verdict with cached: true; failed and unavailable verdicts are never reused.
  • After two failed provider calls in a row, the remaining files fail at once with the reason instead of each waiting out its timeout. Re-checks start as soon as their group finishes, and files are read and parsed in parallel.
  • Claude reviews no longer make a hidden side call that sent every prompt uncached, and no longer load your skills, MCP servers or session files.
  • concurrency counts model calls in flight, each covering up to eight files.
  • bench/RESULTS.md summarizes the current comparison; older reports remain at the release tags. npm run bench is replaced by npm run bench:canary.

Security

  • Only JavaScript/TypeScript source files are sent to the model. A path to a .env or key file is refused before any provider call, imports resolve only to source files, and one call accepts at most 100 files.
  • Each review call starts the CLI in a new, empty temporary folder, deleted afterwards. In the project folder, all three CLIs loaded its CLAUDE.md/AGENTS.md into every review, costing tokens and letting the repository under review instruct its reviewer. See SECURITY.md.
  • Codex reviews turn off Codex’s agent tools (shell, patching, web, browser, computer use, plugins, sub-agents, image generation). With its defaults, asked to read a file, the reviewing Codex called its shell. Each Codex call now sends about 9k more uncached tokens, because its shared prompt cache no longer applies.
  • Copilot reviews expose no tools to the model, including MCP tools from your own configuration.
  • MCP tools declare themselves read-only and non-destructive, which also lets Codex call them with approvals off; v0.8.2 could not run there.

Fixed

  • Brackets in prose before a model’s verdict (checked [x], line [12]) no longer make the verdict unavailable.
  • An explicit unknown verdict stays unavailable instead of becoming a clean result.
View source on GitHub

v0.8.2

Read release notes

Changed

  • Linked the website, predictivedebugger.dev, from the README and set it as the npm homepage. Added mcpName and server.json for the official MCP Registry.
View source on GitHub

v0.8.1

Read release notes

Changed

  • Simplified the README with setup first, expandable client instructions and example prompts. Moved detailed setup, tool behavior and VS Code settings to linked guides, and consolidated the security model in SECURITY.md.
View source on GitHub

v0.8.0

Read release notes

Added

  • Added npm distribution for npx -y predictive-debugger@latest, with agent setup and update instructions for Claude Code, Codex and Copilot CLI. Added --version and --help, and a CI check that installs the tarball through npx in an isolated cache and exercises the installed MCP server.
  • Added the deterministic map_dependencies MCP tool for bounded imports, reverse imports and connected test files, with source-line evidence and explicit coverage limits. It shares module-path resolution with prediction context and makes no provider call. MCP instructions route relationship questions to it.
  • Dependency context resolves unambiguous wildcard barrels, imported named/default bindings that are re-exported, and calls to imported constructors. Conflicts are checked by originating binding; unknown branches and traversal limits produce less context instead of a guessed definition. Namespace re-exports and CommonJS remain unsupported.

Fixed

  • Dependency maps now mark coverage as limited when directory discovery reaches its depth cap, alongside the existing issue detail.
  • Oversized imported object and static class context prioritizes the called member and referenced state/helpers within the existing character limits. An unrelated earlier method could previously consume the budget and hide the called method. Omissions are explicit; small definitions remain unchanged. Dynamic definitions retain the previous truncation behavior.

Changed

  • Benchmark reports and graphs label this release v0.8.0. Raw experiment names and measured build hashes remain unchanged.
  • npm packages include only the MCP bundle, Python helper and package docs. JavaScript dependencies are bundled at build time, so npx does not need to install them separately. Publishing runs the tests and packaged-server check.
  • Clarified member-selection rules, export resolution states and map traversal without changing the 37 benchmark prediction prompts.
  • Broader dependency evidence enabled more detections in targeted tests and used 3% more total tokens than v0.7.1 in the complete comparison. The report separates the original cases from new dependency cases and compares tokens, with no monetary savings claim. See benchmark results.
  • Shared batch caching is deferred after profiling found little repeated indexing time worth saving.
  • Release comparisons reuse compatible saved results for the latest official release and test only the candidate. Detailed results belong in bench/, with a brief explanation of behavior and token usage in release notes.

Removed

  • Superseded development checkpoint runs, proxy-baseline experiments and duplicate benchmark reports. Retained the complete comparison, its supporting historical inputs, and the final dependency-map and batch-parsing measurements.
View source on GitHub

v0.7.1

Read release notes

Fixed

  • Prediction reads now enforce a 4 MB limit, including when a file grows during reading. Oversized files fail individually without starting a model call.
  • Timeout and cancellation stop the Windows CLI process tree and wait for cleanup. Unix CLI processes run in a separate group that is killed on cancellation. Already-cancelled requests no longer start a process.
  • Reject unsafe Windows CLI shim arguments before spawning. Embedded quotes and environment-variable expansion could turn a model override into a shell command. Added a Windows regression test proving the shim never starts for these inputs.
  • Bumped transitive fast-uri and qs to resolve 2 known vulnerabilities (1 moderate, 1 high) via npm audit fix. No direct dependency changes.
View source on GitHub

v0.7.0

Read release notes

Removed

  • Superseded benchmark datasets, charts and runners. The working tree now retains the complete agent-workflow comparison and its reproduction tools. Previously committed artifacts remain in Git history.

Fixed

  • Exclude temporary benchmark worktrees from npm and VS Code packages.
  • Malformed model verdicts now report unavailable instead of clean, while valid findings in mixed replies remain usable.
  • Callee context respects import bindings when parameters or local variables shadow imported names.
  • Complexity metrics count do…while loops, including mixed nested loops.

Changed

  • Prediction evidence distinguishes unseen application wiring from a demonstrated local defect, reducing speculative route-registration and dependency-injection warnings. Numerical claims are checked against a concrete input. The revised candidate retained a verified defect report on all 39 buggy-file trials, with 38 planted matches and one alternative defect, and produced no false alarms in the development comparison.

  • Prediction evidence explicitly includes imported type contracts and leaked resources without a crash. Shortened coverage instructions offset the added policy text.

  • Replaced the benchmark report with fresh Sonnet agent comparisons for direct reading, pinned v0.6 master and the v0.7 candidate. New graphs include caller and internal-provider usage, cache tokens, CLI-estimated cost and adjudicated defect identity. See bench/RESULTS.md and bench/METHOD.md.

  • Dependency context includes referenced types, imported object methods, explicit barrel re-exports and local TypeScript path mappings, with bounded resolution and source-proportional context limits.

  • Oversized files contribute whole declarations from both ends within the existing one-call source budget. Omitted code is disclosed and findings citing unseen lines report unavailable.

  • Benchmark grading separates unavailable replies and location matches from adjudicated defect identity. Added development cases in the existing corpus, response/source hashes, visible model-input token estimates, and offline adjudication.

  • Batch predictions analyze the configured log once per batch, sharing the result across concurrent files.

  • Callee collection resolves each module specifier and parses each dependency once per collection call, reusing its export definitions.

View source on GitHub

v0.6.0

Read release notes

Added

  • predict_failures takes a files array and runs the verdicts concurrently. The tool reviewed one file per call, so an agent checking a four-file change set paid the provider’s latency four times in series. The benchmark had read that as the tool being intrinsically slow; it was not. A verdict is 5–15 seconds of a subprocess waiting on a model, and those waits are independent — one file’s verdict never informs another’s — so the serialisation bought nothing. A batch now bills the same as the same files one at a time and returns in roughly the time of the slowest one.

    file still works and still returns the flat reply it always has; wrapping a single verdict in a one-element array to make the shapes uniform would have cost every existing caller a rewrite to buy nothing. A batch returns a results array in the order the paths were given — a pool finishes out of order, and a caller should not have to re-match replies to requests — with review and viaProvider hoisted out of it, since repeating advice that is identical for every entry makes it a per-file cost that scales with the batch. Duplicate paths collapse, because each entry is a billed model call. Files that fail are reported in a separate failures array rather than folded in as results with an error field, so one unreadable file does not discard the work already done on the others. concurrency (default 4) is there for providers that rate-limit.

    predictProject, behind the VS Code project-wide command, was serial for the same reason and now shares the same pool.

Changed

  • The verification rule now covers new code, not just fixes, and the seat is chosen by file count alone. The rule advertised in MCP instructions fired only when the agent fixed something these tools flagged, but the argument behind it — that the context which wrote the code confirms it rather than tests it — never depended on the change being a fix. Scoping it to fixes left out the case that needs it most: on new feature code a clean predict_failures reply reads as a clearance, when all it means is that the file is locally sound. The trigger is now these tools pointed at code the agent wrote in the session, which still keeps it from firing on work the tools were never shown.

    The seat is chosen by how far the change reaches, because the two seats do not cost the same. A change confined to one file — including a whole feature in one file — gets a fresh predict_failures, already a second model for one call. A change spanning several files gets a sub-agent, because predict_failures reads each file on its own and never sees how they have to agree. File count is the whole test: not how large the change felt, not whether it was a fix or a feature, and not whether “correctness depends on what was asked for” — a condition that sounds narrow and is not, since every feature exists to satisfy an ask and the clause was true of essentially all non-trivial work. Whether a change crosses a file boundary is a fact about the diff an agent cannot argue itself past, and it tracks the one thing a per-file tool structurally cannot check: whether two files still agree.

    The mechanical exemption is unchanged. Sub-agents are to be scoped to the changed files and the goal, and backgrounded where the host allows it: an unscoped one rebuilds the project from cold and reports on code nobody touched, and a blocking one doubles the wait on the turn a user is watching.

    That the sub-agent earns its cost is still an argument, not a result: the benchmark measures review of code the reviewer did not write, which is the one setting where the contamination the rule exists to correct cannot occur, so it can price the sub-agent but not value it. The trigger is held to the narrowest case the argument supports until that changes.

  • onFix on predict_failures replies is now review, and is always present. It was gated on the precision gate, on the reasoning that a clean file should not pay for advice about a fix nobody is making. That gate was the wrong shape once the rule covered new code: a clean reply on code the agent just wrote is the turn where the rule matters most and is least likely to be remembered. The wording forks on the gate instead of the field appearing and disappearing, so neither case pays for the other’s advice.

View source on GitHub

v0.5.2

Read release notes

Fixed

  • A model verdict was thrown away when the reply had prose after it containing a } or ]. extractJson found the end of the JSON with lastIndexOf, so commentary like “the guard at if (a) { return; } already covers it” moved the cut past the reply, JSON.parse failed, and the repair path declined to help because the brackets were already balanced. A correct, confident finding was reported as unknown. The end of the value is now found by scanning forward with depth tracking, string- and escape-aware, the way repairTruncatedJson already did it. The wider lastIndexOf read stays as a fallback, and can no longer be reached by a reply with a complete value at the front.

  • export default foo; sent the model const foo = foo instead of the function. The default-export branch resolved a declaration it could see inline, but an identifier naming a declaration made earlier in the file fell through to the range covering the identifier itself. That excerpt was worse than sending nothing: it spent tokens to say a function is itself. It now resolves through the same alias path a named export uses, and a default export naming something not declared in that file sends nothing rather than something misleading.

    Both were found by running this project’s own predict_failures over src/core/, and both had escaped the test suite. A third report from the same sweep — that predictFile would throw on an empty findings array — was a false positive: rank() guarantees the list is never empty, on a path the model could not see. Verifying before acting is the point.

View source on GitHub

v0.5.1

Read release notes

Project

  • README gets a beginner on-ramp; the benchmark report defines its terms up front. A “New to this?” section now sits right after the opening pitch, ahead of the existing developer-facing content: what the tool does in one sentence, what MCP means, which of the two shapes (VS Code extension or MCP server) a reader wants, and the three prerequisites — Node.js, an already-signed-in AI coding CLI, and a built checkout.

    bench/RESULTS.md opens with a plain-language paragraph on what the test actually did before naming it, and its glossary now appears before the Bottom line table uses the terms rather than partway through the report. Edited in bench/markdown.mjs, the generator, and regenerated rather than hand-edited, so the wording survives the next npm run bench. No measured number changed; the charts came back byte-identical.

View source on GitHub

v0.5.0

Read release notes

Project

  • Code contributions are closed for now; bug reports stay open. The interfaces are still moving between releases, so pull requests against them would be work spent against decisions that have not settled. Documented in CONTRIBUTING.md with the reasoning and the fact that it is temporary. The feature-request issue template is gone; the bug template stays, because a report from a codebase the author cannot see is what the generated-corpus benchmark cannot provide.

Changed

  • scan_project no longer ranks test files (#10). *.spec.*, *.test.* and anything under __tests__/__mocks__/test/tests/spec are left out; includeTests: true brings them back, and the reply reports excludedTests when any were withheld.

    They were never ranked high for a real reason. DENSITY_WEIGHTS scores asyncCalls, and a spec file full of mocked awaits reads as async complexity while carrying none of the defect risk that weight stands in for. Four of the top 25 hits on a real backend were .spec.ts files; on this project’s own src/, tests took five of the top six slots. Those are wasted slots in the one tool whose whole job is spending a limited reading budget well.

    The filter is applied at the scan_project call site rather than inside collectSourceFiles, so the VS Code project-wide run still covers tests — a human who asked for the whole workspace is not paying per file read. The predicate itself lives in core/sourceFiles.ts so both surfaces agree on what a test file is, and it takes a path relative to the scanned root: judged on absolute paths, anyone whose projects sit under a directory named test would have had their entire codebase excluded.

Added

  • The server now ships the fix-verification rule itself, as MCP instructions plus an onFix hint on predict_failures replies that clear the precision gate. It asks the calling agent to have a non-mechanical fix reviewed from outside the context that produced it — a sub-agent where the host has them, otherwise a fresh predict_failures on the edited file.

    An agent that has just written a fix is the worst-placed reader of it: the reasoning that made the fix look right is still in its context, so reviewing it from the same seat re-derives the first conclusion instead of testing it. An independent pass catches defects the author’s pass structurally cannot. Left to the agent’s judgement this happened on some runs and not others; left to each user’s project instructions it reached only the users who already knew about the failure mode.

    It is a floor, not a ceiling — the agent may verify more often, and a mechanical fix is exempt so a typo does not cost two model passes. The hint is duplicated on the wire because instructions is sent once at initialize and not every client forwards it to the model, while a tool result always reaches it; gating it on actionable keeps a clean file’s reply free of it.

View source on GitHub

v0.4.0

Read release notes

Added

  • GitHub Copilot CLI as a third provider (#14). copilot now sits behind the same CliProvider interface as Claude Code and Codex: pick it in Predictive Debugger: Connect, or pass provider: "copilot" to predict_failures. Auth is borrowed the same way — from /login, from the gh CLI’s token, or from COPILOT_GITHUB_TOKEN/GH_TOKEN/GITHUB_TOKEN — and no token is ever read by this extension.

    The prompt is piped in on stdin rather than passed as copilot -p <text>, because a prompt carrying up to 120,000 characters of source does not fit on a command line on any platform; piped input is a full non-interactive turn as long as -p is absent. The turn runs with shell, write and url denied, built-in MCP servers disabled, and ask_user off, which leaves Copilot the same read-only footing as codex exec.

    Sign-in state is a hint, not proof: /login stores its token in the system credential store, which this extension does not read, so the connect flow’s live prompt remains what actually confirms it. Model override: predictiveDebugger.copilotModel.

View source on GitHub

v0.3.1

Read release notes

Fixed

  • The benchmark’s exact-line score was grading against one anchor when the corpus already recorded several. isNear has graded against acceptableLines since the enclosing-function change, on the stated grounds that a defect with two loci has two correct answers and the anchor the corpus author typed first is not privileged among them. exactLine never got the same treatment — it stayed a strict predictedLine === plantedLine — so a model that switched between two equally correct sites read as a regression. That is exactly what the 0.3.0 entry below recorded as a drop from 12→9 and 17→15. Re-grading the same recorded runs against acceptableLines gives 15 and 18; re-grading the pre-#13 runs the same way gives 15 and 18 as well. Like for like, exact-line accuracy did not move. The measurement did.

    Three labels were widened to match, each a defect whose two loci sit on different lines: pricingService.js now accepts the dereference that throws (36) as well as the out-of-bounds read that feeds it (34); the lost update in reconciliationWorker.js accepts the write the stale value lands in (40) as well as the read that goes stale (28); and cache.service.ts, which already accepted the teardown body, now also accepts the onModuleDestroy declaration, since that is the line a reader is told never releases the timer. alsoAnchor takes a list rather than a single anchor to allow it. Runs now carry acceptableLines, so a recorded result can be re-graded without the manifest it came from.

    This does not settle which line the Problems panel should underline. It says the benchmark should not decide it by accident.

  • auditService.js was not a clean control. The service() generator emitted methods that initialise total to 0, await three values, use none of them, and return { id, total } — an unconditionally zero result and three dead fetches, whenever it was parameterised with no loops. The tool reported it, correctly, at score 0.75 in two of three trials, and the answer key scored that as a false alarm. It is the same trap as the duplicated member names fixed earlier in the same file: filler that accidentally contains a bug measures the benchmark rather than the tool.

    A method with no loop now folds what it fetched into the total instead of discarding it. Three generated files change (auditService, customerService, notificationService); no file carrying a planted defect is touched. Clean controls go from 16 of 18 to 18 of 18, and separation from 0.988 to 1.000.

    Worth stating plainly: this defect predates every measurement in this file. The 0.3.0 entry below records 18 of 18 true negatives on this corpus, and that run simply did not happen to flag auditService.js — the file was already broken. Clean-file behaviour varies more between runs than a single three-trial run can establish, so the claim that the callee context “fixed” the one recorded false alarm is weaker than it reads there.

Changed

  • MCP replies are no longer pretty-printed. The reader is a model and every space in a two-space indent is billed to it: 46 of the 161 tokens in a typical predict_failures reply, 29% of the response, spent on making a raw transcript pleasant for a human who is not the audience. Arrays were the worst of it — checked put each pattern id on its own indented line, which is most of what that field appeared to cost when it was added. The tool is worth calling instead of reading the file only while the answer is much smaller than the file, so the response budget is the product rather than a detail of it. No field changed; only the whitespace between them.

    Measured across both corpora: the mean reply fell from 177 to 129 tokens on bench/corpus and from 169 to 126 on bench/corpus-ts, a 27% cut. That is below where the reply sat before 0.3.0 (134 and 133) while still carrying the checked field and the callee context that had pushed it up — so the ~30% those added is repaid, with interest, and the README’s cost-per-answer figure is right again rather than merely stale.

View source on GitHub

v0.3.0

Read release notes

Added

  • predict_failures can return a ranked list of findings per file instead of one verdict, behind multi: true on the MCP tool and predictiveDebugger.multipleFindings in the extension. One finding per file meant fixing the reported issue and calling again to see whether there was another, at 5–15 seconds a call. Off by default because it is not a free win: more findings per call is also more surface for a false positive per call, and the precision gate in confidence.ts was measured on single-verdict replies. The gate now applies per finding rather than per file, the Problems panel gets one diagnostic per finding, and findings appears in the MCP response whenever a reply carried more than one — including when multi was not set, since dropping a volunteered second defect is the behaviour this replaces. Unmeasured: whether asking for a list costs precision is a benchmark question, and bench/measure-file.mjs now records the finding count so it can be answered from a normal run. Resolves #7.

  • predict_failures reports which bug categories were checked, not only which one was found. Nothing in the response distinguished “I checked for this and found nothing” from “I never considered it”: both come back as pattern: "none", score: 0. The new checked field lists the catalogue ids the model says it weighed, always present in the MCP response and empty when the model reported none. It is a self-report, not a proof — it makes coverage visible instead of assumed, which is what makes a bias like the race_condition monoculture legible in the reply rather than silent. Distinct from status: "unavailable", which continues to mean no verdict could be parsed at all. Resolves #12.

  • The classifier prompt carries the definitions of functions the file calls, resolved one level deep through relative imports. The motivating false positive was attributable to single-file scope entirely: the disproof of the reported claim was that a callee was idempotent, and that callee was one import away, so the model had no way to see it. The evidence policy already asks the model to disprove a candidate before reporting it; this gives it the material to do so instead of assuming the worst about code it cannot see. One hop only, relative specifiers only — node_modules is never read — and both the per-callee and the total size are capped, so the added cost is bounded at roughly an eighth of what a large file already costs. Off via calleeContext: false on predict_failures. Resolves #4.

    Measured on bench/corpus and bench/corpus-ts after this change (see bench/RESULTS.md): function-level hit rate held at 18 of 18 planted defects on both corpora, and the one false alarm in the TypeScript corpus (lib/headers.mts, 17 of 18 true negatives before) did not reproduce — 18 of 18 after. Exact-line accuracy dropped on both corpora (JS 12→9, TS 17→15), entirely on three files where the model now names a different line inside the same already-correctly- identified function — reconciliationWorker.js, pricingService.js, and cache.service.ts. Nothing moved to a wrong function or a false alarm. Tool tokens per call rose ~30% (JS 4,816→6,369, TS 4,782→6,095), matching the added callee context and the checked field from #12. Net: no regression on the metrics the product gates on, a real fix to the one recorded false positive, at a real token cost — worth shipping on by default, worth re-measuring if the exact-line drop turns out to matter to anyone reading the Problems panel rather than an agent.

Changed

  • The model-reply parser recovers more from a truncated response. It cut at the last top-level comma, a rule written for one object, which threw away complete findings when a list was cut off two levels down. It now cuts back to the last value boundary at any depth and closes whatever is still open, so a reply that stops inside the third finding keeps the first two, and a verdict whose checked list was cut mid-id keeps the ids that did arrive. Nothing is invented: everything before the cut is what the model sent.
  • BugPrediction is one finding; BugAssessment is what a call establishes about a file. checked and truncated moved to the assessment, because both describe the examination rather than any defect it turned up, and FilePrediction.aiPrediction became FilePrediction.ai.
View source on GitHub

v0.2.0

Read release notes

Changes driven by the benchmark in bench/, each with the measurement that motivated it, plus the housekeeping a first public repository needs.

Changed

  • A prediction is graded against the function the defect lives in, not a tolerance of three lines. The tolerance measured the wrong thing in both directions. On lib/retry.js, thirteen lines long, it accepted most of the file, so a prediction landed inside it by luck. On a defect with two defensible sites, the acquisition of a resource and the teardown that fails to release it, it called a correct answer a miss. bench/enclosing-function.mjs derives the range from the source, skipping single-line callbacks so the unit is the method a reader would open. The exact-line count is reported alongside it, and the two disagree: 18 of 18 inside the function, 12 of 18 on the line itself.
  • The pattern catalogue has an other id. It was a closed list of six, and a closed list threw away correct answers: asked about a method filtering on createdAt where its own documentation promises every record edited since a timestamp, the model named the line, explained the contradiction, then had to answer none because nothing fitted, and parsePrediction forces the score to 0 for none. That happened on every trial of both defects planted outside the catalogue in the TypeScript corpus, five of fifteen buggy runs. Adding other recovered all five. It also started reporting duplicated dead code on the JavaScript corpus, which the model itself described as “redundant but not itself a runtime failure”, so the prompt now states that other is for runtime failures and not for maintainability.
  • The classifier prompt treats concurrency as a normal execution. The evidence policy asks the model to disprove a candidate defect before reporting it, and a single sequential reading of a file always disproves a race condition — so the policy suppressed the planted race in all three trials. Stating that anything on a timer, in a polling loop, or exported as a service method can be re-entered before an earlier call finishes recovers it at no cost to precision: still 0 of 18 false alarms, planted lines named up from 14 to 15 of 18, buggy files reported clean down from 3 to 0, and separation (AUC) from 0.917 to 1.000. The bill is latency, 4.9 s to 6.2 s per file. A local variable accumulated inside one invocation is explicitly exempt, so ordinary loop accumulators do not start reading as shared state.
  • Predictions are precision-gated before they become user-visible defects. VS Code now creates a Problem only for a model verdict at or above 0.70, and summaries call lower scores “no high-confidence failure” instead of repeating a speculative bug. predict_failures exposes the same decision as actionable, plus an explicit status (actionable, uncertain, none, or unavailable). Uncertain results retain their pattern, score, line, and reason in the output but are labelled as not added to Problems. Replaying the same 36 Claude responses cuts false Problems from 12/18 to 0/18, with actionable planted-line hits moving from 18/18 to 15/18. A fresh current-build Codex run produced 0/18 false alarms and 11/18 actionable planted-line hits.
  • The classifier now requires local evidence. Hypothetical malformed inputs, dependency results that violate their normal contract, and an awaited rejection merely propagating to its caller are no longer enough to claim a defect. Wrong return values and side effects still count when documentation or local control flow establishes the contract.
  • scan_project ranks by risk density instead of total risk. The total grows with file length, so ranking by it was close to ranking by size (Spearman ρ = 0.83 against raw token count) and put the two smallest planted bugs 38th and 39th of 40. Density divides the signals by the length of the file and damps the ones that accumulate with it — mutations, branches, cyclomatic complexity — to a tenth of their weight. On the benchmark corpus the planted bugs move from ranks 11, 14, 17, 38, 39, 40 to 4, 9, 11, 12, 39, 40; at a budget of 15 files the ranking now surfaces 4 of 6 bugs against 2 before, and beats random ordering at every budget instead of none. riskScore is unchanged and still reported.
  • combinedScore is now 0.9 × model verdict + 0.1 × static risk, with log anomalies folded in at 0.15 only when a log file is supplied and the other weights renormalised. The old 0.4/0.4/0.2 blend let a complexity score that separates buggy from clean files with an AUC of 0.33 — worse than chance — outvote a verdict with an AUC of 0.91, dragging the combined figure to 0.74 and ranking a clean 200-line service above four of six real defects. It also capped the score at 0.8 whenever no log file was given.
  • predict_failures and scan_project return a much smaller payload. The verdict was about a fifth of the predict_failures reply; the rest was the static metric block, a log stanza reporting that log analysis had not been requested, and the absolute path the caller had just supplied. Metrics and logs now sit behind a verbose flag, the path is echoed as given, and scan_project reports paths relative to the directory it was asked about. Scan output for the 40-file benchmark corpus fell from 6,512 to 3,307 tokens.

Added

  • The report’s charts are Vega-Lite specifications rendered to standalone SVG at build time, replacing hand-written SVG strings. The layout arithmetic — tick placement, label collision, legend offsets — was being maintained by hand for every new chart. The two literal light/dark palettes and the <picture> pairing are unchanged, and the output is still deterministic, so the committed SVGs only move when the data does.
  • A provider comparison (bench/RESULTS.md section 4, charts/providers.*.svg) showing planted lines found and false alarms raised for each CLI, raw and after the actionable gate.
  • A before/after section in bench/RESULTS.md, with bench/baseline.json holding the measurements from commit 3c633b1 so both columns are read out of result files rather than typed by hand.
  • Separation (AUC) reported by bench/measure-file.mjs for all three scores, so “which number should an agent gate on” is answered by the harness. Current run: score 0.96, combinedScore 0.95, riskScore 0.33.
  • riskDensity on StaticAnalysis, and lines on FileMetrics.
  • verbose input on predict_failures and scan_project for the full payload.
  • npm run bench to run the four benchmark steps in order.

Fixed

  • The corpus generator gives every member a distinct name. Both word lists held eight entries and every name was built from a single index into both, so a class with more than eight members defined the same method twice: eight duplicated names of nineteen members in adminController.js, eight of eighteen in orderRepository.js. Those are clean controls, and the duplication is a real defect the tool reports, so the answer key called a correct finding a false alarm. It also padded the “cost of reading the file” baseline with dead code. It confused five separate measurements before it was found.
  • A partial provider outage no longer destroys the measured results. The guard fired only when every call failed. A session limit reached partway through let 21 of 36 calls fail, which emptied the control group entirely and still counted as a run, replacing the previous results. Any failure now writes to a .partial.json sidecar and leaves the measured file alone.
  • The source sent to the model carries line numbers. The prompt asked for a 1-based line number and sent raw source, leaving the model to count newlines by eye. It described the defect correctly and then reported a line six to twelve lines away on the two largest files with a planted defect: five of eighteen runs. Small files were exact, which is the signature of a counting problem rather than a reasoning one. The size cap now applies to the numbered text, so numbering cannot push the prompt past the bound the cap exists to enforce.
  • A verdict cut off mid-reply is recovered instead of discarded. extractJsonObject took everything from the first { to the last } and gave up when JSON.parse failed, so a complete verdict followed by a truncated key was thrown away as unknown and scored as a missed defect. It now closes the object at the last finished pair, and accepts the reconstruction only when it carries both a pattern and a score.
  • Decorated classes no longer fail to parse. @Injectable() produced a parse error, which zeroed a file’s metrics and sank it to the bottom of the scan_project ranking, so the tool recommended reading a Nest or Angular project’s controllers and services last. The verdict from predict_failures was unaffected, since the model receives the raw source either way. Enabling decorators-legacy and decoratorAutoAccessors covers Angular, Nest, TypeORM, MobX and the accessor keyword.
  • Class and object methods are counted as functions. Babel does not report a method as a FunctionExpression, so the metric visitor was blind to every method in a class-based codebase. longFunctions carries 0.15 of the risk weight and had never fired once across the 40 files of the benchmark corpus. The ranking on that corpus is unchanged, since its generated methods are all short, and the rank correlation between riskDensity and raw file size improved from 0.359 to 0.326.
  • .mts and .cts files are found by a scan. They were the only TypeScript extensions missing from the source walker.
  • bench/measure-file.mjs no longer replaces the last valid result file when every provider call fails, and BENCH_OUTPUT can keep provider-specific validation runs separate. This was found when the Claude CLI hit its session limit during the post-tuning rerun.
  • The recommended score threshold no longer sits on the noise. The lowest cut with no false alarms is refitted every run — 0.65, 0.60 and 0.70 across three runs of the same trials — and the A/B in section 4 was given 0.60 and immediately reported a defect in a clean file that scored exactly 0.60. The report now recommends a gate a step above the highest clean score observed in any run, and says why.
  • gpt-tokenizer is declared as a devDependency. The benchmark scripts imported it, so the documented “run it yourself” steps failed on a clean checkout.
  • A handful of em dashes in this file were stored as CP-1252 bytes rather than UTF-8, so they rendered as replacement characters.
  • The score blend is its own module. combineScores moved out of predictFile into core/prediction/score.ts. It was a private function reachable only through a live model call, so the weighting the README devotes a section to had no test at all; it now has five. The blend is unchanged except that out-of-range and non-finite inputs clamp to [0, 1] instead of only being capped above. The JSDoc for predictFile had also drifted onto the private function below it.
  • The MCP server reports the version from package.json. It announced 1.0.0 over the wire while the package was 0.1.0. esbuild now injects the real value, so there is one number rather than two.
  • prepack builds before packing. vscode:prepublish covered vsce but nothing covered npm publish, so publishing without a manual build would have shipped a stale dist/ — or an empty package, since dist/ is git-ignored.
  • dist/extension.js is excluded from the npm tarball. An MCP consumer never loads the VS Code bundle; the package drops from 515 kB to 335 kB.
  • SECURITY.md, CONTRIBUTING.md, issue templates and a pull request template.
  • The benchmark report states that its numbers are in-sample. The corpora were built alongside the tool and the classifier prompt was revised against them, which the per-corpus caveats said and the headline did not.
  • Corrected figures in the README that had fallen behind the benchmark: the headline context table, the clean-control counts, the flat answer cost, and the test count. The README also still described localisation with the ±3-line tolerance that this release replaced with enclosing-function grading, and claimed no TypeScript had been measured after npm run bench:ts was added.
View source on GitHub

v0.1.0

Read release notes

First public release. Pre-1.0 because macOS and Linux are covered by CI but have not been exercised against a real CLI installation — see the caveats in the README.

Added

  • VS Code extension with three commands: connect to a CLI, predict failures in the current file, and rank an entire project. Results appear as diagnostics in the Problems panel and as a report in the output channel.
  • MCP server (out/mcp/server.js) exposing five tools to coding agents:
    • analyze_file, scan_project, analyze_logs — deterministic, local, no model call and no credentials
    • predict_failures — full pipeline including a model verdict from the signed-in CLI
    • list_providers — installation and sign-in diagnostics
  • CLI-borrowed authentication. Model access comes from the Claude Code CLI or Codex CLI the user is already signed in to. The extension never reads, stores, or transmits a token; only the chosen provider id is persisted.
  • Static analysis producing complexity metrics, a heuristic risk score, and the ranked signals that produced it.
  • Dependency-free log analyzer (tools/log-analyzer/analyze_logs.py) that scores lines by severity and vocabulary rarity using only the Python standard library.
  • Test suite of 48 tests on Node’s built-in runner, with no test dependencies.
  • CI across Linux, macOS and Windows on Node 22 and 24, including an end-to-end MCP smoke check that needs no credentials.
  • esbuild bundling for both entry points. The packaged extension went from 1805 files / 2.63 MB to 10 files / 496 KB, which removes the extension-host startup cost of resolving ~1000 loose JavaScript files.

Changed

  • Replaced the OAuth/PKCE and API-key sign-in flows with CLI-based connection.
  • Replaced embedding-similarity bug matching with a prompt-based classifier that returns a pattern, a line number and a reason — the CLIs expose a chat interface, not an embeddings endpoint.
  • Rewrote the log analyzer, which previously required an OpenAI API key and numpy, contradicting the point of borrowing the CLI’s sign-in.

Security

  • The extension declares untrustedWorkspaces: supported: false, and predictiveDebugger.pythonPath is machine-scoped — a repository cannot point the interpreter that gets executed at a binary of its own choosing.
  • Analysed source is framed as untrusted data in the prompt, and the model’s pattern/reason fields are length-capped. Claude runs with --tools ""; codex exec has no equivalent switch, so the boundary is stated explicitly.
  • Files above 4 MB are skipped rather than read into memory, since the MCP tools accept arbitrary paths from the calling agent.

Fixed

  • The risk score saturated at 1 for every non-trivial file, which made scan_project’s ranking useless on real code — the flagship agent tool could not distinguish a 700-line module from a 40-line one. The hard clamp is replaced with a smooth, strictly monotonic saturation, so heavy files always compare correctly. Found by running the tool on its own source.
  • Silent prompt truncation. Source sent to the model was capped at 24,000 characters — about 700 lines at typical density — so a verdict on any larger file was based on a prefix with no indication given. The cap is now 120,000 characters (~3,500 lines) and truncation is reported on the prediction, in the diagnostic and in the output channel.
  • A file that cannot be parsed no longer throws. Babel’s errorRecovery does not handle unbalanced braces, so analyzeSource now reports a parseError with a zero score instead of aborting.
  • A project scan no longer discards all results when one file fails. Failures are collected separately and reported alongside the successful analyses.
  • combinedScore treated a clean log score as high risk. Log health is now inverted into a risk contribution.
  • The extension bundle could not be loaded: "type": "module" in package.json conflicted with the CommonJS output required by the extension host.
  • @types/vscode was newer than the declared engines.vscode, which prevented packaging and allowed use of APIs missing from the minimum supported version.
View source on GitHub