Coverage Studio for VS Code 2.2026.3 — The Microsoft Testing Platform, and an Excluded Line Is Grey
2026-10-25
Added
- Tests on the Microsoft Testing Platform run with coverage. A test project on the platform — xunit.v3 with
UseMicrosoftTestingPlatformRunner, MSTest.Sdk, the NUnit and MSTest runners, TUnit — is a program of its own, and the .NET 10 SDK's dotnet test will not start one. Run Tests with Coverage now sees the platform in the project file and runs each such project as dotnet run --project … -- --coverage --coverage-output-format cobertura --results-directory …, lists its tests with --list-tests, filters one test or one class with the framework's own flag — xunit.v3's exact --filter-method and --filter-class, so Ns.Quote no longer also runs Ns.QuoteBuilderTests — renames the GUID-named report to coverage.cobertura.xml as Microsoft's collector's is, and reads the TRX the TrxReport extension writes. A solution that mixes platform and VSTest projects runs each its own way. When no project has the Microsoft.Testing.Extensions.CodeCoverage package, the recipe names it.
- An excluded line is grey. A line taken out of the count —
LCOV_EXCL_LINE, a region, [ExcludeFromCodeCoverage], or the report itself — was drawn like a line with no code at all, so it could not be told from an untested one once the coverage gutter mode put green on every line that ran. It now gets a grey bar in the gutter and a grey wash where the line highlight is on, and the hover says how it was taken out and why. A gap on the same line wins; a comment the exclude pass took out is not marked. fuciniCoverage.excluded.showMarks: false turns the marks off.
- Files the report names that are not on disk can be left out. They counted, and showed in the Coverage view, however many of them a generated or deleted file left behind.
fuciniCoverage.missingSources.includeInTotals: false leaves them out of the totals and the views; the log names what was left out, file by file, under FUCINI_MISSING_001, so a remap that would bring them back can be written.
- Choose Diff Base… picks the ref for diff coverage, the new-code gate and the indirect changes from the repository's own branches and tags — the ref in force first, then the local branches, then the remote ones, with a last entry for typing any other ref — and writes it to
gitDiff.baseRef for the folder. A base spelled wrong by hand, origin/main where the remote has master, gave a diff of nothing and said nothing.
Coverage Studio for Visual Studio 2.2026.2 — The Microsoft Testing Platform, and an Excluded Line Is Grey
2026-10-25
Added
- Tests on the Microsoft Testing Platform run with coverage, as in VS Code: a test project on the platform is run as the program it is, with the platform's own flags, from Tools → Coverage Studio → Run Tests with Coverage, for one test at the caret and per test; its GUID-named report is renamed like Microsoft's collector's. The reader says what a project file means; the host only runs what it is told.
- An excluded line is grey, in the margin and in Quick Info, which says how the line was taken out and why.
excluded.showMarks in fucini-coverage.json turns the marks off.
- Files the report names that are not on disk can be left out with
missingSources.includeInTotals: false; the log names them under FUCINI_MISSING_001.
- Choose Diff Base… under Tools → Coverage Studio: the repository's branches and tags from
git for-each-ref, the ref in force first, and a prompt for any other; the choice goes to gitDiff.baseRef.
- A tracefile from a URL. An
https:// entry in coverageFile.paths is fetched as the baseline is — same headers, same ${env:…}, same cache — and loaded like a file; the copy fetched before stands in when the server does not answer.
- A project outside the solution folder is searched and watched too. A solution kept in a folder of its own, with the projects beside it, found no report and saw no new one; the project folders are now searched for reports and watched for new ones as the solution folder is, and named in the log at load.
Coverage Studio on the command line 2.2026.5 — files not on disk can be left out
2026-10-25
Added
--exclude-missing-sources. A file the report names that is not on disk is left out of the totals, the gaps and the exports, and the run says how many were left out; "fuciniCoverage.missingSources.includeInTotals": false in the settings file does the same for every command, as the editions' setting does.
Coverage Studio on the command line 2.2026.4 — a file two engines measured is counted once, wherever you start the command
2026-10-04
Fixed
- The folder you start the command in does not decide what a report means. 2.2026.3 roots a report's relative paths at the directory that report was written in — but only for a command started somewhere else. Run from the root of the tree it measures, which is where a coverage command is normally run, a relative path was still answered by that folder, and a file llvm-cov had already named in full was counted a second time: one tree of 5,258 lines read as 5,836. Only
--cwd and the report's own location decide now. The same fix is in both editors, where the folder was the one VS Code or Visual Studio happened to be started from.
export --format json wrote the run over your settings. Its file was fucini-coverage.json — the name the settings are read from — so in a folder configured the ordinary way the export destroyed the thresholds it had just judged by. The run is written to coverage-run.json, and export and badge refuse an --out that names the settings file.
--cwd takes a folder however you type it. A relative one — --cwd ., --cwd ../tree — was kept as it was typed and read by the wrong path rules: on Windows it answered that a settings file half POSIX and half Windows could not be read, and found no reports at all. A trailing separator is no longer part of the folder either.
- A flag nobody declared is not a report.
summary -o out.json took -o for a report and said no such file. Only -h and -v are short; -- still passes a file whose name begins with a dash, and a flag typed before the command is named in the error rather than answered with the whole help.
check --json says what its exit code says. With no threshold set it wrote "passed": false beside exit code 0, and a pipeline that read the JSON rather than the exit code failed a run the command had passed. passed is the exit code in a word; configured is what says there was no gate. An empty --lines "" is refused instead of quietly turning that gate off.
- A compare puts the baseline where it puts the run.
pathRemap and the root a report declares were applied to the run and not to the baseline, so a baseline written on a build server paired with nothing and the compare reported that nothing had moved beside a total that plainly had.
Coverage Studio on the command line 2.2026.3 — a file two engines measured is one file again
2026-10-04
Fixed
- A report's relative paths are rooted where that report was written. llvm-cov writes a file's full path; gcovr writes it relative to the build folder its report sits in. Given both reports of one tree,
summary listed every file the two had in common twice — once with each engine's figures — and added both to the total, because a relative path had no root to resolve against and came out against whatever folder the shell happened to be in. One tree of 5,258 lines was reported as 5,836, the surplus belonging to files under a path that holds none. Each report is now rooted at its own directory and the ancestors above it, up to the folder the command runs in, as both editions already do it — so the two meet as one set of files without a pathRemap rule per build directory.
- A settings file that begins with a byte-order mark was refused too. An editor on Windows may write one in front of the text; it is no longer taken for part of the JSON.
- Output piped into a reader that stops early ends quietly.
summary piped into head, or into PowerShell's Select-Object -First, died with an unhandled EPIPE and a stack trace. The reader closing the pipe is its own choice, not a failure of the command: it now ends with the exit code it had reached.
Coverage Studio on the command line 2.2026.1 — a settings file with patterns in it is read
2026-10-04
Fixed
- A settings file with patterns in it was refused. With
**/*.lcov.info in one path of fuciniCoverage.coverageFile.paths and **/coverage.json in the next, every command ended with is not JSON: what lies between the two was taken for a comment. The file is now read as the editors read it, a comment after a value and a comma before a closing bracket included.
Coverage Studio on the command line 2.2026.0 — fucini-coverage, the first release
2026-10-04
Added
- The reader on the command line, free.
npm install -g fucini-coverage — the same reader as the two editions, for the places an editor is not. One package, one behaviour on Windows, Linux, WSL and macOS, Node 20 or newer.
check, the gate. Exit code 1 below a threshold on lines, branches, functions, MC/DC, conditions, regions, mutation or the weighted score, from the settings or the command line; --new-code 80 --base-ref origin/main judges the lines a branch changed, from git diff run by the tool; --require-reasons fails an exclusion marker without a reason.
summary, export, report, badge, compare. The totals and every file; the run as CSV per file or per function, Markdown, LCOV, Cobertura, SARIF, the exclusions list, a badge or Fucini's JSON; the multi-page HTML report; the badge coloured by the settings' scale; this run against a baseline, regressions first, with --fail-on-regression for a pull-request check.
mcp, for an AI agent. The six tools of the editions, served over the run on 127.0.0.1 behind a token, with the entry for the client's mcp.json printed.
- The settings file of the Visual Studio edition.
fucini-coverage.json beside the code, with the fuciniCoverage.* keys of the VS Code settings; .vscode/settings.json under it. Reports are named as files, folders or patterns, a .gz is unpacked, and every command documents itself: fucini-coverage help <command>.
Coverage Studio for VS Code 2.2026.2 — The Trial Begins With the Licence Server’s Answer
2026-10-04
Changed
- A new install’s trial begins with the licence server’s first answer. Until the extension has reached fucini.software once it runs on the Community tier and says why. An install already on its trial, or with a licence, notices nothing.
- No trial in a development container or a Codespace. The extension starts on the Community tier there; a subscription code, a key or the 7-day pass unlocks everything.
- The usage ledger knows a machine by a second hash, of the operating system’s own identifier. Only the hash is sent; see the privacy policy.
Coverage Studio for Visual Studio 2.2026.1 — Small Fixes
2026-10-04
Fixed
- Small fixes. A maintenance release: small corrections under the hood, and nothing that changes what you see or how it is used.
Coverage Studio for VS Code 2.2026.1 — Small Fixes
2026-10-04
Fixed
- Small fixes. A maintenance release: small corrections under the hood, and nothing that changes what you see or how it is used.
Coverage Studio for Visual Studio 2.2026.0 — The Coverage on Screen, for AI Agents
2026-10-04
Added
- The coverage on screen, for AI agents. An agent writing tests could run them and read a percentage; it could not ask what Visual Studio knows. Six tools answer that, in words an agent can act on:
coverage_summary, coverage_gaps, tests_for_line, patch_coverage, coverage_delta and test_run_command. An agent that speaks MCP reaches them through a server Visual Studio runs when fuciniCoverage.mcp.enabled is on — off by default, on 127.0.0.1 only, behind a token of your own, and closed to web pages. Copy MCP Server Configuration puts the client's entry on the clipboard.
- The same numbers as on screen. The tools answer from the run that is loaded — the same exclusions, the same remapping — so an agent told to cover the gaps of a function, and a reviewer looking at the margin, are talking about the same lines.
Changed
- A key's plan decides in Visual Studio what it decides in VS Code. A plan that leaves out a feature — MC/DC, the HTML report, the names of the tests that ran a line — left it out in VS Code and not here. Both editions now read the same table: a feature the plan does not include is withheld, and its command says so.
- The minimum version the licence server asks for holds here as well. Both editions carry the same version from 2.2026.0 on, so a build older than that minimum asks to be updated, as the VS Code edition does.
Coverage Studio for VS Code 2.2026.0 — The Coverage on Screen, for AI Agents
2026-10-04
Added
- The coverage on screen, for AI agents. An agent writing tests could run them and read a percentage; it could not ask what the editor knows. Six tools answer that, in words an agent can act on:
coverage_summary, coverage_gaps, tests_for_line, patch_coverage, coverage_delta and test_run_command. VS Code's own agent has them as language-model tools, with nothing to set up. Every other agent that speaks MCP reaches them through a server the extension runs when fuciniCoverage.mcp.enabled is on — off by default, on 127.0.0.1 only, behind a token of your own, and closed to web pages. Copy MCP Server Configuration puts the client's entry on the clipboard.
- The same numbers as on screen. The tools answer from the run that is loaded — the same exclusions, the same remapping — so an agent told to cover the gaps of a function, and a reviewer looking at the margin, are talking about the same lines.
Coverage Studio for Visual Studio 1.2026.22 — A File Two Engines Measured Is One File
2026-10-04
Fixed
- A file two engines measured is one file. llvm-cov writes a file's full path; gcovr writes it relative to the build folder its report sits in. A relative path is rooted at the report's own directory and the folders above it — but before that, the resolver asked whether the path existed as it stood, and that question is answered from the folder Visual Studio itself was started in. Where that folder held the file, the path stayed relative: the file had two names, was listed twice in the Coverage window and counted twice in the totals. One tree of 5,258 lines read as 5,836. A path that names no location of its own is no longer asked about at all; it is rooted where the report was written, and the row says so.
Coverage Studio for VS Code 1.2026.23 — A File Two Engines Measured Is One File, and the Gutter Mode You Pick Stays Out of Your Settings
2026-10-04
Fixed
- A file two engines measured is one file. llvm-cov writes a file's full path; gcovr writes it relative to the build folder its report sits in. A relative path is rooted at the report's own directory and the folders above it — but before that, the resolver asked whether the path existed as it stood, and that question is answered from the folder the extension host was started in, which after
code . is your project root. The path then stayed relative: the file had two names, was listed twice in the Coverage view and counted twice in the totals. One tree of 5,258 lines read as 5,836. A path that names no location of its own is no longer asked about at all; it is rooted where the report was written, and the row says so.
- The gutter mode you pick holds for the session, and your settings stay as you wrote them. Choosing a mode from the status bar, the picker or Toggle Coverage Painting wrote it into
settings.json — a file under version control in many projects, rewritten by a button meant to be pressed twice a minute. The choice lives for the session now, and a mode you typed into your settings still wins when the window opens.
Coverage Studio for Visual Studio 1.2026.21 — Would a Test Notice? A Mutation Run at the Caret
2026-10-03
Added
- Run Mutation Tests Here. Coverage says a line ran; a mutation tool says whether a test would notice it being wrong. The function the caret is in — or the lines selected, or the file — is what the folder's runner is told to mutate, and its report is loaded beside the coverage on screen: Stryker.NET for C#, StrykerJS for JavaScript and TypeScript, PIT with Maven for Java. A project's mutation run takes an hour; one function's takes a minute.
Coverage Studio for VS Code 1.2026.22 — Would a Test Notice? A Mutation Run at the Caret
2026-10-03
Added
- Run Mutation Tests Here. Coverage says a line ran; a mutation tool says whether a test would notice it being wrong. The function the caret is in — or the lines selected, or the file — is what the folder's runner is told to mutate, and its report is loaded beside the coverage on screen: Stryker.NET for C#, StrykerJS for JavaScript and TypeScript, PIT with Maven for Java. A project's mutation run takes an hour; one function's takes a minute.
Coverage Studio for Visual Studio 1.2026.20 — The App Itself, with Coverage
2026-10-03
Added
- Run App with Coverage… measures what a person reaches, not only what the tests do: the folder's .NET app is started under Microsoft's
dotnet-coverage, in a console of its own, and runs until it is closed. What was walked through is then loaded like any run, and in a coverage session added to what the tests ran. Needs dotnet tool install --global dotnet-coverage.
Coverage Studio for VS Code 1.2026.21 — The App Itself, with Coverage
2026-10-03
Added
- Run App with Coverage… measures what a person reaches, not only what the tests do: the folder's .NET app is started under Microsoft's
dotnet-coverage, in a console of its own, and runs until it is closed. What was walked through is then loaded like any run, and in a coverage session added to what the tests ran. Needs dotnet tool install --global dotnet-coverage.
Coverage Studio for Visual Studio 1.2026.19 — Zip Artifacts from GitHub and GitLab
2026-10-03
Added
- A CI baseline from a zip artifact. GitHub and GitLab publish an artifact as an archive. Where
baseline.report names one — a URL, or a zip on disk — it is opened and the report in it kept: the member named after the # (ci/artifact.zip#coverage/lcov.info), else the archive's only file, else its only file that looks like a report.
Coverage Studio for VS Code 1.2026.20 — Zip Artifacts from GitHub and GitLab
2026-10-03
Added
- Zip artifacts from GitHub and GitLab. CI publishes an artifact, and an artifact is an archive. A fetched zip is opened now and the report in it kept — the member named after the URL's
# (…/zip#coverage/lcov.info), else the archive's only file, else its only file that looks like a report. For a URL in coverageFile.paths, for baseline.report, and for a baseline that is a zip on disk.
Fixed
- A Cobertura baseline's files were not found under the report's own
<source>. The comparison said 0 file(s) moved beside a total that had moved. The root the report names is used now, as it is for a report that is loaded.
Coverage Studio for Visual Studio 1.2026.18 — New Code by Author and by Date
2026-10-03
Added
- New code by author and by date. New code was the lines changed against a git ref. Two settings ask what a ref cannot:
fuciniCoverage.gitDiff.author — the lines one person wrote — and fuciniCoverage.gitDiff.since — the lines written since a day. Both are read off git blame and apply to diff coverage and to the new-code gate.
Coverage Studio for VS Code 1.2026.19 — New Code by Author and by Date
2026-10-03
Added
- New code by author and by date. New code was the lines changed against a git ref. Two settings ask what a ref cannot:
fuciniCoverage.gitDiff.author — the lines one person wrote — and fuciniCoverage.gitDiff.since — the lines written since a day. Both are read off git blame and apply to diff coverage and to the new-code gate.
Coverage Studio for Visual Studio 1.2026.17 — One Test with Coverage, in Go and Rust
2026-10-02
Added
- Run This Test with Coverage reads Go and Rust. In a Go file the caret's
func TestXxx runs alone, in its own package and by its exact name — go test ./pkg/calc -run "^TestTotal$". In a Rust file the #[test] function runs by its path in the crate, --exact; in a mod, that module's tests; an integration test under tests/, in its own binary.
Coverage Studio for VS Code 1.2026.18 — One Test with Coverage, in Go and Rust
2026-10-02
Added
- Run This Test with Coverage reads Go and Rust. In a Go file the caret's
func TestXxx runs alone, in its own package and by its exact name — go test ./pkg/calc -run "^TestTotal$". In a Rust file the #[test] function runs by its path in the crate, --exact; in a mod, that module's tests; an integration test under tests/, in its own binary.
Coverage Studio for Visual Studio 1.2026.16 — GCC 14 Condition Coverage, Line by Line
2026-10-02
Added
- GCC 14's condition coverage, line by line.
-fcondition-coverage was read as two counts per file; each condition is now marked on its line and named in the hover — condition 1 never true, condition 0 never either way — from gcov's JSON and from its text listing. threshold.conditions gates it, Copy Coverage Summary has a Conditions column, badge.metric takes conditions, and the search takes conditions < 80.
Fixed
- A function's branch figure counted its conditions' outcomes too.
branches 8/12 where the tool said 4 of 6. Conditions are left out there, as they are in the file's and the run's figures.
Coverage Studio for VS Code 1.2026.17 — GCC 14 Condition Coverage, Line by Line
2026-10-02
Added
- GCC 14's condition coverage, line by line.
-fcondition-coverage was read as two counts per file; each condition is now marked on its line and named in the hover — condition 1 never true, condition 0 never either way — from gcov's JSON and from its text listing. threshold.conditions gates it, Copy Coverage Summary has a Conditions column, badge.metric takes conditions, and the search takes conditions < 80.
Fixed
- A function's branch figure counted its conditions' outcomes too.
branches 8/12 where the tool said 4 of 6. Conditions are left out there, as they are in the file's and the run's figures.
Coverage Studio for Visual Studio 1.2026.15 — The Coverage View Follows the Editor
2026-10-02
Added
- The Coverage view follows the editor. The file you are looking at is highlighted in the tree, the folders above it are opened and its row is scrolled into view.
fuciniCoverage.coverageView.followActiveEditor turns it off.
Coverage Studio for VS Code 1.2026.16 — The Coverage View Follows the Editor
2026-10-02
Added
- The Coverage view follows the editor. The file you are looking at is highlighted in the tree, the folders above it are opened and its row is scrolled into view.
fuciniCoverage.coverageView.followActiveEditor turns it off.
Coverage Studio for Visual Studio 1.2026.14 — Branch-Only Exclusion Markers, and Branch Coverage in .NET Reports
2026-10-02
Added
- Branch-only and gcovr exclusion markers.
LCOV_EXCL_BR_LINE and LCOV_EXCL_BR_START … LCOV_EXCL_BR_STOP take the branches on their lines out of the count and leave the lines in it; LCOV_EXCL_EXCEPTION_BR_LINE takes only the arms an exception reaches. Every marker is read under gcovr's prefixes as well: GCOVR_EXCL_LINE, GCOVR_EXCL_START, GCOV_EXCL_BR_LINE and the rest.
Fixed
- A .NET report showed no branch coverage. Coverlet and
dotnet-coverage write a Cobertura line's branch attribute as True, and only the lowercase true was read — so every decision in such a report was dropped. Both spellings are read now. Numbers move: a .NET project's branch coverage appears where there was none, and its score with it.
- A Cobertura report merged from several trees found only the first tree's files. Such a report names one
<source> per tree; each is tried now, in the report's order.
Coverage Studio for VS Code 1.2026.15 — Branch-Only Exclusion Markers, and Branch Coverage in .NET Reports
2026-10-02
Added
- Branch-only and gcovr exclusion markers.
LCOV_EXCL_BR_LINE and LCOV_EXCL_BR_START … LCOV_EXCL_BR_STOP take the branches on their lines out of the count and leave the lines in it; LCOV_EXCL_EXCEPTION_BR_LINE takes only the arms an exception reaches. Every marker is read under gcovr's prefixes as well: GCOVR_EXCL_LINE, GCOVR_EXCL_START, GCOV_EXCL_BR_LINE and the rest.
Fixed
- A .NET report showed no branch coverage. Coverlet and
dotnet-coverage write a Cobertura line's branch attribute as True, and only the lowercase true was read — so every decision in such a report was dropped. Both spellings are read now. Numbers move: a .NET project's branch coverage appears where there was none, and its score with it.
- A Cobertura report merged from several trees found only the first tree's files. Such a report names one
<source> per tree; each is tried now, in the report's order.
Coverage Studio for Visual Studio 1.2026.13 — The Fewest Tests That Still Run Every Line
2026-10-01
Added
- Show Minimal Test Set… finds the fewest tests that still run every line the suite runs, and lists under them the tests that add no line of their own. For the fast run before a commit, and for whoever prunes the suite — with the reminder that redundant by lines is not redundant by what a test checks.
Coverage Studio for VS Code 1.2026.14 — The Fewest Tests That Still Run Every Line
2026-10-01
Added
- Show Minimal Test Set… finds the fewest tests that still run every line the suite runs, and lists under them the tests that add no line of their own. For the fast run before a commit, and for whoever prunes the suite — with the reminder that redundant by lines is not redundant by what a test checks.
Coverage Studio for Visual Studio 1.2026.12 — Exclusions That Say Why
2026-10-01
Added
- Justified exclusions. Code taken out of the count where it is written can say why —
// LCOV_EXCL_LINE: defensive, unreachable by construction, [ExcludeFromCodeCoverage(Justification = "…")] — and the reasons are reported: Export Coverage Report… lists the exclusions and their justifications as Markdown and as CSV. With fuciniCoverage.exclusions.requireReason an exclusion that gives no reason is a warning at its line and fails Check Coverage Thresholds.
Coverage Studio for VS Code 1.2026.13 — Exclusions That Say Why
2026-10-01
Added
- Justified exclusions. Code taken out of the count where it is written can say why —
// LCOV_EXCL_LINE: defensive, unreachable by construction, [ExcludeFromCodeCoverage(Justification = "…")] — and the reasons are reported: Export Coverage Report… lists the exclusions and their justifications as Markdown and as CSV. With fuciniCoverage.exclusions.requireReason an exclusion that gives no reason is a warning at its line and fails Check Coverage Thresholds.
Coverage Studio for Visual Studio 1.2026.11 — A Gate on New Code, and the Coverage That Moved Without an Edit
2026-10-01
Added
- A gate on new code, beside the gates on the whole run.
fuciniCoverage.threshold.newCode is the least line coverage, in percent, of the lines changed against gitDiff.baseRef. Check Coverage Thresholds fails it in a sentence of its own — New code 72.00% < 80% (18 of 25 line(s) changed against origin/main ran) — and the whole run stays on screen.
- Show Indirect Coverage Changes… lists the lines whose coverage moved though nobody edited them — code that stopped running because a test went, or because a branch upstream now returns early. Against the CI baseline where
baseline.report names one, else against the run before.
Coverage Studio for VS Code 1.2026.12 — A Gate on New Code, and the Coverage That Moved Without an Edit
2026-10-01
Added
- A gate on new code, beside the gates on the whole run.
fuciniCoverage.threshold.newCode is the least line coverage, in percent, of the lines changed against gitDiff.baseRef. Check Coverage Thresholds fails it in a sentence of its own — New code 72.00% < 80% (18 of 25 line(s) changed against origin/main ran) — and the whole run stays on screen.
- Show Indirect Coverage Changes… lists the lines whose coverage moved though nobody edited them — code that stopped running because a test went, or because a branch upstream now returns early. Against the CI baseline where
baseline.report names one, else against the run before.
Coverage Studio for Visual Studio 1.2026.10 — Run Only the Tests Your Changes Touch
2026-10-01
Added
- Run Tests Affected by My Changes… lists the tests that ran a line the working tree changes against
fuciniCoverage.testImpact.baseRef (HEAD by default), the one that ran most of the change first. Picking a test opens it; where the reports came from a per-test run (testRun.perTest), the first entry runs only those tests.
Fixed
testRun.skipWhenTestsFail holds past the run. The reports a red run wrote stayed on disk, where the watcher loaded them a moment later. They are removed now.
- The copy a TRX logger makes of a coverage report is never taken for a tracefile.
Coverage Studio for VS Code 1.2026.11 — Run Only the Tests Your Changes Touch
2026-10-01
Added
- Run Tests Affected by My Changes… lists the tests that ran a line the working tree changes against
fuciniCoverage.testImpact.baseRef (HEAD by default), the one that ran most of the change first. Picking a test opens it; where the reports came from a per-test run (testRun.perTest), the first entry runs only those tests.
Fixed
testRun.skipWhenTestsFail holds past the run. The reports a red run wrote stayed on disk, where the watcher loaded them a moment later. They are removed now.
- The copy a TRX logger makes of a coverage report is never taken for a tracefile.
Coverage Studio for Visual Studio 1.2026.9 — Pass or Fail on the Marks, and Visual Studio's Own Code Coverage Files
2026-09-30
Added
- Pass or fail on the marks. A line that a failing test ran gets a red bar at its start, and its hover names the failing tests; Show Tests That Ran This Line says which of the tests failed. It needs a report that says which tests ran a line, and the runner's results file — TRX or JUnit XML — in the report's folder or named in
fuciniCoverage.testResults.paths. Run Tests with Coverage now asks dotnet test for one.
- Visual Studio's own code coverage, loaded. The
.coverage file that Visual Studio's Analyze Code Coverage writes is converted with dotnet-coverage and loaded by itself: Coverage Studio builds on the built-in collector instead of competing with it.
Coverage Studio for VS Code 1.2026.10 — Pass or Fail on the Marks
2026-09-30
Added
- Pass or fail on the marks. A line that a failing test ran gets a red bar at its start, and its hover names the failing tests; Show Tests That Ran This Line says which of the tests failed. It needs a report that says which tests ran a line, and the runner's results file — TRX or JUnit XML — in the report's folder or named in
fuciniCoverage.testResults.paths. Run Tests with Coverage now asks dotnet test for one.
Coverage Studio for Visual Studio 1.2026.8 — Reloads That Keep Your Search, Your Session and Your Last Run
2026-09-30
Fixed
- The search box keeps its query, and its focus, when coverage reloads. Every reload emptied it, often while the query was still being typed. A query such as
< 80% also keeps the risk hotspots it matches.
- A coverage session no longer counts the same run twice. Every reload of unchanged reports added them again, and every hit count doubled. A session now keeps each set of reports once.
- The CI baseline no longer holds up a load. The load is drawn first and the comparison follows. The baseline is prepared once and kept while its report and the settings stay the same, and when the URL cannot be reached the copy fetched before is used.
- “Since the last run” survives a reload of unchanged reports. A settings change or a refresh made the run on screen its own last run, and every change since the run before disappeared. Such a reread is no longer kept in the history either.
- One load at a time. Two reloads asked for together ran side by side, and the slower one won. The reloads asked for during a load now become one load after it.
- A CI baseline over HTTP is fetched as in VS Code. One client, the last copy kept for when the server cannot be reached, and
remote.headers sent only to the address they were written for — never over plain HTTP.
- A command whose work fails says so. A failure in a command or a timer was swallowed; it is now a line in the Output window.
- Less work on the UI thread. The file watchers are made in the background, the Error List entries are worked out once per load, and the log is written in bursts instead of line by line.
- Two Visual Studio windows no longer damage the run history. Its index is replaced in one step under a lock both share, and made again from the runs when it is lost.
- Run Native Project with Coverage builds the project's own platform, and takes
llvm-cov and llvm-profdata from the compiler's LLVM, as a pair.
- The Coverage window's warning, error and passed colours follow a light theme as well as a dark one.
- A settings file open with unsaved changes is not written over. The settings page says to save it first.
Changed
- A coverage file you pick, and the report a test run writes, are remembered for you instead of being written into
.vscode/settings.json. That file is often committed, so a test run left a change in the repository that nobody had asked for. Paths already in coverageFile.paths keep working.
Coverage Studio for VS Code 1.2026.9 — Reloads That Keep Your Search, Your Session and Your Last Run
2026-09-30
Fixed
- The search box keeps its query, and its focus, when coverage reloads. Every reload emptied it, often while the query was still being typed. A query such as
< 80% also keeps the risk hotspots it matches.
- A coverage session no longer counts the same run twice. Every reload of unchanged reports added them again, and every hit count doubled. A session now keeps each set of reports once.
- The CI baseline no longer holds up a load. The load is drawn first and the comparison follows. The baseline is prepared once and kept while its report and the settings stay the same, and when the URL cannot be reached the copy fetched before is used.
- The check for changed report files runs one pass at a time. On a slow network drive two passes saw the same change, and the coverage loaded twice.
- “Since the last run” survives a reload of unchanged reports. A settings change or a refresh made the run on screen its own last run, and every change since the run before disappeared. Such a reread is no longer kept in the history either.
Changed
- A coverage file you pick, and the report a test run writes, are remembered for you instead of being written into
.vscode/settings.json. That file is often committed, so a test run left a change in the repository that nobody had asked for. Paths already in coverageFile.paths keep working.
Coverage Studio for Visual Studio 1.2026.7 — Test Runs That Run Exactly What They Name
2026-09-30
Fixed
- A .NET solution with two or more test projects no longer loses all but one project's coverage.
dotnet test wrote one report per project and all of them were moved onto the same coverage/coverage.cobertura.xml, so only the last was loaded. Such a folder now runs dotnet test once per test project, each report goes into coverage/<Project>/, and a run of one project replaces that project's report only.
- Saving several files at once runs the tests of every file saved. With
testRun.onSave only the last file's tests ran; saves that land while a run is going are kept and run together after it.
- Run This Test with Coverage and the per-test runs run exactly what they name. The .NET filter matched by substring:
Total also ran TotalTax. A test now runs by its exact name, a class by its name followed by a dot, and a test in a nested class is found (Outer+Inner).
- A jest or vitest test whose title has
(, + or $ in it runs. The title went to -t, which reads a regular expression, so such a test matched nothing and nothing ran.
- Run This Test with Coverage reads VB and F# test files.
<Fact> above Public Sub, and [<Fact>] above let or member, were never recognised.
- Cancelling a test run ends every process it started. Only the shell was ended, and
dotnet test ran on behind it. Closing Visual Studio cancels the run as well.
- Typing during a run after a save no longer shifts the marks. They are pinned to the text the run measured.
- A solution on a network share runs its tests there. The run started in
C:\Windows, because the command shell does not take a \\server\share path as its folder.
Coverage Studio for VS Code 1.2026.8 — Test Runs That Run Exactly What They Name
2026-09-30
Fixed
- A .NET solution with two or more test projects no longer loses all but one project's coverage.
dotnet test wrote one report per project and all of them were moved onto the same coverage/coverage.cobertura.xml, so only the last was loaded. Such a folder now runs dotnet test once per test project, each report goes into coverage/<Project>/, and a run of one project replaces that project's report only.
- Saving several files at once runs the tests of every file saved. With
testRun.onSave only the last file's tests ran; saves that land while a run is going are kept and run together after it.
- Run This Test with Coverage and the per-test runs run exactly what they name. The .NET filter matched by substring:
Total also ran TotalTax. A test now runs by its exact name, a class by its name followed by a dot, and a test in a nested class is found (Outer+Inner).
- A jest or vitest test whose title has
(, + or $ in it runs. The title went to -t, which reads a regular expression, so such a test matched nothing and nothing ran.
- A test title with quotes in it reaches the terminal whole. A command with
\" inside quotes fell apart at the first one.
- Run This Test with Coverage reads VB and F# test files.
<Fact> above Public Sub, and [<Fact>] above let or member, were never recognised.
- A test task that ends without starting a process no longer leaves Run Tests with Coverage waiting for ever. It ends, and says the run wrote nothing.
Coverage Studio for Visual Studio 1.2026.6 — Which Tests Ran a Line, Coverage After Every Save, and a CI Baseline
2026-09-29
Added
- Which tests ran this line. Where the report records it — coverage.py with
--show-contexts, OpenCover's tracked methods, an lcov tracefile written per test — a function's CodeLens says how many tests ran it, and Show Tests That Ran This Line lists them, busiest first, and opens the one you pick. For a dotnet test run, testRun.perTest runs the tests one class at a time, so each report says which class wrote it.
- Coverage after every save. With
testRun.onSave, saving a source runs the tests that cover it, with coverage, and the gutter follows. In a .NET solution only the test projects that reference the saved file's project run; several saves in a row are one run.
- Run This Test with Coverage. With the caret in a test method, a test class or
describe, or a test file, one command runs just that with coverage — dotnet test --filter, jest/vitest -t or a pytest node id — and loads what it wrote. The test is read off the text, so it works for C#, VB, F#, JavaScript, TypeScript and Python without a language service.
- Exclude from Coverage, and two .NET filters. A right-click on a row of the coverage tree, or Exclude This File from Coverage on the open file, adds the file or folder to
fuciniCoverage.ignore, and it leaves every total. ignore.namespaces leaves out the files of a namespace (*.Migrations), and ignore.attributes the class, method or property under an attribute — ExcludeFromCodeCoverage by default, which OpenCover, dotCover and a converted lcov do not honour.
- Exclude Selection from Coverage. Select lines and take it from the light bulb or the editor's context menu: above a .NET class, method or property it writes
[ExcludeFromCodeCoverage] — the short form where the file already imports System.Diagnostics.CodeAnalysis — and elsewhere LCOV_EXCL_LINE or an LCOV_EXCL_START/STOP pair in the language's own comment. A marker already there is not doubled.
- Namespaces, classes and methods. For a .NET report that names its classes — Cobertura, Coverlet, OpenCover — a Folders / Namespaces dropdown draws the same run by namespace, class and method, with the same sort and filter; a method row opens the file at the method. Compiler-generated types such as
<>c and async state machines are folded into the class they were generated for.
- A search box over the coverage tree. Type words to keep the rows whose name or path has them all, or a comparison —
< 80%, lines >= 90, branches = 0 — to keep the rows whose figure passes; both together want both. Method rows and risk hotspots are searched too; Escape clears.
- Run Native Project with Coverage. The startup C++ project, from the Tools menu: a ClangCL project is built again with clang's coverage instrumentation into
coverage/native/, run, and its report loaded; any other toolset runs under OpenCppCoverage where it is installed, and where neither is possible a sentence says what to install.
- Coverage sessions and a trend. Start Coverage Session, and every run loaded from then on is added to the ones before it — lines, branches and functions alike: whatever any of them ran is covered — so unit tests, integration tests and a walk through the application show what they reached together. Under the toolbar, the saved history's line coverage is drawn as a trend, one dot per run.
- Compare Two History Runs, as in VS Code: pick two kept runs and the files that moved between them are listed, oldest to newest whichever was picked first.
- Compare with CI Baseline.
baseline.report names the report your main branch's build publishes — a URL, fetched with remote.headers, or a downloaded file — and every load is measured against it: the status bar's card says where this run stands, and Compare with CI Baseline lists the files that moved. The baseline goes through the same ignore list as the run, so the totals compare like with like.
- Where to find them. The new commands are under Tools > Coverage Studio and in the editor's context menu; Show Tests That Ran This Line is also Ctrl+Shift+U, T, and Exclude Selection from Coverage is a light-bulb action.
Fixed
- A test run's report no longer carries your user and machine name. Microsoft's Code Coverage collector names its report
<user>_<machine>_<stamp>.cobertura.xml, and that name stood in the tree and in every screenshot of it; the report is renamed to coverage.cobertura.xml before it is loaded.
- A generated file was said to be left out by
fuciniCoverage.ignore. Each file left out is now counted under the setting that left it out — ignore, ignore.generated or ignore.namespaces — so the name shown is the one to change.
- A per-test lcov tracefile is read as one record per file, its test sections added up, as VS Code reads it; before, every test's record was a file of its own.
- The log says what a parser left out. The files a parser found nothing to instrument in (
FUCINI_PARSE_030) and a report's own account of its gaps (FUCINI_PARSE_031, OpenCover's MissingPdb) are written to the log, as VS Code writes them.
- A header of macros no longer sits in the tree at 100%. llvm-cov and gcov charge a macro's uses to the lines that define it; the branches and functions on excluded lines now go with the lines, and a file left with nothing measurable is taken out of the run and named in the output (
FUCINI_EXCLUDE_001).
- Five reports refused as an unsupported format are recognised. Kover's JaCoCo XML, a Stryker.NET report, an NCover report, a legacy SimpleCov resultset and a SonarQube report of many empty files are now known by what they carry at the top. A Clover or SonarQube report whose paths contain the word
cobertura is no longer read as Cobertura.
- Counts that agree with the tools that wrote them. An MC/DC build's branch figure matches
llvm-cov report; LCOV 2 exception branches (BRDA:12,e0,1,3) count; an llvm-cov function is declared only in the file that defines it; a Go block listed by two test binaries adds its counts; a Cobertura decision reported by two classes and a JaCoCo line listed twice keep all their arms, once.
- Smaller ones. A tracefile cut off mid-write no longer drops a file;
gcov -t over several .gcda files gives one file each, template instantiations are read once and counts such as 1.20k from gcov -H are kept; GNATcoverage files are no longer all reported as changed since their coverage; a pending Stryker mutant or an interrupted PIT run no longer counts against the score.
- A hand edit of the settings file is applied when it is saved, as VS Code applies a settings change.
Coverage Studio for VS Code 1.2026.7 — Which Tests Ran a Line, Coverage After Every Save, and a CI Baseline
2026-09-29
Added
- Which tests ran this line. Where the report records it — coverage.py with
--show-contexts, OpenCover's tracked methods, an lcov tracefile written per test — a function's CodeLens says how many tests ran it, and Show Tests That Ran This Line lists them, busiest first, and opens the one you pick. For a dotnet test run, testRun.perTest runs the tests one class at a time, so each report says which class wrote it.
- Coverage after every save. With
testRun.onSave, saving a source runs the tests that cover it, with coverage, and the gutter follows. In a .NET solution only the test projects that reference the saved file's project run; several saves in a row are one run.
- Run This Test with Coverage. With the caret in a test method, a test class or
describe, or a test file, one command runs just that with coverage — dotnet test --filter, jest/vitest -t or a pytest node id — and loads what it wrote. The test is read off the text, so it works for C#, VB, F#, JavaScript, TypeScript and Python without a language service.
- Exclude from Coverage, and two .NET filters. A right-click on a row of the coverage tree, or Exclude This File from Coverage on the open file, adds the file or folder to
fuciniCoverage.ignore, and it leaves every total. ignore.namespaces leaves out the files of a namespace (*.Migrations), and ignore.attributes the class, method or property under an attribute — ExcludeFromCodeCoverage by default, which OpenCover, dotCover and a converted lcov do not honour.
- Exclude Selection from Coverage. Select lines and take it from the light bulb or the editor's context menu: above a .NET class, method or property it writes
[ExcludeFromCodeCoverage] — the short form where the file already imports System.Diagnostics.CodeAnalysis — and elsewhere LCOV_EXCL_LINE or an LCOV_EXCL_START/STOP pair in the language's own comment. A marker already there is not doubled.
- Namespaces, classes and methods. For a .NET report that names its classes — Cobertura, Coverlet, OpenCover — a Folders / Namespaces dropdown draws the same run by namespace, class and method, with the same sort and filter; a method row opens the file at the method. Compiler-generated types such as
<>c and async state machines are folded into the class they were generated for.
- A search box over the coverage tree. Type words to keep the rows whose name or path has them all, or a comparison —
< 80%, lines >= 90, branches = 0 — to keep the rows whose figure passes; both together want both. Method rows and risk hotspots are searched too; Escape clears.
- Coverage sessions and a trend. Start Coverage Session, and every run loaded from then on is added to the ones before it — lines, branches and functions alike: whatever any of them ran is covered — so unit tests, integration tests and a walk through the application show what they reached together. Under the toolbar, the saved history's line coverage is drawn as a trend, one dot per run.
- Compare with CI Baseline.
baseline.report names the report your main branch's build publishes — a URL, fetched with remote.headers, or a downloaded file — and every load is measured against it: the status bar's card says where this run stands, and Compare with CI Baseline lists the files that moved. The baseline goes through the same ignore list as the run, so the totals compare like with like.
Fixed
- A test run's report no longer carries your user and machine name. Microsoft's Code Coverage collector names its report
<user>_<machine>_<stamp>.cobertura.xml, and that name stood in the tree and in every screenshot of it; the report is renamed to coverage.cobertura.xml before it is loaded.
- A test run failed on Windows when the terminal is PowerShell. The quotes around
Code Coverage;Format=cobertura were lost on the way to pwsh, which read the ; as the end of the command; the recipe is now handed to the terminal word by word and quoted for whichever shell it starts.
- A generated file was said to be left out by
fuciniCoverage.ignore. Each file left out is now counted under the setting that left it out — ignore, ignore.generated or ignore.namespaces — so the name shown is the one to change.
- A header of macros no longer sits in the tree at 100%. llvm-cov and gcov charge a macro's uses to the lines that define it; the branches and functions on excluded lines now go with the lines, and a file left with nothing measurable is taken out of the run and named in the output (
FUCINI_EXCLUDE_001).
- Five reports refused as an unsupported format are recognised. Kover's JaCoCo XML, a Stryker.NET report, an NCover report, a legacy SimpleCov resultset and a SonarQube report of many empty files are now known by what they carry at the top. A Clover or SonarQube report whose paths contain the word
cobertura is no longer read as Cobertura.
- Counts that agree with the tools that wrote them. An MC/DC build's branch figure matches
llvm-cov report; LCOV 2 exception branches (BRDA:12,e0,1,3) count; an llvm-cov function is declared only in the file that defines it; a Go block listed by two test binaries adds its counts; a Cobertura decision reported by two classes and a JaCoCo line listed twice keep all their arms, once.
- Smaller ones. A tracefile cut off mid-write no longer drops a file;
gcov -t over several .gcda files gives one file each, template instantiations are read once and counts such as 1.20k from gcov -H are kept; GNATcoverage files are no longer all reported as changed since their coverage; a pending Stryker mutant or an interrupted PIT run no longer counts against the score.
Changed
- No more
FUCINI_ENCODING_001 on every load. A report without a byte-order mark is read as UTF-8, as every tool writes it, without a note saying so.
Coverage Studio for Visual Studio 1.2026.5 — Watched Where the Report Will Appear
2026-09-28
Added
- A relative entry with a wildcard is searched at any depth.
*.lcov.info in coverageFile.paths finds the file wherever it is under the base folder, as VS Code has always read it; a rooted entry — ${workspaceFolder}/build/coverage-merged/*.lcov.info, ** for any depth — looks exactly where it says, as before.
- Watched where the report will appear. The watch covers the folders the settings name, whether or not a report is there yet: a rooted file's folder, the folders before a wildcard — with everything below them when the pattern reaches into subfolders — and the base folder for a relative wildcard. A report a build writes there loads by itself; a write to any other file in those folders is ignored.
Fixed
- An llvm-cov JSON report was refused as an unsupported format. The format is recognised from the first 4096 characters, and llvm-cov writes its
"type": "llvm.coverage.json.export" marker at the very end; an export whose first file record carries many branches and nested macro expansions had nothing else recognisable in that head. Such a report is now known by its opening, {"data":[{"files":[, which no other format has.
- The same for
gcov --json-format. gcov writes a file's functions before its lines, and a file of a dozen functions has no line entry in the inspected head either; the document's own gcc_version key now identifies it.
Coverage Studio for VS Code 1.2026.6 — A Wildcard in a Full Path, Watched Where the Report Will Appear
2026-09-28
Added
- A wildcard in an absolute
coverageFile.paths entry. ${workspaceFolder}/build/coverage-merged/*.lcov.info loads every such file in that folder, and ** stands for any depth: the folders before the first wildcard are searched, the rest is the pattern under them, as the Visual Studio edition has always read such an entry. Before, an absolute entry was looked for as written, so one with a star matched nothing — and, being a wildcard, was never reported as matching nothing either. A report that appears there later is loaded by the watcher or the poll like a literal entry; a wildcard that matches nothing is not reported, since it may be waiting for a build.
Fixed
- An llvm-cov JSON report was refused as an unsupported format. The format is recognised from the first 4096 characters, and llvm-cov writes its
"type": "llvm.coverage.json.export" marker at the very end; an export whose first file record carries many branches and nested macro expansions had nothing else recognisable in that head. Such a report is now known by its opening, {"data":[{"files":[, which no other format has.
- The same for
gcov --json-format. gcov writes a file's functions before its lines, and a file of a dozen functions has no line entry in the inspected head either; the document's own gcc_version key now identifies it.
Coverage Studio for Visual Studio 1.2026.4 — Two Reports, One Answer, and Green Where It Is Covered
2026-09-26
Added
- Choose or combine the reports of a file. Right-click a file in the Coverage window and pick Choose Coverage Source... — offered where more than one loaded report measures it — or use Tools > Coverage Studio > Choose Coverage Source for This File...: each report with its own figures, then Combine them — agree (covered only where every report ran a line: one code base, two compilers) and — union (covered where any did: several test suites of one build), for the file, its folder or a pattern. The lines the reports disagree on are teal in the margin and the Annotated Source, with what each report said; the row names the reports and the number of disputed lines, and the status bar and both cards say where the numbers come from.
- Red, amber and green. Gutter: Coverage paints every covered line green beside the red and amber — the mode for anybody who expected a file at 100% to be green. The heatmap's five shades are settings (
heatmap.colors, heatmap.colorsDark), and the settings window previews every colour while it is picked.
Fixed
- The file's number in the status bar is its card's. A metric with nothing in it — MC/DC on a file with no decisions — counted as 100% in the item and not at all on the card it opens.
- A header of constants no longer reads 0% or 100%. Preprocessor directives count as no code, like comments, when
excludeComments is on.
Coverage Studio for VS Code 1.2026.5 — Two Reports, One Answer, and Green Where It Is Covered
2026-09-26
Added
- Choose or combine the reports of a file.
coverageFile.sources decides, per file, which of several loaded reports speaks for it — or combines them: agree counts a line covered only where every report ran it, and marks the lines they disagree on in teal; union where any did. Choose Coverage Source... on a row of the Coverage view or for the open file writes the rule for you. Part of the paid plans; on Community the newest report decides.
- Red, amber and green, and a one-click gutter mode. The
coverage mode paints covered lines green; the heatmap's shades are settings with a live preview in the settings panel; a status-bar button and Alt+G move the gutter through off, uncovered, coverage and heatmap.
Fixed
- The file's number in the status bar is its card's. A metric with nothing in it — MC/DC on a file with no decisions — counted as 100% in the item and not at all on the card it opens.
- A header of constants no longer reads 0% or 100%. Preprocessor directives count as no code, like comments, when
excludeComments is on.
Coverage Studio for Visual Studio 1.2026.3 — Nothing to Choose but the Code
2026-09-24
Changed
- The Licence dialog asks only for the code. The four plan buttons are gone: a subscription or invitation code names a pool, and the pool knows its plan, so there was nothing a person could answer better than the code itself — and a Business invitation pasted with Pro still selected was refused for the wrong plan. Paste the code and press Activate.
Coverage Studio for VS Code 1.2026.4 — Nothing to Choose but the Code
2026-09-24
Changed
- Enter Subscription Code asks only for the code. The plan question is gone: the code names a pool and the pool knows its plan, so the server takes it from there and no longer refuses a code as being for the wrong plan. The one-click links in the emails are unchanged; the activation emails no longer have a "choose your plan" step, and the same code activates Coverage Studio for Visual Studio with no VS Code needed.
Coverage Studio for Visual Studio 1.2026.2 — Every Test Run, Every Report, and What Moved
2026-09-24
Added
- Coverage after every test run. Collect Coverage in Test Explorer… puts one
.runsettings with Microsoft's Cobertura collector beside the solution — picked up by itself with Tools → Options → Test → Auto detect runsettings files on, or selected once under Test → Configure Run Settings; from then on every Test Explorer run writes a report under the test project, and it is loaded the moment it appears, merged with whatever the settings name. Nothing hooks Test Explorer, so Visual Studio 2026 and Microsoft.Testing.Platform work as they are. Run Tests with Coverage recognises dotnet test, vitest, jest, pytest, go, cargo, maven, gradle, phpunit and ctest, runs them with coverage on in the Output window and loads what they wrote; a test project without coverlet.collector is run with the collector the test SDK ships.
- Since the last run. Every load is compared with the one before: a triangle in the margin on each line that started or stopped running, the trend beside the score, and Compare with Last Run for the files whose numbers moved.
history.enabled keeps the runs across restarts; gitDiff.enabled cuts the run down to the lines your branch changed.
- The gates and the Error List. Check Coverage Thresholds judges the run against
threshold.*; with compliance.diagnostics, problems.enabled, problems.perLine or deadCode.enabled the findings sit in the Error List beside the compiler's own, each opening its file.
- Reports, exports and a badge. Show Annotated Source opens the active file as a page in the document well — every line in its colour, the findings beside it — scrolled with the editor both ways. Open HTML Report and Generate Full HTML Report write the run to
htmlReport.outputDir and open the browser. Export Coverage Report… writes CSV per file or per function, Markdown, LCOV, Cobertura, SARIF, an SVG badge or Fucini JSON. Generate Coverage Badge keeps the badge in the repository, after every load with badge.autoUpdate.
- Risk hotspots and row filters in the Coverage window, as in VS Code: the functions where complex code meets little coverage, worst first; and a dropdown beside the sort for all files, files with gaps, untested files, or the files changed since the last run.
- A settings file at the solution root.
fucini-coverage.json beside the solution is read after .vscode/settings.json and written by the settings window when it is the file that exists — for a team that would rather not keep a .vscode folder. Both editions read it.
- .NET without ceremony.
ignore.generated leaves designer, generated, obj\ and migration files out of every total; testRun.skipWhenTestsFail keeps the last good coverage on screen when a run ends red.
Not yet in this edition
- The Test Coverage panel is a VS Code API, and Solution Explorer cannot show badges. Compare Two History Runs follows when subscribers ask for it.
Coverage Studio for VS Code 1.2026.3 — Hotspots, Filters, and .NET Without a Package
2026-09-24
Added
- Which files to show. A second dropdown in the Coverage view's toolbar, beside the sort: all files, files with gaps, untested files, or the files whose numbers moved since the previous run. Folders follow their files and keep their own numbers. In every tier.
- Risk hotspots. Under the tree: the functions where complex code meets little coverage, worst first, each opening its file at the function. A function qualifies by CRAP score —
hotspots.crapThreshold, 30 by default, the figure the metric's authors proposed — or by cyclomatic complexity alone, hotspots.complexityThreshold, off by default. Community sees the count and the worst score; the ranked list is Pro.
dotnet test without a package. Run Tests with Coverage reads the project files first: where none references Coverlet, the run uses Microsoft's own collector — --collect:"Code Coverage;Format=cobertura" — which ships with Microsoft.NET.Test.Sdk, and its report is loaded like Coverlet's.
- Generated .NET files are left out.
ignore.generated, on by default, drops *.Designer.cs, *.g.cs, *.generated.cs, *.razor.g.cs, AssemblyInfo, global usings, everything under Migrations/ and under obj/ from every total, page and export. A designer file nobody tests no longer costs a solution its gate.
- A failed run keeps the last good coverage. With
testRun.skipWhenTestsFail, a run that ends with a failing test leaves the coverage on screen as it was, and says so in the Output pane. Off by default.
Changed
- A Test Explorer run is found by itself. With nothing configured, the well-known names now include every test project's
TestResults/<run>/ folder, not only the workspace folder's own, and Microsoft's collector's <user>_<machine>_<date>.cobertura.xml beside Coverlet's. A .NET solution whose tests were run in Visual Studio shows its coverage on first open; every run folder loads, and the merge keeps the newest run's word on each file.
Coverage Studio for Visual Studio 1.2026.1 — The Same Coverage, in the Other IDE
2026-09-23
Added
- A second extension, for Visual Studio 2022 and 2026 — Community, Professional and Enterprise. The same reader as the VS Code edition, so the same report gives the same numbers, to the digit, in both editors; and the same
fuciniCoverage.* settings, read from .vscode/settings.json as well as from VS Code's own user settings when it is on the machine. It does not use Visual Studio's own code coverage: in Visual Studio 2022 that is an Enterprise feature, and on Community and Professional this is the only coverage the IDE will show.
- In the editor: a margin bar per line — red never ran, amber partly, a heatmap by visit count — inline hit counts, a line above each function with its coverage, complexity, CRAP score and MC/DC, stripes on the scroll bar, a hover that says which branch was not taken, and a dot on a line you have edited since the report.
- The Coverage window (View → Other Windows → Coverage): every file with its metrics and one score over all of them, sortable, with the unmeasured files listed beneath; the status bar's
Coverage and File items, each opening the full card.
- Commands under Tools → Coverage Studio, with two-stroke chords after
Ctrl+Shift+U: N/P for the next and previous uncovered line, B for the next partly-tested decision, R to reload. Watch mode reloads when the file your build writes changes.
- Getting Started, the six steps with a button for each, in 18 languages; it opens by itself the first time.
- Settings from the gear in the Coverage window's toolbar, writing this edition's user settings or the folder's
.vscode/settings.json and keeping the comments of a file it edits.
- Licence: included with every paid plan. A machine where Coverage Studio for VS Code is activated needs nothing more — the key is found by itself; anywhere else, Tools → Coverage Studio → Licence… takes the subscription code from your activation e-mail. Without a subscription no coverage is read: there is no free tier in Visual Studio.
Not yet in this edition
- Run Tests with Coverage and the Test Coverage panel are VS Code APIs; Solution Explorer cannot show badges. The annotated source view, the HTML report and the exports, Check Thresholds, the Error List entries, history and trend, and diff coverage follow in the order subscribers ask for them.
Coverage Studio for VS Code 1.2026.2 — Get Started, and Two Editors on One Subscription
2026-09-23
Added
- A walkthrough. Get started with Coverage Studio — under Help → Welcome → Walkthroughs, and opened by itself after the install — takes a new user from a coverage report to the lines nobody tests in six steps, each with a button for the command it is about. In all eighteen languages.
- The install id is left where Coverage Studio for Visual Studio reads it. The Visual Studio edition verifies the key in
license.key against the same fingerprint and cannot read this extension's state store; on every activation the id is now also written beside the extension's global storage, so a machine activated in VS Code needs no second activation in Visual Studio. A random UUID, tied to nothing, never sent anywhere.
Changed
- A refund ends the subscription. An approved refund or chargeback now cancels the subscription at once and its activation code issues no further keys; refunds are one per customer. Keys already on a machine run to their own expiry, as before. The refund policy says exactly what happens.
Coverage Studio for VS Code 1.2026.1 — File Names You Can Always Read
2026-09-21
Fixed
- File names in the Coverage view are readable at any width. The name was the one part of a row that shrank — to seven characters in an ordinary sidebar. It is now drawn in full, and the metric columns, the score and the bar slide under it from the left, fading out at the cut so that half a number never reads as a whole one. Every figure is still in the row's tooltip.
- The "file has changed" mark keeps up with the file. After a load it followed the first edit and the first restore, and then nothing moved it until the next load. Every save and every change on disk now lands.
- The Coverage view's toolbar no longer breaks its button labels. In a narrow sidebar the toolbar now wraps between its controls, and the sort box moves to a line of its own.
- The coverage badge is free in the export picker too. Generate Coverage Badge always wrote the SVG badge on every tier, while Export Coverage Report… refused the same file on Community. Both now write it.
- Show Worst-Covered Folders is licensed like the other two lists. Nothing changes on Community, where all three are free and uncounted, nor for any key already issued.
Added
- A minimum supported version. The extension now reads a small signed policy from our licence server about once a week. When a build is older than the oldest version still supported, the trial and the free tier stop and ask for an update; a paid licence is told the date and keeps working for a grace period first. The read sends nothing about you or your install, and an install that cannot reach the server is never stopped for that reason. No minimum is set today.
Changed
- The licence is proprietary from this release on. The extension was published under MIT terms, which never matched a product sold by subscription. You may install and use the published build within your plan, commercially too; you may not copy, modify, redistribute or reverse engineer it. What it produces from your data — reports, exports, badges — is yours, and the old attribution condition is gone. A copy of an earlier release stays under the terms it came with.
- The path-actions button (⋯) takes no room until you point at a row. The 25 pixels it kept reserved on every row now go to the file name; it appears right after the name on hover, and nothing in the row moves.
Coverage Studio for VS Code 1.2026.0 — Conditions, Mutants, and the Tests Behind a Line
2026-09-20
Fixed
- LCOV 2 tracefiles list each function once.
FN:<line>,<end line>,<name> is read as lcov 2.0 writes it — before, every function appeared twice, once as never called and once at line 0 — and lcov 2.2's FNL:/FNA: pairs are read with it.
- A C++ template is not painted as never executed because one instantiation never was, and a function full of macros is one function again rather than one per macro.
- The consent texts say what the install actually sends: no monthly page of your source, and your usage summary carries the request code your key was issued against.
Added
- Condition coverage is a metric of its own.
a && b is one decision and two conditions, and a suite can take both branches without ever seeing b false. It now has its own row, column, export, badge, weight in the score (3, between branches and MC/DC) and gate — read from BullseyeCoverage, a TRACE32 cond export and GCC 14's -fcondition-coverage.
- Mutants, where they are. PIT and Stryker reports are read mutant by mutant: the surviving ones are marked on their own line in the editor, with the change and the tests that ran it on hover, and listed in the HTML report and the annotated source.
- Which tests ran this line. coverage.py written with
--show-contexts and OpenCover collected with -coverbytest both say which tests reached a line; the hit-count hover names them.
- The part of an executed line that never ran is tinted in the editor, and C and C++ functions get a complexity and a CRAP score counted from the branch data llvm-cov already carries.
- JaCoCo, Cobertura and Coverlet projects load on first open, with nothing configured — including where Maven, Gradle and
dotnet test write by default.
- Every metric beside every file, at any width, ordered by the weight it carries in your score, with what does not fit scrolling rather than vanishing — and a count on the Coverage icon when a tracefile reloads while you are looking at something else.
- A pre-release channel you can volunteer for, and one button in the editor title bar that turns the coverage painting off and brings back exactly what it had.
Changed
- Bullseye and TRACE32: branch coverage no longer includes conditions. The decisions are branch coverage and the conditions are condition coverage, so the branch figure of such a project changes and its composite score with it. Set
whmWeights.conditions to 0 for the score as it was.
- The free Community tier has a weekly allowance: five each per week of the metered actions, where there were twenty a day, and at a limit you are offered a subscription or a 7-day pass. An install already in use keeps its old caps for 30 days and is told once, with the date.
- The trial and the free tier keep a usage ledger with the licence server: a hash of VS Code's machine id, the edition, the day the evaluation began and the counts of the metered actions — nothing else, never on a paid licence, and never waited for. The telemetry trade is now a one-time 30 days.
- The monthly page of source code is gone — no longer collected, held or sent for anybody, and one kept by an earlier build is deleted the first time this one starts.
0.2026.82 — Thresholds That Fit Your Project, and Everything Your Reports Carry
2026-09-17
Fixed
- The heatmap reads the right way round on a dark theme. Its ramp ran from pale yellow to dark brown, so the hottest loop had the dimmest bar. A dark theme now has a ramp of its own, dim olive to vivid orange.
- The MC/DC hover shows its detail again, and an llvm-cov decision counts its conditions correctly. Current VS Code folded the hover into one line; and
(a && b) || c was listed with one condition at 100% under a heading that rightly said 2 of 3 pairs.
- C++ functions are named the way you wrote them:
clamp_to<int> and gearbox::next_gear instead of the linker’s ??$clamp_to@H@@YAHHHH@Z, for llvm-cov, lcov and gcov.
- Istanbul line counts match nyc,
dotnet-coverage XML is read, SimpleCov :nocov: lines are excluded rather than broken, and a report that only carries a summary keeps its figures whichever order the reports are read in.
- Multi-root workspaces: the HTML report no longer names a folder twice, the Coverage view heads a workspace folder by its name, and a
pathRemap rule may say ${workspaceFolder} as well as ${workspaceRoot}.
Added
- Everything a report carries is shown. Function calls, object code and a mutation score get a column in the Coverage view, the HTML report and the CSV and Markdown exports, a row in the status-bar cards and a badge in the annotated source — and a metric a report does not carry is left out rather than printed as zero.
- Complexity and CRAP. Where a report carries cyclomatic complexity (OpenCover, JaCoCo, Clover, Cobertura), every function gets a CRAP score on its CodeLens, a file gets a Max CRAP that links to the method, and the HTML report lists the Riskiest Methods.
- More from every format: named branch arms, excluded lines, sub-line column spans, the tool, version and date of the report, and the warnings a tool wrote into it.
- Optional weights for object code and the mutation score in the composite score, both
0 unless you set them.
Changed
- The default thresholds are no longer 100%. A threshold you have not set now stands for lines 80, functions 80, regions 75, branches 70 and composite score 75, with no gate on MC/DC, instantiations, function calls or object code — what an ordinary project can be held to.
fuciniCoverage.threshold.profile: "safety" puts every gate back at 100, exactly as before, and a threshold you did set wins over either profile. If you relied on the old defaults you are judged more leniently until you select safety; an install that ran before is told so once, with a button that sets it.
- A metric without a gate shows a neutral dash in the cards instead of a tick or a cross, and the settings panel previews the verdict under the gates actually in force.
0.2026.81 — Exact Percentages, and a Report You Can Send
2026-09-16
Fixed
- Coverage in a buffer with unsaved changes lines up. A run loaded while a file had unsaved edits put every mark, hover and CodeLens as many lines off as the edits had pushed it. The edits are now recovered from the file on disk, and a whole line pasted in above a line no longer counts that line as edited.
- Merges and repeated LCOV records add up. A newer tracefile no longer keeps the older run’s branch and MC/DC summaries, and a file listed twice in one tracefile is combined as genhtml combines it.
- The licence key stays on its own machine. Settings Sync no longer carries it to other installs, where it blocked coverage; an expired Business, Enterprise or Pro Plus key renews itself for up to 45 days after expiry.
- Remote tracefiles are fetched more carefully.
remote.headers stay on their origin across redirects, a fetch gives up after 30 seconds, a sign-in page is refused rather than cached, and a window opened offline loads the last downloaded copy. Self-update installs only its own edition.
- Parsers: llvm-cov line counts match
llvm-cov show, OpenCover decisions from different methods no longer overwrite each other, xccov, TRACE32 and EMMA keep their line figures, Cobertura classes sharing a file are one file, JaCoCo aggregate reports parse, and path mapping resolves .. and matches whole segments.
- In the editor: history is kept per workspace, marks stay on real files rather than the HEAD pane of a diff editor, lines beyond the end of a file are not painted, Explorer badges on Windows work for
C:\… paths, and large runs are faster to look up.
Added
- Send diagnostic report. The notifications for a failed load, remote fetch, merge, history comparison or chat offer it. It previews exactly the JSON that would be sent, with an optional 4 KB file sample, comment and reply address, and sends nothing until you click Send.
- Error reports. A
FUCINI_* error is reported without file content, paths or names when VS Code’s telemetry level is all or error. Three log lines have codes of their own: FUCINI_REMOTE_002, FUCINI_REMOTE_003 and FUCINI_GITDIFF_002, each described in the user guide.
Changed
- Every coverage percentage has two decimals, truncated rather than rounded:
87.43%, and 100.00% only when coverage is exactly 100.
- Gates decide on the exact value. Until now every percentage was rounded to a whole number first, so 89.6% passed a 90% gate. A project that passed only by rounding will now show NON-COMPLIANT until its coverage really reaches the threshold.
- Telemetry is daily. Every install sends a small daily licence message at VS Code’s telemetry level
all; with telemetry.enabled on, the usage summary goes out daily instead of monthly. Installs that unlocked features for telemetry are asked before their terms change.
0.2026.80 — An Update You Can See
2026-09-14
Added
- The Coverage view says when an update is out, and keeps saying it. A bar above the licence banner names the new version beside the one you have, with the same three buttons as the update notification: Download & Install, View Changelog and Skip This Version. The view’s icon in the activity bar carries a badge as well, so it is seen with the view closed. The notification appeared only once, on the automatic check, and a click anywhere else dismissed it until the next check a day later; the bar stays until the update is installed, skipped, or
fuciniCoverage.update.checkForUpdates is turned off, and it survives a window reload in between.
0.2026.79 — One Ping a Day
2026-09-14
Changed
- The small pings go out at most once a day, together. An update, a licence change, a new coverage format and a file that would not parse used to be a ping each, sent the moment it was noticed — and on some installs noticed again every session: a revoked seat reported its revocation on every start, and switching between workspaces that use different formats reported a “new” set each time. They are now collected and sent as one ping, at most once a day. A day with nothing to report sends nothing.
- Only what actually changed is reported. A licence that moves and moves back within the day says nothing, and the formats are every format the install has loaded rather than the last workspace’s. The install ping and the removal notice still go out immediately, and nothing new is collected.
- VS Code’s own telemetry switch now covers the small pings too. With
telemetry.telemetryLevel set to off, nothing is collected for the daily ping and nothing is sent; until now only the monthly summary and the removal notice honoured it. The one-time install ping is unchanged.
- A month that ends while the editor sits idle is still reported. The monthly summary was only checked after loading coverage, so an install that stopped being used never sent its last month. It is now also checked every hour while VS Code is open — still only with telemetry switched on, and still at most once every 30 days.
Fixed
- A ping the server refused no longer counts as sent. Any answer from the server, an error included, was taken as delivery, so a refused daily ping lost what it had collected and a refused install ping was not tried again. Only a successful answer counts now; a refused daily ping or monthly summary is kept and tried again a day later.
0.2026.78 — A Fresh Fourteen Days
2026-09-13
Changed
- Every evaluation gets a fresh 14 days, once. The first time this release starts, the trial re-opens on every Pro install that has no paid licence — whether it ran out long ago or still had a few days left — and runs for 14 days from that day. A short notice says so when it happens. A paid licence is unaffected, and the Community edition, which has no trial, is unchanged.
- A paid licence says “Activated”, in green. The licence banner used to show a subscriber “Activated — 30 days left”. A subscription key runs to the end of the paid period and is renewed with it, so that countdown would never stop reading like a trial about to run out. The headline now just says Activated, the date sits in the details as “Valid until”, and the countdown returns only in the last week before a key expires, beside the renew-soon note that was already there.
0.2026.77 — Paste Your Code, That’s All
2026-09-12
Added
- “Enter Subscription Code” — and nothing else is asked. Choose your plan, paste the code from your purchase email, confirm, and your licence key is issued and applied for you. Until now the only route from inside the editor was the evaluation questionnaire — name, email, account type, company, a survey — which is the wrong form to put in front of somebody who has already bought. The other route was the
vscode:// link in the email, and being wary of clicking that is sensible rather than timid: a link like it can be triggered by any page you visit, which is precisely why activation asks you to confirm. It is in the Command Palette, and it is a button on the coverage panel’s licence banner and in the activation dialog — in every state, because a licence moves between machines and buying part-way through a trial is the ordinary case, not the exception.
- A way to see the plans from the trial itself. The pricing page was reachable from the free-tier screens but not from the activation dialog or the licence banner — the one place someone watching “3 days left” actually looks. That reader was offered a questionnaire and a
settings.json hint, and no price at all.
Changed
- The activation dialog and the licence banner no longer open with
FCK1. Both used to lead with your request code and “paste the key into settings.json”, which is the path for when our licence server cannot be reached at all. Shown first, it read as the instruction — so a customer holding an FCB1… subscription code was sent to “Enter Activation Key”, the one field certain to refuse it. Both are still there, below the routes that fit somebody who has bought, and described as what they actually are.
0.2026.76 — The Command the Email Named
2026-09-12
Fixed
- “Request Activation Key” is a command now. Your purchase email — and the activation page, in every language — tells you that if the one-click link does not survive your mail app, you can run Fucini Coverage: Request Activation Key from the Command Palette, choose your plan and paste your code. That name belonged to the panel’s title bar and to the link itself, never to a command, so the palette had nothing under it — and the nearest match by name, Enter Activation Key, takes the
FCK1… key our server issues rather than the FCB1… code your email carries. Following the instructions put you in the one field certain to refuse what you were holding. It is a real command now.
- The activation dialog offers Request License… while a trial or key is still live. It used to be withheld until one had expired, on the reasoning that a working install has no reason to contact the licence server. Buying part-way through a trial is the ordinary case rather than the exception, and a buyer in that position had no route from the dialog to the field their code goes in: the two buttons left asked for a key that only redeeming the code would produce.
0.2026.75 — A Colour-Blind Palette
2026-09-12
Added
- A colour-blind palette. Set
colorScale.preset to colorblind and coverage is drawn in vermillion, orange and blue (the Okabe–Ito palette) instead of red, amber and green — the gradient, gutter bars, line highlight, Explorer badges and since-last-run marks alike. A colour you set yourself keeps precedence.
0.2026.74 — Coverage, Filtered by Test
2026-09-12
Added
- Filter coverage by test in VS Code’s own Test Coverage view. When a tracefile names its tests — lcov’s
TN: sections — each test becomes an item under Fucini Coverage in the Test Explorer, every file in the Test Coverage view lists the tests that ran something in it, and choosing one shows the lines that test reached.
${command:…} in path settings. ${command:cmake.buildDirectory}/coverage.info follows whichever CMake kit is selected. The command runs before each discovery, for at most five seconds and only in a trusted workspace; one that fails leaves the variable as written, and the output says why.
- A 30-second quick start and a comparison with the alternatives open the Marketplace page and the home page.
Fixed
- A tracefile that recorded a file once per test counted it once per test. The records are now added together per tracefile, the way genhtml reads them: the file counts once in the totals, and with several tracefiles loaded no test’s lines are lost.
0.2026.73 — Signed Updates, and a Key That Survives the Switch
2026-09-11
Fixed
- The install id is now stored before anything uses it. 0.2026.72 build 2 stopped the id being worked out afresh on every call, but start-up still did not wait for it to be written, so a window closed moments after opening could still lose it — and with it the machine fingerprint an activation key is bound to. Start-up now waits.
- Switching from Pro to Community keeps your activation key valid. Each edition has its own extension storage, so Community used to start with a new install id and a new fingerprint, while the key in your settings — including the one the switch had just requested for the free unlock — was bound to the old one. The switch now hands the id over.
Security
- Updates from fucini.software are signed and checked before they install. Every
.vsix in the update feed carries a SHA-256 hash and an Ed25519 signature from a key that is never on the web server. The built-in updater and the switch to Community check both against the file they downloaded and refuse anything else — a proxy’s error page, a truncated download, a cached older build, or a file that is not ours. HTTPS alone does not cover this: a TLS-inspecting corporate proxy re-signs every connection it touches.
- Each release has a file name of its own. The update feed now points at a versioned
.vsix instead of one overwritten on every release, so a cache that ignores Cache-Control cannot serve last month’s build as this month’s. The links on the download page are unchanged.
0.2026.72 — The Decisions Tested Only One Way
2026-09-10
Added
- Jump to the decisions tested only one way. Next Partly-Tested Decision and its Previous twin walk the branches some of whose arms ran and some of which never did — the gap uncovered-line navigation cannot show you. A line whose statement ran is covered, and stays covered however many of its arms were never taken:
if (a && b) reached once with a false is 100% line-covered and half tested. One stop per decision rather than per arm, because the arms of an if/else can be pages apart, and jumping to the fifth arm of a switch says less than jumping to the switch. Decisions no arm ever reached are left to the uncovered-line commands, which already stop there — and a file whose report records no branches at all says so, rather than claiming every decision was tested both ways.
Reload Coverage re-reads what is already loaded. With watching off, the only way back to fresh numbers after re-running the tests was Load Coverage, which runs discovery again and can come back with a different set than the one on screen. This re-reads exactly what is loaded now, including tracefiles picked by hand that discovery would never find again.
Copy Coverage Summary puts the run in a Markdown table. Worst-covered file first, with a totals row, ready to paste into a pull request. The branch and MC/DC columns appear only when something in the run measured them: a column of dashes reads as zero coverage rather than as not measured, which is the opposite of true.
Fixed
- The install id could change between sessions — and it is what the licence fingerprint is built from. It was worked out afresh on every call and stored without anything waiting for the write, so a second call, or an editor window closing shortly after start-up, could produce a new one. An id that changes is an activation key that stops matching; it also inflated every figure counted per install. It is now settled once per session and stored before anything uses it. Build 2.
0.2026.71 — Which Tracefile Covered This Line
2026-09-08
Added
- Line-level origin attribution. With more than one tracefile loaded, the hit-count hover ends with Covered by: and the reports that recorded a hit on that line — and clicking a tracefile row in the Coverage view now lights the lines it contributed, in every open editor, the way it already highlighted the files it measured. This is the one question a merged run can answer and a single report cannot: two tracefiles that both measure a file disagree about its lines, and until now the merge flattened that disagreement and nothing ever showed it. Merging is still by file — the newest report wins a shared file outright — so the per-line origins are indexed before the merge rather than taken from it. Only covered lines count: a report that recorded a line as
0 did not cover it.
- lcov’s
TN: record is read at last. A tracefile that names its suite — TN:integration — is shown as coverage.info (integration) wherever origins are named, and the name survives an export back to lcov instead of being written out empty. Most producers write TN: empty and are unchanged; the ones that fill it in are exactly the ones whose tracefiles get merged, a suite or a platform per file, where the name is the only thing that says which is which.
fuciniCoverage.lineHighlight tints the lines themselves. off | uncovered | heatmap, off by default: a translucent wash behind every uncovered and partially covered line, and in heatmap behind every executed line as well, hotter the more often it ran. Its own setting rather than a fourth editorGutter.mode value, because the two are wanted in different pairs — the gaps tinted with the gutter left clean, or bars with no colour behind the code. Translucent because it sits under the text, and it tints the gaps rather than the covered lines, for the same reason the gutter does.
fuciniCoverage.overviewRuler decides the scrollbar stripe on its own. follow (the default) is what it always did — a stripe wherever the gutter draws an uncovered or partial bar, and never for the heatmap. uncovered paints it even with editorGutter.mode off, which is a map of the file’s gaps down the scrollbar with nothing at all beside the code. off keeps the bars and drops the stripe. The ruler was hardwired to the gutter before, so those two combinations could not be had.
Fixed
- A tracefile that will not parse now says what it was read as. “…is not well-formed” left the reader guessing whether the file had even been recognised as the format they think it is — and a Cobertura document refused by the VectorCAST parser is a different problem from a Cobertura document that is genuinely truncated. The notification ends with It was read as cobertura. when a parser claimed the file. Nothing is added when none did: “not in a format this extension reads” already says that in full.
0.2026.70 — The Stale-Data Notice Shows Its Icon
2026-09-08
Fixed
- The stale-data notice on the status-bar hover shows its clock icon again, instead of the text
$(history). The notice that says the numbers below it describe an older version of the files is prefixed with the same $(history) mark the status bar itself carries. In the status bar that mark is drawn as the clock-arrow icon, because StatusBarItem.text substitutes icons on its own; the hover is a MarkdownString, which has to opt in to that with supportThemeIcons and never did — so the one place that explains why the coverage cannot be believed opened with a piece of raw syntax. The tooltip now opts in.
0.2026.69 — Gutter Modes, Remote Tracefiles, Multi-Root Workspaces and Unmeasured Files
2026-09-08
Added
- The editor’s own gutter can now show uncovered lines, or a hit-count heatmap. By default it still shows only findings — MC/DC bars, since-last-run triangles, edited-line dots — and the colour-coded view of a whole file stays the Annotated Source.
fuciniCoverage.editorGutter.mode opts in to more: uncovered puts a bar on every line that never ran and an amber one on every line with a branch arm never taken, and nothing on covered lines, so a green wall does not hide the three red ones; heatmap adds a warm shade on every executed line, hotter the more often it ran. Choose Editor Gutter Mode... switches between the three from the palette or the status-bar menu. The README used to claim “native gutters” outright; it now says what is actually there.
- Inline hit counts.
fuciniCoverage.hitCounts.inline writes ×12 after each line a statement starts on, ×0 included — the number that has always been on hover, without hovering. Toggle Inline Hit Counts flips it; the status-bar menu offers it as Show or Hide, whichever applies.
${env:NAME}, ${config:section.key}, ${userHome} and ${workspaceFolderBasename} in every path setting, next to the ${workspaceFolder} that was already there. A variable the extension does not know is left as written, so a typo shows up in the discovery log as the path it looked for rather than vanishing.
- A
coverageFile.paths entry may be an http(s):// URL. The artifact is fetched into the extension’s storage on every load and loaded from there; the Coverage view names the URL, not the cache path. Each fetch is conditional (ETag / Last-Modified), so a re-fetch of an unchanged artifact downloads nothing, and when a fetch fails the last downloaded copy is loaded instead and the reason is in the output. fuciniCoverage.remote.headers adds Name: value headers with variables expanded — Authorization: Bearer ${env:CI_TOKEN} keeps the token out of settings.json — and fuciniCoverage.remote.refreshMinutes re-fetches on a timer. Add #lcov.info (or another file name) as the URL fragment when the URL itself does not end in one, so the format is known.
- Loading and Generate Full HTML Report show progress, and can be cancelled. A load of ten or more tracefiles, or 8 MB or more of them, runs under a progress notification with a Cancel button that names the file being read and the phase (reading, then checking sources); a smaller one only spins the status bar as before. Cancelling leaves the previous run loaded and says so. The HTML report does the same per page rendered and written.
- Two more reasons to reload. An absolute
coverageFile.paths entry outside the workspace — a build tree beside the checkout, a Bazel output root — is now polled too, so a file appearing there after the window opened is loaded rather than never looked at again; the watcher only ever saw the workspace. And fuciniCoverage.watch.reloadAfterTasks names tasks (a label from tasks.json, or source: name as the Run Task list shows it, e.g. npm: test) whose ending reloads coverage, whether or not watch.enabled is on — for builds the watcher cannot follow: a run that replaces a symlinked output tree, a report on a network share, a coverage step that post-processes its file.
- Multi-root workspaces. Every folder of a workspace is now discovered on its own terms:
coverageFile.defaultLocation, coverageFile.paths, pathRemap, ignore, watch.*, remote.*, badge.*, the report directories, gitDiff.*, problems.perLine, editorGutter.mode and hitCounts.inline are read per folder, so a folder’s own .vscode/settings.json or its .code-workspace entry applies; ${workspaceFolder} means the entry’s own folder; tracefiles found in every folder merge into one run, and the Coverage view groups the files by folder. ${workspaceRoot} in a pathRemap rule tries the tracefile’s own folder first, then the others. Anything that needs one folder — the file dialogs, the report and badge output, git metadata, Run Tests with Coverage — takes the active editor’s, and with several folders and no editor the test run asks which. A folder added to or removed from the workspace reloads coverage by itself. Before this, twelve places took the first folder and a second root got the wrong base for everything.
- The run is published to VS Code’s own Test Coverage view. With
fuciniCoverage.testCoverage.publish (on by default) every loaded run also goes to the native Test Coverage panel, its per-file percentages in the Explorer and the editor’s own “Show Inline Coverage” toggle — lines, branch arms and functions, with the details loaded on demand. Show in Test Coverage View publishes the active run and opens the panel, whatever the setting says; the status-bar menu offers it behind quickActions.showTestCoverage.
- A path-remap rule is suggested when files do not match. The most common “it loaded but nothing lights up” is a tracefile whose paths come from a container or a CI checkout (
/build/src/a.c, /home/runner/work/repo/repo/src/a.c, C:\agent\_work\1\s\src\a.c) while the workspace has src/a.c. When a load leaves files unmatched, the extension looks the file names up in the workspace, infers the prefix rule that explains the most of them, logs it as [FUCINI_REMAP_002], and the load result offers Apply Suggested Remap — one click appends the rule to fuciniCoverage.pathRemap and reloads. Suggest Path Remap Rule runs the same analysis on demand. Nothing is written without the click.
fuciniCoverage.ignore: files that never count. Vendored, generated or third-party files (vendor/**, **/*.generated.ts, build/**) are dropped from the run when it loads, so the Coverage view, status bar, thresholds, badges, CodeLens, history and exports all count without them; the load result says how many were left out and the output names them. Patterns match the workspace-relative path and the absolute path; a pattern with no / matches a folder or file name at any depth, like .gitignore. For whole files — lines inside a file keep using the tool’s own exclusion markers.
- Per-line Problems entries, opt-in.
fuciniCoverage.problems.perLine (off, uncovered or uncoveredAndPartial; off by default) puts an Information diagnostic on every contiguous block of lines that never ran — a forty-line dead function is one entry, not forty — and, in the second mode, on every line whose branch arm was never taken. Only for documents that are open, so a two-thousand-file run cannot flood the panel; the entries follow their lines as the buffer is edited and go when the document closes. The per-file entry behind problems.enabled is unchanged.
- A coverage badge for the README. Generate Coverage Badge writes a shields-style SVG for the active run into the workspace (
fuciniCoverage.badge.outputPath, default coverage-badge.svg), for a README to embed with  and no hosted service. badge.metric picks lines, branches, MC/DC or the weighted score; the colour follows the colorScale tiers; badge.autoUpdate rewrites it after every load. The SVG badge that Export Coverage Report already offered now shares the same renderer.
- Unmeasured versus uncovered. A file no test ever loaded is not in the tracefile at all, so the extension has always kept it apart from a measured file with 0 hits — and out of the numbers, which is exactly how a project half of whose modules have no tests reads “92%”. Now the distinction shows: opening such a file puts
not measured in the status bar, and fuciniCoverage.unmeasured.mode decides what else happens. list adds an Unmeasured group to the Coverage view with every workspace file of a kind the run measured that the run does not contain, counts them in the load result, marks them ? in the Explorer when badges are on, and lets threshold.unmeasuredFiles fail Check Coverage Thresholds past a chosen number. count goes further: each such file joins the run as 0% — one uncovered line per line of code — so the status bar, thresholds, badges, history, exports and the Test Coverage view all see the honest figure; the file is drawn as unmeasured everywhere, never as “no longer matches its coverage”. unmeasured.exclude keeps headers and the like out (**/*.h, **/*.hpp, **/*.hh, **/*.inl, **/*.d.ts by default). Off by default; the status-bar state is the one change that is always on.
- The Coverage view shows what merging did. A module’s own rerun loaded on top of a whole-project run has replaced that run’s numbers for the module’s files since 0.2026.63, and every row has named the report it is shown from — but a row whose numbers overruled an older report looked exactly like one measured by a single report among several, and nothing said how much of a report had been overruled. Now the tracefile badge on such a row is outlined; each tracefile row above the tree counts what the merge did with its files (“42 files · 7 overruled by a newer report”, “7 files · overrules 7 in older reports”); and clicking a tracefile row highlights the files it measured in the complete tree, dimming the rest, until it is clicked again.
fuciniCoverage.coverageView.mergedMarks (on by default) turns the outline and the highlight off; the counts stay.
0.2026.68 — Team Seats Ask for the Invitation Code
2026-09-07
Fixed
- A Business or Enterprise user is now asked for their invitation code, not the company code. Since 2 September the licence server refuses the shared company code for a Business or Enterprise seat: a seat claimed with it named nobody, so every team seat is now tied to the address it was invited at, and the code that activates one is the
FCI1… invitation code from that email. The questionnaire still labelled the field “Company code”, hinted to ask the manager for it and showed an FCB1… placeholder — sending people to paste the one code that no longer works, then telling them to enter a company code when they left it blank. On the team plans the field now reads “Invitation code”, the hint says where the code comes from and that the shared company code does not activate a seat, the placeholder shows the FCI1… shape, and the validation message asks for the invitation code. Pro asks for its subscription code by that name. Pro Plus is unchanged: it keeps the shared code on purpose. All 17 display languages carry the new wording.
0.2026.67 — ${workspaceFolder} Works in Both Path Settings
2026-09-05
Fixed
${workspaceFolder} now works in coverageFile.paths, as it always has in coverageFile.defaultLocation. The two settings sit next to each other and only one of them documented the variable, so people wrote it in both — and an entry starting ${ is not absolute, so it went into the discovery glob as a literal under the base folder, matched nothing, and the extension said there was no coverage. Such an entry is now expanded first, which makes it an absolute path, and is simply checked for. It is left as written in the setting, is not written a second time in another form when the same file is picked, and comes out of the setting by itself when unloaded from the Coverage view.
- A configured tracefile that is not there is now said so, in the output. An entry naming a wrong path failed in the one way nothing reported: “no coverage found”, indistinguishable from a project that genuinely has none.
[FUCINI_DISCOVERY] now names each entry that found no file and the path it looked for, once per entry rather than on every reload; a wildcard entry is not reported, since it may be waiting for a build to write something.
0.2026.66 — Branches, MC/DC and a Score on the Function CodeLens
2026-08-29
Added
- The per-function CodeLens now shows branches, MC/DC and a composite score, not just lines and hits — “add: 92% covered (12/13 lines, 52 hits, score 88%, branches 3/4 (75%), MC/DC 2/3 (66%))”. Each is left off a given function when the tracefile has nothing there to report — a plain-C build with no branch data, or one not instrumented with
-fcoverage-mcdc — rather than showing a misleading 0/0; the score is likewise only shown alongside at least one of the other two, since with lines as the sole metric it is by definition the same number the lens already leads with.
- The origin badge’s tooltip says how long ago each tracefile was written, not just its timestamp — “Measured by module.info (2026-08-15 14:32 · 3 hours ago), replacing what lcov.info (2026-08-01 09:10) said about it.” Buckets down to “just now”; past 30 days only the absolute date is shown.
- A tie in tracefile mtime is no longer silent. Two reports landing in the same build step, or a filesystem whose mtime resolution is coarser than a second, can report the identical timestamp for files they both measure — previously the winner fell back to
coverageFile.paths order with nothing on screen to explain it. The output channel now says so: [FUCINI_MERGE_TIE] names both tracefiles and how many files they share.
- “Show What Changed Since Last Run” on a file row’s
… menu, once a previous run exists to compare against — the same per-file delta Compare with Last Run already computes, aimed at the one file you asked about rather than the whole tree. A file nothing moved on says so plainly rather than offering nothing.
- Tune Colour Scale command: try
colorScale.* colours and thresholds against the live Coverage view before committing to them. Each edit writes straight to the setting — the view already repaints on any colorScale/threshold change, so writing the value is the preview — and a final “Keep” or “Revert to the original values” closes the loop.
- Per-file coverage gaps in the Problems panel, one entry per loaded source file under
threshold.statement, behind the new fuciniCoverage.problems.enabled (off by default — the default statement threshold is 100). Each entry summarises what’s uncovered (“62% lines covered — 14 uncovered lines, 2 uncovered functions”) and carries a quick fix that opens whichever chat extension is installed (Copilot, Claude Code, …) with a prompt to write a test for the gap — a per-line diagnostic would flood Problems on a real codebase, so this stays at file granularity, the level a reader can actually act on.
Fixed
- A missing/mismatched/verified file’s reason, in the Coverage view, can now be selected and copied. It used to live only in the row’s native
title tooltip, which — being the browser’s own — cannot be selected; someone wanting to paste the exact reason into a bug report had to retype it by hand. Hovering now shows the same text in a real, styled panel instead, which can be dragged over like any other page content. An ordinary file’s tooltip (just its path) is unaffected.
- A function whose parameter list spans several lines no longer falsely reports as moved.
detectMismatch allowed a declaration’s recorded line to disagree with where its name is actually found by at most two lines — enough for the usual disagreement between a signature line, a return-type line and an opening brace, but not for a multi-line parameter list, which can put the two arbitrarily far apart. void LOG_Emit(...) — seven parameters, one per line — recorded at its opening brace, line 163, was reported as "LOG_Emit" is at line 3: the nearest textual match, which happened to be the function’s own name mentioned in the file’s header comment, nowhere near line 163 or the real declaration at line 156. The check now also accepts a name found further back when every line between it and the recorded line reads as one unbroken declaration — no blank line, no line ending the previous statement or block — which a parameter list always does and an actually-moved function almost never does. Where a mismatch is still genuine, the reported line is now the closest match to the recorded one rather than always the first found in the file.
- The origin badge no longer says “+N”. Loading a module’s own report on top of a wider run marked the file’s badge with the module’s name plus how many other tracefiles also covered it — meant as “this many were overruled,” it read as “these reports agree,” and there was no way to tell from the tree which of the n it actually meant. The badge now only ever names the tracefile whose numbers are on screen; the tooltip still spells out what was overruled, and by when.
- The origin badge no longer pushes the filename it sits beside off the edge of the panel. The badge’s width was fixed (
flex: none), so once a row ran out of room the filename absorbed the entire deficit and elided first, even though the badge is the less important of the two. The badge now yields its own width before the filename loses a single character; the percentage and bar, which already outrank the filename, are unaffected.
0.2026.65 — A Module’s Rerun Replaces What a Wider Run Said
2026-08-15
Changed
- A module’s own rerun replaces what a wider run said about it, instead of adding to it. Loading a project-wide report and then a single module’s own report on top used to sum their hit counts for every file the two shared — a rerun of just that module’s tests inflated numbers a wider, now-stale run had already reported, rather than refreshing them. The tracefile with the newest mtime on disk now wins outright for any file it measures: statements, branches, declarations and the per-file rollups (branch, MC/DC, function, function-call, object and mutation summaries) all come from that report alone, regardless of where either path sits in
coverageFile.paths — a module rerun wins because it is actually newer, not because it happens to load last. A tracefile that reports no statements for a file at all — a TRACE32 object-code export, a Stryker mutation report loaded beside a real one — still only contributes the summary it actually carries, leaving an earlier report’s line/branch detail untouched; that case is unaffected. This also removes a standing defect where a per-file MC/DC (or function/function-call/mutation) rollup measured by two runs had its denominator summed along with its numerator, understating the true percentage. The origin badge’s tooltip now says “replacing what _ said about it” rather than “added together.”
Fixed
- A long filename in the Coverage view now elides instead of spilling. The row’s name span was missing
min-width: 0, the one declaration that lets a flex item’s overflow/text-overflow: ellipsis actually engage — without it, a long name kept its full width once the panel narrowed enough, running underneath the origin badge, percentage and bar that follow it.
0.2026.64 — Sort the Coverage View by Score
2026-08-01
Added
- Sort the Coverage view by score, not just name. Every row already carries a WHM score and a per-metric breakdown; finding the worst file in a large tree meant reading down an alphabetical list for it. A dropdown next to Collapse all / Expand all now offers File name, Composite score, Lines, Branches and MC/DC — every mode but File name interleaves folders and files together, worst first by that metric’s own percentage, so a badly-tested folder ranks exactly like a badly-tested file rather than being grouped away from one. A file with no data for the metric in view (a format that never reports branches, say) sorts to the bottom rather than being read as a 0%. The choice is written to the new
fuciniCoverage.coverageView.sortBy setting as it is made, so it survives a window reload instead of always starting back at File name.
Fixed
- A multi-line function signature no longer falsely flags as “moved.” Istanbul’s
fnMap gives a function two line references — where its name sits, and where its body starts — and the parser preferred the body line for the mismatch check that later searches the source text for the name. For any function whose parameter list spans more than two lines (routine in a codebase wrapped at 100 columns), the name’s real line was too far from the recorded one, and the file dimmed in the Coverage view with a false “no longer matches its coverage” warning. The parser now prefers the name’s own line, which is what the mismatch check was always looking for.
0.2026.63 — Every Row Names Its Tracefile
2026-07-20
Added
- Each row says which tracefile measured it. Loading one module’s own report on top of a whole-project run is the same files measured twice, and the tree said nothing about which report a row was reading. With more than one tracefile loaded, every file row now carries the name of the report that measured it — the most recently loaded one, where several did, with
+1 for each of the others and a tooltip naming them. A merge adds their hits together rather than letting one win, and the tooltip says so. With a single tracefile loaded nothing is drawn, because there would be nothing to tell apart.
Changed
- The extension’s own page names every setting, with its real default. Twenty settings had no row at all — the language, three thresholds, one weight and the fifteen quick-action switches — and the telemetry row claimed a default of on where it has been off for some time. A test now compares the page against the manifest, defaults included.
Fixed
- The re-check of sources under
files.watcherExclude, added in the previous release, obeys staleWarning.enabled like the rest of that check rather than a setting name of its own.
0.2026.62 — The Explorer Left Alone, Silenced Sources Re-Checked
2026-07-06
Changed
- The Explorer is left alone. Coverage badges next to files in the Explorer are off by default. That column belongs to whoever is using it — VS Code puts git’s own M and U there, and only one badge fits — and the Coverage view lists the same files with the same numbers, in a place that exists for them.
explorerBadges.enabled turns them back on, unchanged.
Fixed
- A source under
files.watcherExclude is re-checked too. The live match between a file and its coverage is driven by saves and by a file-system watcher, and that watcher is silent for anything excluded — generated sources under build/ above all, which is where a stale mark is hardest to notice. Those files are now polled on the same two-second tick as the tracefiles and re-read only when they actually move. Past two hundred of them the output says so instead, rather than spending the tick.
0.2026.61 — One Question About the Test Runner, Not One per Run
2026-06-09
Added
- The test runner is asked about once, not once per run. A workspace with more than one recognised runner — a jest config and a
go.mod, say — put the picker in front of every Run Tests with Coverage, charging a keystroke for a decision already made. The answer is remembered for the workspace, and the prompt says so. Forget the Test Runner puts the question back, and a remembered runner the workspace no longer has is ignored rather than run.
Changed
- The extension’s own page names every command. Fourteen of the thirty-four had drifted out of its table — documented here and in the user guide, but not on the page people read before installing. All of them are listed now, and a test keeps it that way.
0.2026.60 · build 3 — Undo Takes the Dots Off Again
2026-06-01
Fixed
- Undo takes the edited-line dots off again. A buffer edited since the run shows grey dots on the rewritten lines, and coverage withdraws from them. Undoing those edits left the dots where they were: every change counted, including a change that put the text back. What the buffer held before anybody typed is now remembered, and the moment it holds that again the dots come off and the numbers apply to it once more.
- The release numbers are checked against each other. The manifest, the changelog’s top entry and the release tag all carry the version and build, all three are typed by hand, and they have drifted before now. The test suite compares them and checks that the entries run newest first with a real date on each.
0.2026.59 — An Unload That Lasts
2026-05-12
Changed
- An unloaded tracefile stays unloaded. Taking a file off the list in the Coverage view lasted as long as the window: with nothing configured, discovery falls back to the well-known names and found
lcov.info again the next morning, and the only way to say otherwise was to configure a path in order to exclude one. The decision is now remembered for the workspace. It is undone by picking the file again, or by naming it in coverageFile.paths — a file the setting names is never withheld, so nothing can go missing without somebody having said so.
Fixed
- Faster repainting of an edited buffer. The list of rewritten lines was recomputed from every recorded edit on every repaint, so a long file edited all afternoon paid line count times edit count for an answer that had not changed since the last keystroke. It is now computed once per keystroke.
0.2026.58 · build 2 — A Test Run Remembers Its Tracefile
2026-05-01
Changed
- A test run remembers its tracefile, like a pick does. Run Tests with Coverage loaded what the run wrote and then asked whether to add it to
coverageFile.paths. A file picked by hand has been written there by itself since 0.2026.56, and a test run is the same act by another route, so it now writes it the same way. One setting, coverageFile.rememberPicks, governs both; the tracefile’s row in the Coverage view is where either comes off again.
Fixed
- The background timers stop when the extension does. The tracefile watcher was disposed with the window, but the two-second poll behind it and any pending source re-check are plain timers, which nothing disposes. Disabling the extension, or reloading a window with it disabled, left them running and reading a workspace the extension no longer had any business reading.
0.2026.57 · build 4 — A Labelled Live Preview, Drawn Flags, Every Format in the Pick Dialog
2026-04-24
Added
- The live preview says what it previews. The settings panel's preview now leads with Run: coverage/lcov.info · 12 files and, when the active editor shows a file the run covers, Active file: src/a.c with that file's own verdict beneath the run's. It follows the editor and every load, under whatever the form holds — unsaved edits included. With the panel in front, a click in the Coverage view switches the preview to that file instead of opening it.
- The pick dialog names every format. All coverage formats first, then LCOV; JSON (llvm-cov, Istanbul/nyc, coverage.py, SimpleCov, Coveralls, gcov, xccov, Stryker); XML (Cobertura, JaCoCo, Clover, OpenCover, GNATcoverage, Bullseye, SonarQube, VectorCAST, TRACE32, EMMA, UCIS/UCDB, NCover, dotCover, PIT); Go coverage profile; gcov listing and Testwell CTC++ — and
.lcov, .gcov, .txt and .coveragexml are no longer greyed out.
Changed
- The status-bar hovers wear the colour scale. The metric bars in the run and file tooltips take
colorScale.*'s three colours, tiered by each metric's own threshold where one is configured and by the scale's breakpoints otherwise; the verdict pills keep their fixed status colours.
- Drawn flags in the language setting. The flag emoji of 0.2026.56 render as two bare letters on Windows. The dropdown now names each language by itself — Nederlands (nl) — with a row of small drawn flags under it that select in the dropdown.
0.2026.56 · build 3 — Picks Are Remembered, Tracefiles in the Coverage View, Flags in the Language Setting
2026-04-08
Added
- A pick is remembered. A file chosen with Pick Coverage File lived only in the window it was picked in. It now goes into
coverageFile.paths in the workspace settings by itself — relative to the base folder when it is under it, absolute otherwise — so the next window loads it and the watcher reloads it. coverageFile.rememberPicks (default on) keeps the old behaviour for anyone who wants it. An absolute entry in that setting now names one file wherever it is.
- Tracefiles in the Coverage view. Above the tree, one row per loaded tracefile with its file count and an unload button that takes it out of the run and out of the settings; a Pick… button beside them adds one. Unloading the last tracefile clears the run.
- Flags in the language setting. The settings panel's language dropdown showed bare codes; each entry now carries a flag and the language's own name for itself — 🇳🇱 Nederlands (nl), 🇯🇵 日本語 (ja), 🌐 Automatic.
0.2026.55 — A PHP File's Last Line Is Not Coverage Past the End
2026-03-27
Fixed
- A PHP file's last line no longer counts as coverage past the end. Xdebug and PCOV record the implicit end-of-file return on the line after the file's last newline, so php-code-coverage writes a hit on "line 252" of a 251-line file — and the mismatch check called that a contradiction on every PHP file with a trailing newline. One line past a final newline is now allowed; a reference beyond that, or beyond a file with no final newline, is still the contradiction it was.
0.2026.54 · build 2 — A Trial Row in What Community Includes
2026-03-24
Added
- A trial row in What Community Includes, in the Community edition. That build never had a trial, so its list now leads with Start a 14-day Pro trial…, which opens the Pro listing; the Pro build's activation then offers to take the Community build out of the way. Community stays free whatever is decided.
0.2026.53 — Show Worst-Covered Folders
2026-03-03
Added
- Show Worst-Covered Folders. The folder-level answer to Show Worst-Covered Files: every folder ranked by the coverage of everything below it, worst first, from the same index the Explorer badges are painted from; a pick reveals the folder in the Explorer. In the status-bar menu with
quickActions.worstCoveredFolders.
0.2026.52 — The Threshold Gate in the Problems Panel
2026-01-23
Added
- The threshold gate in the Problems panel. With
compliance.diagnostics on, every failing threshold.* metric is one error in Problems, on the tracefile that failed it, refreshed on every load and by Check Coverage Thresholds, cleared when the gate passes. Off by default, because the shipped statement threshold is 100.
Changed
- The
watch.enabled description no longer promises a prompt. It still said the free tier is asked before a reload — true until 0.2026.48, not since. Rewritten in all 18 languages.
0.2026.51 · build 2 — Run Tests with Coverage
2025-12-08
Added
- Run Tests with Coverage. Recognises vitest, jest, pytest, go, cargo, dotnet, maven, gradle, phpunit or ctest from the workspace root, runs it with coverage on in a terminal task, loads what it wrote whether or not the tests passed, and offers once to add that path to
coverageFile.paths so the watcher does the loading from then on. Several runners are offered as a pick; none sends you to the recipes.
0.2026.50 — Coverage Follows the Text in an Edited Buffer
2025-10-23
Added
- Coverage follows the text in an edited buffer. The edits made to each open buffer since the run was loaded are kept, and every surface that reads run line numbers — the hit count on hover, the MC/DC bar, the since-last-run triangle, the CodeLens, Next Uncovered — reads through them: a mark moves with its line and withdraws from a line that has been rewritten, where a small grey dot says why.
dirtyMarks.enabled turns the dots off.
0.2026.49 — Since Last Run, in the Gutter
2025-09-26
Added
- Since last run, in the gutter. Every line executed in the previous run and not in this one carries a red triangle in the editor gutter, every line that went the other way a green one, each with a stripe in the overview ruler and the hit counts on hover. Lines that held show nothing.
sinceLastRun.enabled turns it off.
Changed
- The baseline for "last run" survives a restart. With
history.enabled on, the newest run in history stands in on a window's first load, so a build that finished before the window opened still shows what it changed — in the gutter and in Compare with Last Run.
0.2026.48 · build 6 — Reload by Itself Everywhere, the Mark Follows the File, the Exact Difference on Hover
2025-09-19
Added
- The verdict on a file stays current after the load. A file was checked against its coverage once, as the tracefile was loaded, and then never again: edit it all afternoon and the gutters went on painting numbers about a version of it that no longer existed. Now a save in the editor re-checks that file at once, and a change from outside the editor — a
git checkout, a formatter, a generator — re-checks it from disk a moment later. A file that comes to contradict its run gets the mark; a checkout that puts the measured version back clears it.
- The exact difference, in the hover. Where a file no longer matches its coverage, the Coverage view and the Explorer tooltip say precisely what proved it: the line as it reads now against what the report recorded where the tracefile kept the text, the line and a failed checksum where it kept a digest, coverage past the end of the file, a function no longer where the report puts it. The same detail goes to the output channel, one line per file, where Show which on the load result lands.
- A switch for the Explorer badges.
explorerBadges.enabled (default on) hides the score, ✓ and ⚠ next to each covered file in the Explorer, and the per-metric hover with them. Gutters, status bar and the Coverage view are unaffected.
Changed
- Every tier reloads by itself when the tracefile changes. Since 2026-09-02 the free tier stopped at a new coverage detected — load it? prompt while paid licences reloaded silently. A build finishes, the numbers are new, and somebody who turned watching on has already answered the question — so the prompt is gone on every tier, and so is the notice that explained it.
- Loads the product does by itself no longer count against the free tier's daily cap. A watcher reload or the startup discovery is not a load somebody asked for; counting them would spend a Community install's allowance on one morning of test runs and then leave an old run on screen with nothing to say so. The cap now meters the command and the picker only.
- A moved timestamp alone no longer flags a file. The warning N file(s) changed since this run was drawn from each source's mtime, and a
git pull or a formatter moves every mtime without changing what was measured. It is gone, with its status-bar mark. A file is now called changed only when its contents contradict the run, and the status bar, the load result and a new FUCINI_MISMATCH_001 output line all read from that. staleWarning.enabled keeps its name and now describes exactly the content check.
- Watching a folder under
files.watcherExclude is said in the output. Build output is the first thing people exclude from VS Code's watcher, after which only the 2 s poll notices a new run. The output channel now names the folder and the glob when the watcher starts.
0.2026.47 · build 2 — Unmatched Paths on Screen, a First-Run Hint, Two More Keys
2025-08-09
Added
- A first-run hint for a workspace with nothing to load. Watching is on by default and the startup load is silent, so a fresh install in a project with no tracefile yet said nothing at all. The first silent load that comes up empty now says so, once ever, with the two things worth doing about it: pick a coverage file by hand, or open the recipes for how to produce one with your toolchain. A load somebody ran by hand keeps its own message.
- Keys for Toggle Annotated Source and Load Coverage.
Alt+A flips the active tab between source and its annotated view; Alt+Shift+R rediscovers and reloads coverage (Ctrl+Alt+A / Ctrl+Alt+R on macOS). Remappable like the two added in 0.2026.45.
Changed
- Paths that match nothing in the workspace are counted on screen. A tracefile path that resolves to no workspace file loads and never lights up — the most common "it worked but nothing shows" there is — and until now the only trace of it was a
FUCINI_REMAP_001 line in the output channel, with the pathRemap rule that would fix it, unread. The load result now counts them, with Show which for the names and the suggested rule, and Open pathRemap Setting for the place to put it.
- The status-bar menu's file picker names the formats it reads. Its description still listed the five formats from the first release; it now says twenty-five, from lcov, Cobertura and JaCoCo to gcov, llvm-cov, TRACE32 and mutation reports.
0.2026.46 — A Remedy for the Edition Clash, and a Quieter Cap
2025-08-06
Changed
- Both editions installed: two buttons instead of instructions. With Pro and Community both present neither can activate — they define the same commands and views — and the message saying so used to end with directions to the Extensions view. Both remedies are one command each, so they are buttons now: Keep This One uninstalls the other edition and offers the reload; Show Both in Extensions opens the Extensions view on exactly the two of them. If the uninstall fails, the reload is still offered — a reload is what settles which edition is live either way.
- A usage cap is announced in full once, then only in the status bar. The first time a Community cap is hit it deserves the whole answer — the numbers, the plan, the telemetry unlock, the list of what the tier includes. The fifth time, ten minutes later, it does not. Within a session, further hits on the same cap put an eight-second line in the status bar and interrupt nothing. Entering a key starts over, because a new licence is a new set of caps.
0.2026.45 — Keys for Next / Previous Uncovered
2025-07-26
Added
- Next / Previous Uncovered have keys. They are the two commands pressed over and over while reading a file, and until now each press meant opening the Command Palette and typing the name.
Alt+N steps to the next uncovered range and Alt+P to the previous one (Ctrl+Alt+N / Ctrl+Alt+P on macOS, where Option types characters). Both are scoped to a focused text editor and remappable under Keyboard Shortcuts like any other default.
0.2026.44 · build 3 — The Free Tier's Limits, in One List
2025-07-24
Added
- What Community includes, as one list. The free tier caps eight actions and withholds twelve features, and every one of those decisions was enforced at its own door — so the only way to learn the shape of the tier was to walk into each wall in turn, mid-task. A new command, What Community Includes, shows all of it at once: every capped action with how much of its day or month is already used, every withheld feature by name, and the two ways to lift them — a paid plan, or the telemetry unlock.
- Offered wherever the question comes up. An entry in the status-bar menu, a button in the Coverage view's licence banner, and a button on the two messages a Community install meets at a wall — usage limit reached and this action is not included. The rows are derived from the same entitlement set the gates enforce, so what the list says is what will happen.
Changed
- Watch mode says what it will do on the free tier. The status-bar entry promised to "automatically reload coverage" to everyone, and on Community that is a promise the watcher does not keep — it notices, and asks. The entry now says so there, and the command is titled Toggle Tracefile Watch, since "Toggle Watch" never said what was being watched. The behaviour itself is unchanged.
0.2026.43 · build 3 — A Failed Load Says Why, and Hidden Files Are Counted
2025-06-25
Changed
- A tracefile that will not load now says why, and what to try. Every failure used to produce the same notification — "no coverage could be parsed. See output." — with the reason one channel away as a code and a parser message. The notification now leads with the file and sorts the refusal into what it was: not in a format this extension reads, not well-formed (a build still writing it is the usual cause), well-formed but records nothing (the run executed no instrumented code), or could not be read. The parser's own words still go along, several files are counted, and the output channel is offered behind a Show Output button rather than forced open.
- Files a tracefile lists but has nothing to measure in are now counted on screen. A declaration-only header has always been kept out of the Coverage view rather than scored 100% off a 0-of-0 — and its name was written to the output channel, and nowhere else. The load result now says N file(s) with nothing to instrument are not shown, with a Show which button that opens the channel where the names are.
0.2025.42 · build 3 — NCover and dotCover, the .NET Coverage With Nowhere to Go
2025-06-08
Added
- NCover and JetBrains dotCover. Coverlet's Cobertura and OpenCover were already read here, which covers most new .NET work and none of the older kind. A build still writing NCover's
Coverage.xml, or a shop measuring with dotCover, had coverage on disk this extension could not open. Neither needs a converter: both carry file names and line numbers already.
- An excluded NCover point is dropped, not scored as unhit.
excluded="true" marks code deliberately taken out of measurement — an [ExcludeFromCodeCoverage] member, or an NCoverExplorer filter. Counting it as uncovered would make an exclusion lower the number it was written to correct.
- dotCover needs
/ReportType=DetailedXML. Its other XML report is a rollup with no line numbers anywhere in it; that report is recognised and turned away with the flag that fixes it, rather than failing as an unknown format. dotCover records a yes or a no per statement, never a visit count, so a covered statement arrives as one hit.
0.2025.41 · build 4 — Mutation Testing: PIT and Stryker
2025-05-28
Added
- Mutation testing. Coverage says a line ran; a mutation score says something failed when that line was changed. A suite can execute every line in a file and detect none of the faults injected into it, and no coverage number anywhere says so. PIT covers Java, Kotlin and Scala; Stryker covers JavaScript, TypeScript, C# and Scala.
- It is never averaged with coverage. Blending "did this line run" with "did anything notice when I changed it" answers neither, so it takes no weight in the composite score and is gated on its own through
fuciniCoverage.threshold.mutation — defaulting to 0, since a mutation score is a target to raise rather than a bar to clear on day one.
- A mutant that never got a fair run counts in neither half. PIT's
NON_VIABLE and Stryker's CompileError and Ignored are mutations the tool could not make; scoring them as undetected would punish a suite for a fault it was never offered.
0.2025.40 — gcov's Plain-Text Listing, for Compilers Older Than GCC 9
2025-05-16
Added
- The annotated
.gcov listing. gcov --json-format arrived in GCC 9, and embedded cross-compilers are frequently much older; until now the answer was "install lcov or gcovr first". The execution count has five spellings and only two are numbers, and branch/call/function annotations attach to a neighbouring line rather than their own — all of it read correctly here.
- Call annotations become call-site coverage.
call N returned X% is exactly the function-call metric added in 0.2025.38, carried by this format since long before anything here could hold it.
0.2025.39 · build 2 — Object-Code Coverage, and With It Assembly
2025-04-27
Added
- Object-code coverage — how much of the built binary actually ran. The only metric here not measured from source at all: it comes from a debugger watching the instruction stream on the real target, with no probe effect on timing. It is also the honest answer to "does this cover assembly?" — hand-written assembly is never instrumented, and every other metric here works on source and cannot reach it.
- It is a requirement, not another weight. Object-code coverage subsumes statement coverage — 100% here implies 100% there, never the reverse — so it takes no weight in the composite score and is gated instead, which is how DO-178C level A asks for it on top of MC/DC.
- The gap is the deliverable. A run at 100% line coverage and less than 100% object coverage has located exactly the compiler-generated code no source line accounts for; the compliance card says "312 bytes never executed, with 100% line coverage" rather than burying it as "94%". The reverse case — a whole binary reported as run while its source was not — is reported as a contradiction, not a low score.
0.2025.38 · build 2 — Function-Call Coverage, and VectorCAST and TRACE32 Complete
2025-04-01
Added
- Function-call coverage — was every call site exercised. A different question from function coverage: a function entered once from one of its three call sites is complete under Functions and one third complete here. VectorCAST, TRACE32 and Cantata all reported it while this model had nowhere to put the number.
- This completes VectorCAST and TRACE32. Earlier releases deliberately refused to fold this data into branch coverage, because inflating a branch percentage inflates a number that ends up in an audit. Both formats now load what they always sent.
Changed
- The compliance gate and the weighted score know about seven metrics, not six — two new settings,
fuciniCoverage.whmWeights.functionCalls and fuciniCoverage.threshold.functionCalls, with descriptions in all 17 languages.
0.2025.37 — Function Coverage as a Count, Not Only as a Place
2025-02-16
Changed
- Function coverage now counts even when the tool won't say where the functions are. TRACE32's
func export, VectorCAST's self-closing <method> elements, and EMMA's method, % rollup all state a function count with nowhere to place a CodeLens — all three were discarded on the way in. Declarations stay the preferred source wherever they exist; the count is now used as a fallback where there are none.
0.2025.36 · build 4 — UCIS and Aldec UCDB, and the New Parsers Covered to the Line
2025-02-03
Added
- UCIS and UCDB XML — VHDL and Verilog, out of the simulator. Accellera's standard
<UCIS> interchange form and Aldec's own ux:ucdb (what acdb2xml produces from Active-HDL and Riviera-PRO) both load. Statement and branch coverage come across; toggle, FSM, assertion and functional coverage are not read, and a document holding only those says so rather than loading empty.
- Open-source VHDL already needed nothing here: GHDL's GCC backend instruments with
-Wc,-ftest-coverage -Wc,-fprofile-arcs, and the result goes through gcov, gcovr or lcov. This release is for the commercial simulators.
0.2025.35 · build 3 — EMMA XML, and Delphi Finally Has a Way In
2025-01-22
Added
- EMMA XML. Delphi had no way in at all — no lcov, no Cobertura — and DelphiCodeCoverage writes this schema with
-xml. It is also what a good deal of older Java tooling still emits, so one parser opens a language and clears out a legacy at the same time.
- Add
-xmllines and the report gains real per-line hit counts, so gutters work; without it there is only a rollup percentage, which gets a file summary and no gutters rather than a guess. A dotted package name becomes a directory only when the file is a Java-family source — in a Delphi report the same field is a unit name, and prefixing it would invent a folder nobody has on disk.
0.2025.34 · build 3 — Lauterbach TRACE32, One Metric Per Export
2025-01-01
Added
- TRACE32 coverage exports.
COVerage.EXPORT.ListModule writes what a debugger measured from on-target trace — not instrumented source, but the instruction stream the processor actually executed, with no probe effect on timing. An export carries one metric at a time: stmt becomes line coverage, dec/cond become branch coverage, mcdc becomes MC/DC. Load two exports of the same run and they merge on module name.
- Exports carrying
func, call or object coverage were refused by name rather than loaded empty, naming which metric the file holds — all three now have a home, in 0.2025.37, 0.2025.38 and 0.2025.39 respectively.
0.2025.33 · build 4 — Testwell CTC++, Read by Column Because the Text Isn't Whitespace-Delimited
2024-12-19
Added
- Testwell CTC++ execution profile listings.
ctcpost turns MON.sym and MON.dat into a fixed-width text listing — the tool sitting opposite Bullseye in European embedded and safety evaluations, and the one that measures multicondition coverage. Read by column position rather than by splitting on whitespace, because a description can itself begin with a digit.
- Multicondition is not MC/DC, and is not reported as MC/DC. CTC++ counts combinations of conditions; MC/DC asks whether each condition was shown to independently affect the outcome. Putting these counts behind the MC/DC gate would give an auditor a number under a name it does not mean, so they are branch coverage here, which is what they are.
0.2025.32 · build 4 — VectorCAST XML, and the MC/DC It Was Quietly Discarding
2024-12-08
Added
- VectorCAST XML. VectorCAST writes a Cobertura report down to the doctype, and then adds attributes for what plain Cobertura cannot say. Which meant these files already loaded here and looked complete — with
mcdcpair-coverage discarded on the way in, silently, from the one audience that bought a tool specifically to produce it. Those pair counts now feed the MC/DC gate, percentage and composite score.
- Function-call coverage is deliberately not approximated. Folding it into branch coverage would inflate a number that ends up in an audit; it waits for the model to have a name for it, which arrives in 0.2025.38.
Changed
- The Cobertura reader now carries a dialect:
<coverage line-rate=…> is a family, not one format, and which dialect is being read travels with the parse instead of being guessed per element.
0.2025.31 — Ada, Swift, and Coverage That Reloads Itself
2024-11-07
Added
- GNATcoverage XML — Ada, at last. DO-178C avionics and EN 50128 rail teams are the audience this product’s safety-weighted score and MC/DC support were built for, and until now they could load nothing here unless their
gnatcov was new enough to also emit Cobertura, which throws away everything below the line. gnatcov coverage --annotate=xml writes a directory rather than a file, so load the per-source *.xml files — opening index.xml tells you where the coverage actually is instead of failing obscurely. A partially covered line stays a covered line with an uncovered decision: the shortfall shows up in branch and MC/DC coverage, where it belongs. Exemption regions become excluded lines, an undetermined obligation counts as uncovered rather than covered, and with --level=stmt+mcdc MC/DC is counted per condition and drives the gate and the composite score.
- BullseyeCoverage, and Visual Studio
.coveragexml. Bullseye is the commercial C/C++ tool embedded and safety-critical teams own; run its own covxml and load the result. Its headline metric is condition/decision coverage rather than line coverage, and the two stay separate here — and a single-outcome probe is scored out of one rather than two, so a fully tested switch is not reported as half covered. Visual Studio’s .coveragexml completes .NET for teams on MSTest and vstest rather than Coverlet: convert the binary .coverage with dotnet-coverage convert first. Partially covered lines survive the trip in both.
- SonarQube generic coverage — and a straight answer about Xcode. Four attributes on two elements, and it matters because almost any tool that cannot emit one of the other formats can emit this one, so a project whose coverage tool this extension has never heard of now has a way in. It is also what
xcresultparser writes, which is how Swift and Objective-C projects get here: .xcresult itself has no parser, and the documentation now says why rather than leaving you to guess — xccov reports percentages, not per-line data, and the per-line archive is readable only one file at a time, in a document that never names the file it describes.
- Gzipped tracefiles load as they are.
gcov --json-format writes .gcov.json.gz by default, so the format the documentation advertised was one most people who tried it could not actually load. Detection is by the file’s own magic bytes rather than its name, so a compressed report that lost its .gz still works — and any supported format may now arrive gzipped, not just gcov’s.
- Swift and Objective-C, from Xcode's own report.
xcrun xccov view --report --json is the one coverage document an iOS or macOS build produces with no extra tooling. You get file percentages, per-function execution counts and CodeLens. It carries no per-line data, and Studio does not invent any, so gutters stay empty for this format — an invented gutter is indistinguishable from a measured one. For line-by-line Swift coverage use llvm-cov export -format=lcov or slather, both of which already worked.
- Coveralls JSON — written by reporters for Elixir, Ruby, Python, Rust, Go and JavaScript alike. Line-level, so gutters work.
- Active days, gate blocks, and the licence a rollup belongs to — three additions to the monthly usage summary, for installs that have opted into telemetry.
Removed
- The computer name is no longer sent.
hostname had been in the telemetry baseline from the beginning — outside every gate, sent for anyone with telemetry on. A computer name is very often a person's name, a desk number or a customer's project, and it answered no question about usage that the OS platform did not answer better. An install is now identified by its random install id and nothing else. With this gone, nothing that names you or your machine leaves it: not the computer name, the OS username, the home directory, the network interfaces, your file paths, your project's folder name, or a signed-in account label. What remains is the operating system, its release and its architecture — what kind of machine, not which one.
- Telemetry is now deleted within 90 days at the latest, where the policy previously said only that it was “kept while it is useful”, which is not a period at all.
Changed
- A paid licence now reloads coverage by itself. Watching a tracefile has always meant being asked “new coverage detected, load it?” after every build, on every tier — a question with one sensible answer, put several times an hour. That prompt is now the free Community behaviour; a paid licence simply loads. Community keeps the watcher, the detection and the prompt it always had.
- A mismatched file is named, with the check that proved it, on a paid licence. The detection is not gated and will not be: telling somebody their coverage no longer describes their file is not something to sell them. Community still gets the finding, and the row still dims. A paid licence adds the pointer — the mark, the Explorer badge, and the line, function or checksum behind it.
- Telemetry sends less, and says who it belongs to. The monthly summary no longer carries file paths, your project's folder name, or a signed-in account label. The free-features-for-telemetry trade now asks for an email address, so whoever agreed to it can be told what their install sends and have it deleted on request.
- Three setting descriptions corrected.
watch.enabled, staleWarning.enabled and telemetry.enabled had each drifted from what the code does — telemetry.enabled most of all: it described sending considerably more than is sent.
Fixed
- The file picker no longer hides formats it reads. Pick Coverage File filtered to
info, json and xml, so Go’s coverage.out and gcov’s .gcov.json.gz — both formats this extension reads — were greyed out in the very dialog that asks you to choose one.
- A plain Community install no longer sends a page of its source. The check gating the annotated-source sample treated every Community install as having taken the telemetry trade, not only those that had.
- One warning per load instead of two — a file that no longer matches its coverage has almost always been edited since the run as well, and each was reported separately.
- An uninstall no longer reports the OS username and home directory of someone whose licence forbids it.
0.2025.30 · build 3 — Whatever Your Build Already Writes
2024-10-18
Added
- Go coverage profiles.
go test -coverprofile is the only coverage output the standard Go toolchain produces — there is no lcov to fall back on the way Rust, Dart and Elixir have one — so a Go project had nothing this extension could read. A profile records blocks rather than lines, and every line a block spans takes its count, closing brace included, exactly as go tool cover -html highlights it. coverage.out joins the discovery fallbacks, so a Go project with no configuration finds its profile the way a C project finds lcov.info.
- Clover XML, and OpenCover XML. Clover is what PHP projects actually have:
php-code-coverage can emit Cobertura, but Clover is its default and what nearly every phpunit.xml configures, so PHP coverage was unreadable here while a Cobertura parser sat waiting for output almost nobody generates. Istanbul's clover reporter writes the same document, so a Jest project configured that way is covered too. OpenCover is the .NET counterpart: C# and VB.NET already arrived as Cobertura, which flattens a decision into a condition-coverage="50% (1/2)" string, so which arm ran was gone before a parser saw it. OpenCover keeps a visit count per arm — the difference between knowing half a branch is covered and knowing which half.
- coverage.py JSON, and gcov/gcovr JSON. coverage.py's Cobertura and lcov exports both have to call a
# pragma: no cover line either covered or missing, having no word for "deliberately excluded" — and this extension does have one, the same flag its LCOV_EXCL markers set, so a Python project's pragmas now mean here what they mean in coverage.py's own report. GCC coverage, meanwhile, arrived only after a trip through lcov or gcovr -x: a second tool run to convert data GCC had already produced. gcov --json-format --stdout and gcovr --json emit near-identical documents, and one parser now reads both.
Fixed
- Comments are read in the language the file is written in. The non-code scanner was written for C and applied to every file, which was correct while every format this extension read described a C-family language. It is not any more: a C scanner meeting
# don't sees the apostrophe open a string, never sees it close, and counts the comment line as code — a number quietly slightly wrong, with nothing to say so. It is now # for Python, Ruby, shell, Perl, R, Elixir and Julia; all three forms for PHP, which takes all three; and // with /* */ everywhere else, which stays the default for an unknown extension — #define is code, and excluding it would be wrong in the other direction.
0.2025.29 · build 4 — Every Setting, Explained
2024-10-15
Added
- Every
fuciniCoverage.* setting now has a place in that panel, not just the thirteen compliance ones. All fifty-five are grouped into the same sections, in the same order, as the published settings reference, so the page you configure from and the page you read from agree about where a setting lives. A search box filters by name, key or description; a jump list moves between sections. The form is derived from the extension's own packaged manifest at runtime — types, defaults, bounds, enums and every word of prose — so nothing about a setting is written down twice and the panel cannot drift from the extension it edits.
- The descriptions follow the language you pinned. VS Code resolves a manifest's localization from the editor's Display Language when the extension loads, and offers no way to override that per extension — so setting
fuciniCoverage.language to German in an English editor translated everything except the settings. Because the panel reads and resolves those files itself, it is the one place that can honour the pin.
- The widgets a JSON schema cannot ask for. Nothing in a settings schema marks a string as a path, so the native UI cannot offer a picker for one; the three settings that hold a directory now get a Browse… button.
coverageFile.paths and pathRemap get real list editors instead of a raw JSON box, and each colorScale colour gets a swatch beside its hex.
Changed
- Saving writes what you changed, and nothing else. With thirteen settings it hardly mattered that Save wrote all thirteen; with fifty-five it would mean one edit stamping the whole namespace into a
settings.json that had two lines in it. Only settings whose value actually moved are written, and a value equal to the shipped default clears its override rather than restating what the extension already ships.
- Your licence key always goes to your user settings, and the row says so.
license.key is declared application-scoped, which means VS Code only ever reads it from there — writing it into a workspace is a save that appears to work and changes nothing. Reset now clears all fifty-five keys at the chosen scope rather than thirteen, so it asks first. And an edit made outside the panel no longer costs you your place: the page compares before it repaints, so pressing Save no longer scrolls fifty-five rows back to the top.
0.2025.28 · build 3 — Thirteen Rows, One Decision
2024-10-12
Added
- The compliance mode, the seven threshold gates and the six composite-score weights now have one editor. They are thirteen unrelated rows in the Settings UI and a single decision in practice: a threshold is inert unless the mode in force consults it, and a weight is inert unless the weighted gate is the one deciding. Fucini Coverage: Configure Coverage Compliance & Thresholds..., on the gear button in the Coverage view's title bar, groups the three, explains each mode where the mode is picked, and names the ways a gate can be configured into doing nothing — a strict mode with no per-metric threshold set, a weighted mode whose threshold is
0, weights that are all 0 — as warnings, while you are looking at them.
- It previews the verdict, and writes through VS Code. The COMPLIANT / NON-COMPLIANT the unsaved form would produce against the loaded run is computed by the extension's own compliance engine rather than by a second copy of the rules, because a preview that can disagree with the gate is worse than no preview. Changes go through the ordinary configuration API into
settings.json — user or workspace, your choice — so VS Code formats the file and keeps whatever comments are in it, and an edit made by hand or in another window flows back into the open form, whether or not a run is loaded. No licence is needed: Community gets the same editor Pro does.
Fixed
- The update notification's button read
Download && Install. The doubled ampersand is the mnemonic escape, which is correct in package.json titles and on modal dialogs — but a notification's action buttons take their label as plain text, so both characters were printed exactly as written, in all 17 locales, to everyone who was offered an update.
0.2025.27 · build 7 — A Trade You Opt Into
2024-09-30
Changed
- Telemetry is off by default. It shipped defaulting to on, on the reasoning that the evaluation trial wanted the data. That reasoning stopped applying once the Community tier made switching telemetry on the thing that unlocks every Pro feature for free: a trade only means something if the person makes it, and a trade nobody opted into is not a trade. Nothing is sent until you turn it on, and Community keeps working with its standard feature set either way.
- The telemetry setting says what it is for, and links the rest. Its description used to enumerate every field a ping carries, which meant two copies of that list — one in the setting, one in the privacy policy — that had to stay identical forever and did not. It now says what switching it on gets you and links to the privacy policy for the field list, so there is one list to keep true instead of two.
- Installing this release re-opens your evaluation window, once. Anyone whose trial had already lapsed gets a fresh 14 days, because this release changes the terms they evaluated under: a trial spent with telemetry on by default was not a trial of the product now being offered. It is keyed to a one-time token rather than to the version, deliberately — resetting on every release would mean the trial never ends for anyone who keeps up to date, which is not a trial but an unpriced licence with extra steps. The token is spent on first activation and the ordinary rules resume. A paid licence is untouched, since key verification returns before the trial is ever consulted, and Community is unaffected because that edition has no trial to re-open.
- A licence key no longer costs a questionnaire. The activation prompts, the licence-key setting and the trial-ended dialog all presented a key as something issued in exchange for answering questions. It is not: the key is issued automatically on request, and the feedback questionnaire is voluntary and entirely separate.
Fixed
- Three places still promised a trial "through 2026-12-31". There has never been such a date in the product — the trial is 14 days, counted per install from first use. Two earlier releases caught this claim in the settings descriptions and in the README; it survived in this page's own evaluation callout, in the User Guide, and in the licence tooling's README. The User Guide was the worst of the three: it stated the window applied "for every install, worldwide, at once (not a per-install countdown)", which is precisely backwards, and said coverage rendering is paused when the trial ends when in fact the extension settles onto the free Community tier.
- Telemetry notification emails were sent from the wrong domain. The
From: header was a mailbox on fucini.consulting while the server sending them is fucini.software — the alignment the setting's own comment warns about, and a reliable way to have mail spam-filtered or rejected outright.
0.2025.26 · build 2 — Greyed, Not Gone
2024-08-27
Changed
- A file the report names but that is not on disk is shown where it belongs, greyed out, and says why. 0.2025.25 stopped inventing locations for such files, which was right, but it left them holding a relative path that is not a location at all — so the tree still made a project out of the first segment of it, and a submodule's three deleted sources sat in a phantom
app beside the submodule they had actually come from. The mismatch was visible, which was the point, but it was visible in the wrong place and said nothing about what was wrong.
- It now appears in the project it was measured from, in the folder the report puts it in — drawn in the theme's middle grey and in italic instead of the ordinary foreground. Hovering it gives the path it would have had and explains that nothing is there, most likely renamed or deleted after the report was generated, and that its numbers are still counted. Nothing is dropped and no total moves: the row is the report's own account of itself, and the greying is what stops it reading like a row that opens.
- The guess stays inside the view. The file's recorded path is still exactly what the report said, because everything else is keyed by it — the external-report enrichment matches against it, telemetry relativises it, the annotated view opens it — and handing those a presumed location would trade something known for something invented. Only the tree joins the two, and only for a path that names no location of its own: an absolute path that simply is not there keeps what the report said and is greyed where it stands.
0.2025.25 · build 4 — Where It Was Measured
2024-08-07
Fixed
- A submodule's coverage lands where the submodule actually is, and its rows open. A git submodule builds its own report beside its own tree, and gcovr writes that report saying
<source>.</source> — "the directory this file is in" — with filenames like app/Application.cpp beneath it. Nothing anchored that . to anything: paths were resolved once, after the merge, by which point the run no longer knew which of its inputs any file had come from, so the only base left to try was the workspace root. Probing <workspace>/app/Application.cpp found nothing, every path stayed relative, and the Coverage view drew them as a project called app — named after the first segment of a path that was never a location — whose every row copied and opened somewhere no file was.
- Paths are resolved per tracefile now, before the merge. A relative
sourceRoot is anchored to the directory its tracefile sits in, so . beside a submodule's coverage.xml means that submodule. When that does not find the sources, the search walks up from the tracefile — the ordinary CMake layout writes coverage several levels down inside build/, and which of the two shapes a project uses is recorded nowhere in the report, so it is answered from the filesystem rather than from configuration. The walk stops at the workspace root: going past it would let a bare src/main.c resolve into a sibling checkout that happens to share a layout, which is a wrong answer that looks like a right one. A path already absolute and present is left exactly as it was, so ordinary lcov runs are untouched.
- A submodule stays its own project in the Coverage view. Files now resolving inside the workspace would otherwise have been folded into the workspace's own tree — the submodule's folders interleaved with its parent's, and its numbers added to the parent's total as though one codebase had been measured twice. Each file records the root it was measured from, and the view groups by that, so a submodule keeps its own heading and its own totals even though it sits inside the folder that is open. A run whose paths were already absolute has no such root and groups exactly as before.
- A file the report names but that is gone is still reported missing. It is not given a plausible path it is not at: a report naming files that have since been deleted is a stale report, and drawing them at invented locations would hide that. Each one is named in the output channel under
FUCINI_REMAP_001, with the path it would have had.
0.2025.24 · build 3 — Why Your Seat Went
2024-07-27
Changed
- A seat that disappears now says why. Every revocation showed the same line — your seat in this plan is no longer active — which reads as an accusation aimed at the wrong person. A team subscription reduced below the seats in use now releases the surplus after a grace period, and whoever loses a seat did nothing except be the one who had not used it lately. Where the licensing server has something useful to say, the extension says it: a seat released to fit a reduced subscription explains exactly that and points at whoever manages the subscription, with Request new key still offered — because the way back is real, and the pool has room the moment that person frees a seat.
- A subscription that has ended offers no button. A new key cannot be issued from a lapsed subscription, so the one action on that notification could only ever fail — which reads as a broken extension rather than an ended subscription. It now says what happened and stops there. Any reason this build does not recognise keeps the general wording: the licensing server is deployed continuously and an installed extension is not, so meeting an unfamiliar reason is the ordinary case rather than the edge one — it never surfaces a raw identifier, and it never goes quiet. Both new messages are translated into all seventeen locales.
0.2025.23 · build 5 — Out of the Extension
2024-07-17
Added
- The run can now leave, in six more formats. Until this release the only ways out were Fucini JSON, which nothing but the extension reads, and an HTML report, which nothing reads mechanically at all. Export Coverage Report... opens a format picker and writes the active run as a CSV per file (covered/total/percent for lines, branches, functions, MC/DC, regions and instantiations, worst-covered first, with a
TOTAL row), a CSV per function (hit count, whether it ran, uncovered branches inside it, MC/DC pairs), a Markdown summary to paste into a pull request, an LCOV tracefile, Cobertura XML, SARIF, or a self-contained SVG badge for a README. It sits in the Command Palette, in the Covered Files view title bar beside Generate Full HTML Report, and in the status-bar quick menu once quickActions.exportReport is on.
- It is a converter now, not only a viewer. The extension already reads lcov, llvm-cov JSON, Cobertura, JaCoCo, Istanbul/nyc and SimpleCov; with the LCOV and Cobertura writers it can go the other way too. Load a JaCoCo report, or several reports merged into one run, and emit the single tracefile genhtml, Codecov, Coveralls and SonarQube all accept — or the XML Jenkins, GitLab and Azure DevOps ingest natively. Both round-trip through the extension's own parsers, and line counts are expanded per line so the
LF/LH written out agree with the percentage on screen instead of approximating it.
- Every writer says what it drops. All of them except the CSVs are lossy, and silently so: MC/DC, code regions and C++ instantiations have no representation in LCOV, Cobertura or SARIF, and Cobertura records how many of a line's conditions were covered but not which arm. Fucini JSON and the two CSVs remain the formats that carry everything measured. In the CSVs a metric the run does not have leaves its cells empty rather than zero — an empty cell says "this tool reported no branch data", a
0 would say "every branch was missed", and a spreadsheet cannot tell those apart afterwards. Anything a spreadsheet would otherwise execute as a formula — a field beginning =, +, - or @, which is every npm-scoped path — is quoted and defused before it is written.
- SARIF annotates the diff; the badge needs no network. Uncovered ranges are reported at level
note, not warning: uncovered code is information about the test suite, and code scanning treats a warning as something that ought to block — the gate that blocks is still Check Coverage Thresholds. The badge is drawn here rather than fetched from shields.io, so it works in an air-gapped or on-premises repository, and it takes its colour from the same colorScale as the gutters and the status bar. In the free Community edition the picker opens and both CSVs work, capped at 20 exports a day like Export Fucini JSON; the interchange writers are paid-tier, on the line this edition has always drawn — reading your own numbers is free, feeding a CI pipeline is not.
0.2025.22 · build 3 — Nothing to Measure
2024-06-24
Fixed
- A file with nothing in it to instrument is no longer reported as 100% covered. A declaration-only header — prototypes, a macro, include guards — has no function bodies, so the compiler emits no coverage mapping for it, and anything that divides zero by zero answers 100%. It sat in the tree in green, beside code that had really been tested. Both readers now take the tool's own totals as the file's account of itself —
LF/FNF/BRF in lcov, the summary block in llvm-cov JSON — and a file whose every total reads zero is left out of the run instead of scored. The per-line records beneath those totals are deliberately ignored: a macro's expansion is attributed back to the line that defines it while the count belongs to the line that uses it, in another file, and that borrowed line was the only reason such a header ever looked measured. Run totals lose it too, so the headline figure is a little lower and a lot truer. Every dropped path is named in the output channel, and a report in which every file is declaration-only keeps them all.
Changed
- Picking a coverage file adds to what is already loaded instead of replacing it. A build that writes one report per module has no single file to pin, and picking the second module's report silently unloaded the first. The dialog now takes several files at once, each pick adds to the set, and everything picked is merged into one run — drawn in the panel under each project's own name. The watcher follows all of them, and one report being rebuilt no longer takes the others off screen. Show Loaded Coverage Files ends with Unload the files you picked, which returns to ordinary discovery.
- Support has an address on the product's own domain:
support@fucini.software. Where activation cannot complete on its own, the license panel and the request-code message used to hand you a fucini.consulting address — a different domain from every other link in the extension, and one that reads as a different company. license@fucini.software is unchanged: it sends keys, it is not an inbox to write to.
0.2025.21 — Whose src Is This
2024-06-21
Added
- Every tree in the Coverage view says which project it belongs to. 0.2025.19 gave a merged run one named block per project, but an ordinary run still drew a bare tree — so a workspace cut back to its own folder opened on
src with nothing above it saying which src. Each tree now carries its root: the folders leading up to the project in the muted colour, the project's own folder after them in full. A tree cut back to the workspace folder still says where it was cut back to, and two codebases merged into one run are never read as one tree. It is a heading rather than a row — there is nothing there to expand, and the folders below it fold exactly as they did before. In a narrow panel the leading directories shorten first, so the folder that actually names the project is the last thing to go, and hovering shows the whole path.
0.2025.20 · build 7 — The Menu You Expected
2024-06-08
Added
- The Coverage view's rows have the Explorer's own right-click menu. Right-clicking a file or folder used to open the webview's Cut/Copy/Paste — three entries with nothing on the row to act on. It now opens VS Code's real menu: Open, Open to the Side, Open With…, Reveal in Explorer View, Reveal in File Explorer, Open in Integrated Terminal, Find in Folder… for a folder, and Copy Path / Copy Relative Path — with the forward-slash variants beside them on Windows, where the two forms differ. The
⋯ button and its menu are unchanged, and are the last entry in the new one.
Changed
- A metric your tracefile carries no data for is no longer judged. A threshold on MC/DC, with no MC/DC in the build, counted as a failure — so a file scoring 100% on every metric its build actually produced sat under a card reading "3 of 5 met", against a bar nothing in that tracefile could ever clear. Metrics with no data are now skipped everywhere a verdict is formed: the per-file status bar, the run-wide card, the annotated-source banner and the HTML report. Check Coverage Thresholds still names a missing metric explicitly, which is where that belongs.
- Files with nothing measurable in them are left out of the Coverage view. A header of declarations, a barrel of re-exports, a types-only module: no test you write will change their number, and opening one shows no metrics at all. They were listed with a dash in 0.2025.19; they are simply not listed now, and a folder left holding nothing goes with them. A run in which nothing is measurable still lists everything.
Fixed
- A drive letter could still head the tree. For coverage whose paths do not sit under the open workspace — a merged tracefile from another project, or a window with no folder open — the tree began at
W:, or on Linux and macOS at a row with no name at all: one row, holding exactly one child, expandable to no purpose. A filesystem root is never drawn now, whatever the paths look like, and every row still carries its real absolute path for the menu and the editor.
0.2025.19 · build 6 — Paths, Not Preamble
2024-05-16
Added
- A row menu on every file and folder in the Coverage view. The button at the end of a row copies that path in any of the four forms an editor, a shell or a prompt might want — absolute or workspace-relative, native separators or forward slashes — and offers Reveal in File Explorer and Open in Integrated Terminal, with a file opening the terminal in its own directory. The forward-slash entries are listed only where they differ from the native form, so the menu is two items shorter on Linux and macOS. Copying a path used to mean finding the same file a second time in the Explorer.
- Collapse-all and expand-all buttons above the tree. Collapsing everything and then opening the one folder you care about is the quickest way through a large tree, and it cost one click per folder before. What you collapse this way is remembered exactly like a folder you closed by hand, so a reload will not undo it.
- A merged run spanning more than one project now draws each project separately, under its own name. Merging a tracefile from a second codebase used to hang its folders off the same tree as the workspace's own, so
src appeared twice with nothing to say why, and the two projects' directories interleaved alphabetically. Each project now gets its own named block, separated by a rule, with the workspace's own first. A run covering a single project is unchanged and carries no heading — there would be nothing to tell it apart from.
Fixed
- A file with nothing executable in it was reported as 0% covered. A module that is only
export * re-exports, or only type declarations, compiles to nothing a coverage tool can measure — so the tracefile records no statements for it. The Coverage view scored that 0%, filing the file in red beside code that genuinely is untested, with nothing anyone could write a test for; the Explorer, meanwhile, badged the same file a green 100%, because an empty total reads as "all of nothing covered". Neither number was true and neither is shown any more: the panel row gets a dash and a dashed outline, with a tooltip saying why, and the Explorer badge is left off. A folder containing nothing but such files is unscored too. A file that is 0% of something it did measure still reads 0% — the dash means "nothing to measure", not "measured and missed". - Rows from a merged project pointed at the wrong place. Their paths were built from the workspace root rather than their own, so for a file outside the workspace the row menu copied — and "Reveal in File Explorer" opened — a path the file was never at. They now carry the root they actually came from.
Changed
- The Coverage view no longer draws the workspace root. The tree used to begin at the filesystem root and walk down through every folder above the project — the drive, then the directory holding your projects, then the project itself — before reaching the first folder that was actually yours. That was several rows of indentation, on every row, repeating what the window title already said. Paths now start at the workspace folder. The full absolute path is still what the row menu copies and what the editor opens.
0.2025.18 · build 4 — Stays Where You Put It
2024-04-30
Fixed
- Folders you collapsed in the Coverage view sprang open again on every reload. Watching is on by default, so a build that regenerates coverage re-renders the panel — and because every folder was drawn open, collapsing one lasted only until the next build. On a large tree that meant doing the same work several times an hour. The panel now remembers which folders you closed and leaves them closed. A directory that appears for the first time in a later run still shows up open, like any other: what is remembered is the deliberate act of closing one, not the default state.
- Switching telemetry off did not stop the uninstall ping. That ping is sent by a small standalone script VS Code runs when you uninstall. It has no access to the VS Code API and cannot read your settings, so the only thing that ever tells it not to send is the absence of a file the extension writes while telemetry is on — and that file was never removed when you switched telemetry off. Anyone who had it on and later turned it off would still have sent their OS username, hostname and home directory on uninstall. The file is now deleted the moment telemetry is off.
- A Pro renewal could report success for a key you had just replaced. The silent renewal read your configured key twice: once to decide whether to renew, and again afterwards to decide whether to say anything. Pasting a new key while a renewal was in flight meant the second read saw a different key, and the "your Pro subscription was renewed" message described the one that had been replaced. It now reads the key once.
Changed
- Every module in the extension is now covered by its own tests. For a tool whose whole job is to tell you what your tests actually reach, that seemed worth doing properly: 53 of the 54 source files sit at 100% on statements, branches, functions and lines, and the remaining one — the VS Code wiring layer — at 95% of its functions. Writing them turned up the faults above, along with twenty-odd guards that could never run: fallbacks standing behind checks that had already ruled them out, which read as safety nets while catching nothing. Those are gone rather than papered over with a test that could not fail.
0.2025.17 — Asked At A Moment Worth Answering
2024-04-27
Changed
- The optional feedback survey now waits until you have actually used the extension, and asks at a better moment. It used to count calendar days since installation, which measured how long ago you installed this rather than whether you had ever watched it work — so an install left untouched for a month was asked to rate a product it had never shown anything to. It now needs real coverage loads as well as elapsed time, and no longer appears while your editor is starting up: it waits until after a coverage load has succeeded. There is also a third and final invitation after a year, because two inside a single quarter permanently retired anyone who happened to be busy at the time.
- The Dutch translation is no longer formal. It addressed you as "u" throughout, which reads stiff for a developer tool and matched nothing else about how the product speaks. Every Dutch string now uses "je" — rewritten sentence by sentence, since the change carries the verb forms with it.
0.2025.16 — A Code, Not a Company
2024-04-23
Changed
- The activation form asked solo developers for a "company code". Buying Pro or Pro Plus as one person gives you a code from your own subscription — there is no company behind it, and being asked for one reasonably suggests you are on the wrong form. That field now reads Subscription code for Pro and Pro Plus, and stays Company code for Business and Enterprise, where a team genuinely does share one. The hints beneath it were already written per plan; only the label had stayed generic.
0.2025.15 · build 3 — Measured Against What You Asked For
2024-03-25
Fixed
- The generated HTML report judged your code against 100%, whatever thresholds you had set. The Status column tested every metric against a hidden 100, so a project that set its gates at 80 and met them saw a green COMPLIANT banner directly above a column of red crosses — for the same run, on the same page. The metric legend did the same. Both now use your configured thresholds, and where no threshold applies they show a muted dash instead of inventing a verdict.
- The compliance banner claimed more than it had checked. It always read "all configured coverage thresholds met", including in the default mode, which only tests the weighted score — so a run could assert every threshold had been met with one of them plainly failing in the table below. Each mode now says which rule it actually applied.
- The README said the extension "runs free through 2026-12-31". No such date exists in the product. An earlier release corrected that claim in the settings descriptions but missed the README — the first thing anyone reads on the Marketplace. It now says what really happens: a 14-day trial from first use, then the free Community tier, with no date after which anything stops.
0.2025.14 — Telling Them Apart
2024-03-15
Changed
- The two editions no longer look identical in the Marketplace. Pro and Community are built from one source tree, and until now that included the icon — so the two listings were the same picture with different captions, shown side by side in the same search results. Each edition now carries its own: the same artwork with a coloured band across the bottom, teal for Pro and slate for Community. The word is what tells them apart when the icon is shown large; in a search-result row the word is too small to read, so the colour does it instead.
0.2025.13 — Reachable From Where You Are Standing
2024-02-26
Fixed
- Opening the annotated view no longer stops you re-opening it. The annotated-source panel is a webview rather than a text editor, so while it had focus the extension could not see a "current file" — and running Open Annotated Source from there answered "open a file with loaded coverage first", to someone looking at the annotated view of a file that plainly has coverage. It now falls back to the file behind the panel you are looking at. The same block was also quietly skipping the re-check a second run exists to perform: when an llvm-cov HTML report is configured, running the command again is what switches the panel to that report's own page — so if your coverage build finished after you opened the panel, the way to pick it up was unreachable from the panel itself.
0.2025.12 · build 2 — Saying What Is True
2024-02-15
Added
- A paid Pro subscription now renews itself. Every activation key only lasts as long as the period actually paid for. The team plans pick up a renewed period when they next check in, but individual Pro never checks in — it works fully offline — so nothing was carrying a renewal to it, and a monthly subscriber's key would have run out roughly every month however faithfully they paid. The extension now asks for a fresh key quietly, only in the days around its own expiry and only when it holds the code proving a subscription paid for it. Being offline through that window costs you nothing: the key keeps working until it genuinely runs out, and the next attempt afterwards restores it.
- Activation from the purchase email is one click. The link hands the extension your plan and code directly, instead of opening VS Code, finding the command and pasting the code by hand. It always asks before it uses a seat. Entering the code yourself still works exactly as before — the link is a shortcut, never the only way in — and it works in the Community build too.
Fixed
- A settings description claimed the extension "runs free through 2026-12-31". There is no such date in the product. What actually happens is a 14-day trial counted from the first time you use it, after which the extension settles onto the free Community tier. The description now says that, in every language.
- The threshold settings no longer cite the release that changed their defaults — a version number in the middle of a sentence that answers a question nobody reading a settings description is asking.
- Several translated descriptions named a command that no translated VS Code shows. They referred to the activation command by its English name, so anyone not working in English was sent looking through the Command Palette for a string that is not in it.
0.2025.11 · build 3 — Counting What Counts
2023-12-26
Fixed
- A project with no MC/DC decisions could show 0% overall coverage. Some tools report an MC/DC section even when there is nothing in it to measure, and that was being read as "0% covered" rather than "nothing to cover". Because the overall score drops to zero as soon as any metric reads zero, a fully covered project could show 0%. A metric with nothing to measure is now left out of the score entirely, and its row is no longer shown at all instead of appearing as "0 / 0 conditions covered".
- The licence status is now translated in all 17 languages. It stayed in English everywhere, and because it appears inside sentences that were already translated, the result was a half-translated line in the status bar, in notifications and in the Covered Files panel.
- The Annotated Source view and the coverage hovers are now translated too — the compliance breakdown table, the navigation buttons and their tooltips, and the branch and MC/DC detail shown on hover.
Changed
- A paid Pro subscription can now be activated with its own code. The plan list in the activation request gained a Pro entry, so the code that arrives with a Pro subscription finally has somewhere to go. Individual Pro keys still work fully offline and never contact the server after activation.
- The activity-bar icon is larger and easier to see next to the built-in icons.
- Switching to the Community edition now installs it from fucini.software. It tried the Marketplace before, where the Community edition is not published, so the switch could leave you with no extension installed at all.
0.2025.10 · build 5 — A Softer Landing
2023-12-08
Changed
- A lapsed subscription now settles onto the free tier — when an individual activation key ran out, the extension kept asking you to renew indefinitely. Choosing not to renew is a normal end to a subscription, not a fault, so it now lands on the permanent free Community tier instead. Team plans are deliberately unaffected: an expired Pro Plus, Business or Enterprise key still reports as expired, because there an administrator has to renew it and quietly dropping the whole company to the free tier would hide that.
Fixed
- Turning telemetry off is now respected in every case. One code path could skip the check when the extension's internal detailed-diagnostics mode was on. That mode is switched off in every published build, so no release ever behaved this way, but the check is now unconditional.
- Settings apply immediately instead of waiting for a reload. Editing path remapping, comment exclusion, git-diff filtering or the excluded-file totals now reloads coverage straight away — path remapping especially, since that is the setting you reach for precisely because paths don't line up.
- "Unlock Full Features" and "Switch to Community" could stop halfway without saying so. Both now run to completion.
- Your activation key stays bound to the same machine identity. That identity could previously be regenerated between sessions on some setups, which invalidated an already-issued key.
0.2025.9 — Complete Translations
2023-12-02
Added
- Complete translations across all 17 shipped languages — roughly a hundred of the newer interface strings, including the activation and licensing dialogs, the optional feedback survey, the Community unlock flow, and the self-update notifications, were still falling back to English in every non-English bundle. All 17 languages now cover the extension's full runtime interface.
Changed
- Housekeeping pass over source comments, build-script documentation, and the packaging file list. The set of files shipped in the extension is unchanged; no functional or behavioural changes.
0.2025.8 — Editions Can't Conflict Anymore
2023-11-20
Fixed
- Installing both the Pro and Community editions at once could crash one of them on startup — they register the same commands and the same coverage-files view. Now detected up front: if the other edition is also installed, activation stops cleanly with a message telling you to uninstall one, instead of an unexplained activation failure.
0.2025.7 — Housekeeping
2023-10-30
Housekeeping release; no functional changes.
0.2024.6 — Community Tier & Team Plans
2023-10-17
A permanent free tier, team plans that share a seat pool, and a separate Community edition of the extension.
Added
- A permanent, free Community tier — instead of a hard stop once the 14-day trial ends, the extension now settles into Community: every feature except MC/DC, a limited monthly taste of the Full HTML Report and the persistent Annotated Source views, generous daily caps on a handful of automatable actions, and everything else unlimited.
- Unlock every feature for free — a new "Unlock Full Features" command trades detailed usage telemetry for a full, unrestricted grant, reversible any time. An optional in-app feedback survey rewards a 30-day full-featured key instead, no strings attached.
- Team plans: Pro Plus, Business, and Enterprise — these share a seat pool (a company code plus a seat count) instead of one key per machine, with a background check-in that keeps a seat's grant current and flags a revoked or reduced seat automatically.
- Fucini Coverage Studio Community — a separate, dedicated listing built from the same source, with the paid-tier trial/activation/team-plan code genuinely absent rather than just hidden. See the download page.
Changed
- Full HTML Report generation and the persistent Annotated Source views (plain and side-by-side) are now paid-tier features in the Community baseline — only the in-place toggle view remains, capped monthly as before. Unlocking still grants all of it.
- A usage-limit warning on the Community tier now offers both ways forward in one prompt: view paid plans, or unlock via telemetry.
Fixed
- Detailed, potentially-identifying telemetry (OS username, home directory, CPU/RAM specs, network interfaces, and the full report/source content) can now only ever be sent by Community or a telemetry-gated free grant — never a paid Pro/Pro Plus/Business/Enterprise license, regardless of local telemetry settings.
- An install already running an earlier build no longer gets handed a fresh trial window just because it upgraded — it now correctly lands on the Community tier if its old trial had already run out.
- The update notification, download progress, and error messages now show the plain
Major.Year.Minor version (matching what VS Code's Extensions view shows) instead of leaking the internal build number, e.g. 0.2024.6 instead of 0.2024.6.2.
0.2024.5 — Fine-Tuned License Keys
2023-09-03
Activation keys can now cap individual features separately, and license admins can hand-issue a finely-tuned key from the dashboard.
Added
- Per-feature usage caps — activation keys can now cap each feature/action independently (for example, HTML report exports at 1/month) instead of one shared coverage-load limit. Existing keys keep working unchanged.
- Manual key issuance — license admins can now hand-issue a key for a request code received outside the in-editor questionnaire, picking exactly which features to grant, an expiry, and per-feature caps, with the key emailed automatically when an address is given.
0.2024.4 — Update Feed Reliability
2023-07-24
Fixes a caching bug that could hide new releases from users behind certain corporate proxies.
Fixed
- Self-update feed reliability — the update manifest is now served with a strict no-cache header, so a caching proxy between fucini.software and the reader can no longer serve a stale copy that hides new releases.
0.2024.3 — Minor Fixes & Performance
2023-07-13
A small maintenance release: a few minor bug fixes and performance improvements.
Fixed
- Bug fixes — minor bug fixes and stability improvements.
- Performance — small improvements to coverage loading and rendering.
0.2024.2 — Automated Activation
2023-06-20
Activation keys are now issued and applied automatically — no more waiting on an email.
Evaluation release. Every install gets a free 14-day trial with everything unlocked, counted from first use; after that it settles onto the free Community tier rather than stopping. A license key is issued automatically on request. Learn more →
Changed
- Automated activation — requesting a key now submits a short questionnaire and applies the returned key to your settings automatically, no email round-trip.
- Faster renewals — a renewal request only asks usage/wishes questions; your contact details from a prior request are reused, never asked again.
- Fixed evaluation window — the trial now runs through 2026-12-31 for everyone, replacing the previous 14-day-from-install countdown.
- Localized activation flow — the renewal questionnaire and activation confirmations are now translated into all 18 supported languages, same as the rest of the extension.
0.2024.1 · build 3 — Smarter Update Checks
2023-06-02
The extension now checks both fucini.software and the VS Code Marketplace for updates, always offering whichever release is newer.
Fixed
- Dual-source update checker — now queries both fucini.software and the VS Code Marketplace, and offers whichever release is newer.
0.2024.0 — Initial Release
2023-04-26
First public release of Fucini Coverage Studio — native VS Code coverage that ingests industry-standard tracefiles, normalises them into one canonical model, merges runs deterministically, and reports through VS Code's built-in Test Coverage API.
Added
- Multi-format ingestion — LCOV, llvm-cov JSON, Cobertura XML, JaCoCo XML, Istanbul/nyc and SimpleCov JSON, auto-detected.
- Deterministic merge — several tracefiles combined into one run with recorded provenance, stable regardless of load order.
- Native coverage UI — line/branch/function gutters, overview ruler and the built-in Test Coverage panel.
- MC/DC coverage — per-decision hover breakdown and an optional coloured margin gutter.
- Composite score — a SIL-weighted Weighted Harmonic Mean across lines, branches, functions, regions, MC/DC and C++ instantiations.
- Compliance verdict — COMPLIANT / NON-COMPLIANT gating with
strict, whm, both and either modes.
- Path remapping, diff coverage, Explorer badges and a run-wide status bar.
- In-editor analysis — function CodeLens, branch hover, dead-code warnings, stale-coverage warnings and Next/Previous Uncovered.
- Covered Files view, HTML reports, history & trends, watch mode and Fucini JSON export.