Skip to content
| Marketplace
Sign in
Visual Studio Code>Testing>Fucini Coverage Studio ProNew to Visual Studio Code? Get it now.
Fucini Coverage Studio Pro

Fucini Coverage Studio Pro

Fucini Consulting

|
18 installs
| (4) | Free
Did those tests actually exercise your code — including the ones your AI assistant wrote? MC/DC and branch coverage expose tests that only walk the happy path, dead-code detection finds functions nothing ever called, and diff coverage ties it all to the change you just made. Native VS Code coverage
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Fucini Coverage Studio Pro

Did those tests actually exercise your code? Native VS Code coverage for TypeScript, JavaScript, C, C++, Java, Ruby and anything else that can emit a tracefile: gutters and overview ruler, a Covered Files view, per-function CodeLens, branch & MC/DC hover, diff coverage, a composite safety-weighted score, Annotated Source, and self-hosted HTML reports — all driven from the tracefiles your build already produces. Reads lcov, llvm-cov JSON, Cobertura, JaCoCo, Istanbul/nyc, SimpleCov, Clover, OpenCover, coverage.py, gcov/gcovr JSON and plain-text .gcov, Go coverage profiles, Coveralls JSON, SonarQube generic coverage, GNATcoverage (Ada), BullseyeCoverage, Visual Studio .coveragexml, Xcode's xccov report, VectorCAST, Testwell CTC++, Lauterbach TRACE32, EMMA, UCIS/UCDB (VHDL/Verilog), NCover, dotCover, and PIT and Stryker mutation reports.

Fucini Coverage Studio in VS Code: a file opened from the Coverage view, uncovered lines in the editor, the MC/DC hover, the heatmap, the status-bar card and the annotated source

One workspace, every project in one view. A multi-root workspace with C, C++, .NET, TypeScript, JavaScript, Python and Visual Basic in it: the Coverage view holds them all — collapse the tree and you read eleven projects' coverage on one screen, open a folder and you are at its files, open a file and you are on its lines.

The Coverage view over a workspace of eleven projects: every tracefile listed, the tree collapsed to one row per project with its percentage and bar, scrolled through, expanded again, and a file opened from it

On the command line too. The same reader as this extension, free, for the places an editor is not — a CI gate, a badge, an HTML report on a build server, an MCP server for an agent: npm install -g fucini-coverage, then fucini-coverage check coverage/lcov.info --lines 80. See fucini-coverage on npm.

The command line on the demo: --help, summary, check with two gates passing, export as Markdown, compare against a baseline, badge, report and the MCP server's help

30-second quick start

  1. Install, and open your project.
  2. Command Palette → Fucini Coverage: Run Tests with Coverage. It recognises vitest, jest, pytest, go, cargo, dotnet, maven, gradle, phpunit and ctest, runs your tests with coverage on, and loads what they write. Already have a tracefile? Fucini Coverage: Pick Coverage File loads it and remembers it.
  3. Open a source file. The gutter shows which lines ran, the status bar the file's percentage, a hover each line's hit count — and the run is in the Coverage view and in VS Code's own Test Coverage panel.

From then on it reloads by itself whenever the tracefile changes. Bars on uncovered lines only, a hit-count heatmap and inline ×N counts are a setting away (editorGutter.mode, hitCounts.inline).

The same six steps, with a button for each, are the Get started with Coverage Studio walkthrough: Help → Welcome → Walkthroughs, or Command Palette → Welcome: Open Walkthrough…. It opens by itself after the install.

A short tour

The user guide has every feature with its settings; these are the ones you meet first.

Gaps in the editor. A red bar on a line that never ran, amber on a line with a branch arm never taken, the execution count after each line. Alt+N / Alt+P jump between the gaps.

Uncovered lines and half-taken branches marked in the editor

CodeLens and hover. Above every function: its coverage, and its complexity and CRAP score where the report carries them. On a decision: the branch arms and the MC/DC test vectors.

The MC/DC hover on a decision, under a function CodeLens

Heatmap. Every executed line, warmer the more often it ran — on dark and light themes.

The hit-count heatmap

Every metric the reports carry. Lines, functions, regions, branches, MC/DC and instantiations, and function calls, object code and a mutation score when a report brings them — merged onto the same file and judged against your thresholds. Object-code coverage is the unusual one, because it measures the built binary rather than the source: what it tells you that statement coverage cannot, and the three ways to get it off an embedded target, are in OBJECT-CODE-COVERAGE.md — which ships with the extension — and in User Guide §11a.

Four reports merged onto one source file

Mutants, where they are. A mutation score says 71%; the editor says which injected fault on which line no test noticed — from PIT and Stryker reports, with the change itself and the tests that ran it on hover.

Surviving mutants marked in the editor and listed in the annotated source

Condition coverage. The level between branches and MC/DC, as a metric of its own for the reports that carry it (BullseyeCoverage, TRACE32, GCC 14). For GCC 14's -fcondition-coverage the line itself is marked, and the hover says which condition was never seen true or false — from gcov's JSON (gcov --json-format --conditions) and from its text listing alike. The demo's c-gcc14 sample is a smoke alarm built that way: every branch taken, and one operand of each decision never seen one way.

One decision and the same two tests: branches 2 of 2, conditions 3 of 4, MC/DC 1 of 2

Thresholds that fit your project. The defaults suit an ordinary project (lines 80, functions 80, regions 75, branches 70); fuciniCoverage.threshold.profile: "safety" puts every gate at 100.

Switching the threshold profile in the settings panel

Annotated source and HTML reports. The whole file colour-coded with its findings in place, and a self-hosted report to share.

The annotated source view

Every metric beside every file. The Coverage view's columns are always there, heaviest-weight first, with an em dash where a metric does not apply to that language rather than a gap you have to interpret. Hide the ones you do not use; the order stays.

The Coverage view: C, C++, JavaScript, Python and .NET merged into one tree, MC/DC, branches, regions, functions, lines, instantiations, mutation score, CRAP and the weighted score in columns beside each file

The build finished while you were reading code. A watched tracefile reloads without asking, and the Coverage icon says how many reports moved while you were somewhere else. Open it and the count is gone; the tooltip names them, so three reports rebuilt is one number and not three interruptions.

The Coverage icon in the activity bar carrying a count of the tracefiles that reloaded, with a tooltip naming them

Which tests reached this line. Hover a covered line and the hit count names the tests behind it — the answer to "can I change this?" that a percentage never gives you.

The hover on a covered line naming the tests that reached it

Straight to the next gap. Next Uncovered and Next Partly-Tested Decision walk the findings without scrolling and squinting.

Jumping between uncovered lines with a keystroke

Updates come from us, and you can be early if you want. The extension checks fucini.software for new builds. Open the licence panel in the Coverage view and you can volunteer for the pre-release channel, which is offered builds before they reach the Marketplace. They are tested, but they are the builds we are least sure of — you would be volunteering to find what we missed, and leaving puts you back on the ordinary path at the next stable release.

The licence panel: pressing Test pre-release builds joins the channel, and an update bar appears offering a build the Marketplace does not have yet

Especially when an AI wrote the tests

A language model will write you a test suite in seconds, and it will look thorough. The question it cannot answer for you is whether those tests reach anything that matters — and line coverage cannot answer it either, because a test with no assertions at all still lights up every line it touches.

Three things here answer it:

  • MC/DC and branch coverage show whether each decision was actually exercised, not merely whether the line was reached. Generated tests characteristically walk the happy path and leave the other side of every if untouched — which reads as 100% line coverage and 50% branch coverage, and that gap is the finding.
  • Dead-code detection lists the functions nothing ever called. A generated suite names them in its test titles and never reaches them.
  • Diff coverage ties all of it to the change in front of you, so "the tests pass" becomes "the code you just wrote is tested".

None of this proves a test is good — nothing can. It tells you, precisely and per line, what was never run, which is the part a human reviewer cannot hold in their head and an AI will not volunteer.

And the agent can ask. The coverage on screen answers six questions an agent has while it writes tests, in words it can act on:

Tool What it answers
coverage_summary The run's totals per metric, and the files furthest from covered.
coverage_gaps What never ran in a file, a function or a range of lines: the uncovered lines, the branches with a side not taken, the MC/DC conditions without their pair, the mutants that survived.
tests_for_line Which tests ran a line, where the report says (a per-test run).
patch_coverage The coverage of a diff — the working tree against a ref, or a patch handed over — and its lines that never ran.
coverage_delta What moved since the run before: the totals, and the files that gained or lost.
test_run_command The command that runs this workspace's tests with coverage on.

VS Code's own agent has them as tools with nothing to set up (#coverageGaps in a prompt names one). Every other agent — Claude Code, Cursor, any MCP client — reaches them through an MCP server the extension runs when fuciniCoverage.mcp.enabled is on: Copy MCP Server Configuration puts the entry for the client's mcp.json on the clipboard — as VS Code and Visual Studio read it; a client that names the outer key mcpServers (Claude Code, Cursor) takes the same entry under that name. The server listens on 127.0.0.1 only, answers only a call that carries your token, and refuses a call a web page sent; it reads the run that is loaded and writes nothing. The numbers are the ones on screen — the same exclusions, the same remapping — so an agent told "cover the gaps in parse_header" and a reviewer looking at the gutter are talking about the same lines.

By Mario Fucini — Fucini Consulting

Full documentation: User Guide · Notice · License · Contact


The generated multi-page HTML coverage report, showing MC/DC expression detail

The generated report. buffer_can_write is covered by every other metric and fails MC/DC on 0 of 3 conditions — the gap MC/DC exists to find.

How it compares

VS Code itself and several free extensions show coverage too. This is what each one's own documentation says it does, as of September 2026 — where a README does not mention something, the table says so rather than guessing.

Fucini Coverage Studio VS Code, built in Coverage Gutters Code Coverage (markis) Gcov Viewer
Where the coverage comes from 25 tracefile formats, plus PIT and Stryker mutation reports a testing extension that reports coverage lcov and XML files lcov files GCC's .gcda files, through gcov
Uncovered lines in the editor ✓ ✓ ✓ ✓, and in Problems ✓
Branch coverage, per arm ✓ when the testing extension reports it not described not described not described
MC/DC ✓ (Pro) — not described not described not described
Several tracefiles merged, and which one covered each line ✓ — several files supported not described not described
Reloads when the tracefile changes ✓ shows the last test run ✓ not described not described
Diff coverage, thresholds and history ✓ coverage shown in the diff editor not described not described not described
HTML report generated, self-hosted — previews an existing one not described not described

It is not either/or with VS Code's own view: every load is published to the native Test Coverage panel as well, filter by test included. Something here out of date? Say so on the contact page.

How licensing works. You get a 14-day trial with everything unlocked, counted from the first time you use it — once per machine, not per install: a second VS Code profile continues the same trial. After that the extension settles onto the free Community tier: it keeps working, with a narrower feature set, for as long as you like — there is no dead end and no date after which it stops.

To lift that, either upgrade to Pro, or run Fucini Coverage: Unlock Full Features to trade detailed usage telemetry for the full feature set for 30 days, once per machine. Running Fucini Coverage: Show License / Activation shows where you stand and every way forward from there. If activation ever fails, send the request code it shows via the Contact page or support@fucini.software instead.

Editions: Community and Pro

Two listings, one codebase. Community is the permanently free tier; it is also what the Pro listing settles onto after its 14-day trial. Pro (and the trial) is everything, uncapped. The line is drawn so that the free tier always sees every finding and only pays for convenience, proof and scale — a gate you cannot see past teaches nothing about what it is missing.

Capability Community (free) Pro, and the trial
All 25 formats, multi-file merge, path remapping ✓ ✓
Gutters, overview ruler, Coverage view, CodeLens, hovers (Explorer badges optional) ✓ ✓
Watch mode with automatic reload; Run Tests with Coverage ✓ ✓
Diff coverage, history and trend, since-last-run marks ✓ ✓
Loading coverage by command 25 / day (automatic loads are not counted) unlimited
Compare runs, Check Coverage Thresholds, Open HTML Report, Export Coverage Report, Export Fucini JSON 5 / week each unlimited
Annotated Source, in place 5 / week unlimited, plus a persistent side pane and Generate Full HTML Report
Export Coverage Report CSV (per file, per function), SVG badge also Markdown, LCOV, Cobertura XML, SARIF
A file that no longer matches its coverage reported and dimmed also the line, function or checksum that proved it
Surviving mutants marked on the line, with how many also what each mutant changed
Tests that ran a line how many also their names
CRAP Max CRAP of the run, CRAP on every function also the ranked Riskiest Methods list
MC/DC coverage, gutter bars and hover — ✓
A file measured by several reports the newest report also choose one per file, or combine them (agree / union)
Object-code coverage scored and gated its presence is announced ✓
Telemetry required no — optionally, and once per machine, Unlock Full Features trades detailed usage telemetry for 30 days of everything in the right column no

A week runs seven days from the first use that opened it, so the message at a limit names the day the count starts again. Nothing that only reads coverage is counted. An install that was already in use when the weekly allowance arrived (0.2026.83) keeps its previous caps for 30 days.

Not ready for a subscription? A 7-day pass is everything in the right column for a week, bought once, with nothing to cancel — redeemed with Fucini Coverage: Enter Subscription Code, in either edition.

The same code activates Fucini Coverage Studio for Visual Studio, 2022 and 2026 — there under Tools → Coverage Studio → Licence…. One machine is one seat: activate VS Code or Visual Studio, and the other editor on the same machine follows by itself, with nothing to enter twice.

Fucini Coverage: What Community Includes shows this list inside VS Code, with the current counts against each cap and the day each one resets. Plans, the pass and prices are on the pricing page.

Coming next: the same coverage in Azure DevOps. A pipeline extension that reads every one of these 25 formats, for every language, and shows the result as a Coverage tab on the build — the numbers, the tree and the gates you see here, at the end of the pipeline that produced them. Follow the changelog for the first release.

Requirements

  • VS Code ≥ 1.88.0 (matches engines.vscode in package.json).
  • A coverage tracefile your project already produces — see Supported formats below.

Supported formats

Format Typical file
LCOV lcov.info, *.info
llvm-cov JSON llvm-cov export --format=json output
Cobertura XML coverage.xml
JaCoCo XML jacoco.xml
Istanbul / nyc coverage-final.json
SimpleCov (Ruby) .resultset.json, coverage.json (simplecov-json gem)
Go coverage profile coverage.out (go test -coverprofile=coverage.out)
Clover XML clover.xml (PHPUnit / php-code-coverage, Istanbul's clover reporter)
OpenCover XML coverage.opencover.xml (Coverlet -p:CoverletOutputFormat=opencover)
coverage.py JSON coverage.json (coverage json, Python)
gcov / gcovr JSON gcov --json-format (.gcov.json.gz), gcov --json-format --stdout, gcovr --json
Coveralls JSON coveralls.json (excoveralls, coveralls-python, goveralls, cargo-tarpaulin --out Coveralls, …)
SonarQube generic coverage any tool that emits SonarQube's <coverage version="1"> import format
GNATcoverage XML (Ada) gnatcov coverage --annotate=xml — the per-source *.xml files
BullseyeCoverage covxml output (the binary .cov database needs covxml first)
Visual Studio .coveragexml MSTest / vstest (the binary .coverage needs dotnet-coverage convert first)
Xcode xccov JSON xcrun xccov view --report --json (Swift / Objective-C — file and function totals, no gutters)
gcov plain-text listing *.gcov (gcov without --json-format — for toolchains older than GCC 9)
VectorCAST its Cobertura export, with the MC/DC pair counts and function-call coverage that plain Cobertura drops
Testwell CTC++ the textual coverage listing (ctcreport)
Lauterbach TRACE32 COVerage.EXPORT — statement, decision, condition, MC/DC, function, call and object-code coverage
EMMA XML (Java) emma report -r xml — the legacy Java format that predates JaCoCo
UCIS / UCDB Accellera <UCIS> and Aldec ux:ucdb — VHDL / Verilog / SystemVerilog
NCover XML (.NET) Coverage.xml — also written by tools that were never NCover
dotCover DetailedXML (.NET) dotcover report --reportType=DetailedXML (the plain Xml rollup has no line numbers)
PIT (mutation) mutations.xml — Java, Kotlin, Scala
Stryker (mutation) mutation-report.json — JavaScript, TypeScript, C#, Scala

Multiple tracefiles are merged deterministically into one run with recorded provenance. A file measured by more than one loaded tracefile takes its numbers from whichever tracefile has the newest mtime on disk — a module's own rerun replaces what a wider, older run said about it rather than adding to it, regardless of where either path sits in coverageFile.paths. The Coverage view shows what that did: every file row names the tracefile its numbers came from, the badge is outlined where those numbers overruled an older report, each tracefile row counts how many of its files were overruled or overruled others, and clicking a tracefile row highlights its files in the tree (fuciniCoverage.coverageView.mergedMarks turns the outline and the highlight off). Mutation reports carry a score and no coverage, so loading one beside a real tracefile is the intended use.

When the newest is not the right answer, coverageFile.sources decides per file (paid plans): take one report — llvm-cov's MC/DC over a GCC cross-check — or combine them. "combine": "union" counts a line covered where any report ran it, for several test suites of one build; "combine": "agree" only where every report did, for the same code built by two compilers, and marks in teal the lines they disagree on. The Coverage view's row menu, Choose Coverage Source..., offers the reports and both combinations for the file, its folder or a pattern.

Gzipped tracefiles load as they are — gcov --json-format writes .gcov.json.gz by default, and there is no need to decompress it first. 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 of the formats above may arrive gzipped.

Is this coverage still about this file?

Coverage read from a tracefile describes the source as it was when the tests ran. Nothing in most formats says so, which is how a green gutter ends up describing a version of a file that no longer exists.

Every file in the Coverage panel is therefore in one of three states:

Meaning
✓ Verified. The tracefile records what each measured line said, and it still matches.
(nothing) Not disproved. Nothing contradicts the coverage, and nothing confirms it either. The ordinary state.
⚠ Contradicted. Something in the file itself disproves its coverage; the row is dimmed and the tooltip says what.

Only two formats can earn the tick, because only two record anything about the source they measured:

  • lcov written with --checksum (geninfo --checksum, off by default) puts an MD5 of each line beside its hit count — its documented purpose is "to prevent attempts to combine coverage data from different source code versions", which is exactly this question. Worth turning on.
  • GNATcoverage XML, always: --annotate=xml embeds the text of every line it measured.

The other thirteen say nothing about the content of the source, so a file read from them can never be more than not disproved — and this extension will not pretend otherwise.

The warning does not need either. It fires on contradictions any format can produce: coverage recorded past the end of the file, a named function that is now somewhere other than where the report puts it, or executed lines that are blank in the file. Timestamps are never enough on their own — a git checkout moves every mtime without changing a byte — so a moved timestamp is at most a reason to look again, never a finding. The check runs at load and again whenever a file is saved or changes on disk, so the mark follows the file: an edit that contradicts the run sets it, a checkout that restores the measured version clears it. The hover says exactly what proved it — the line as it reads now against what the report recorded, where the report kept the text.

Switch all of it off with fuciniCoverage.staleWarning.enabled: false.

Unmeasured versus uncovered

A file the run never mentions is unmeasured: no test loaded it, so no tool wrote a line for it. That is not the same as uncovered — measured, with zero hits — and this extension has never scored an absent file as 0%. What it did not do, until 0.2026.69, was show the absent ones at all: a module with no tests whatsoever never appeared in the tree, the totals or the threshold check, and a reader saw "92%" for a project half of whose files had never been exercised.

Now, with a run loaded, the per-file status-bar item says not measured for a source file of the run's own kind that the run does not contain — "of the run's kind" meaning it has one of the file extensions the run measured, so a C project's headers-and-sources are candidates and its Python scripts are not. Everything beyond that is opt-in through fuciniCoverage.unmeasured.mode:

  • off (default) — nothing else changes.
  • list — the Coverage view gets an Unmeasured group at the bottom naming every such file, each row opening it; the load result counts them; and fuciniCoverage.threshold.unmeasuredFiles can fail Check Coverage Thresholds when there are more than a number you set. No percentage moves.
  • count — everything list does, and each unmeasured file also counts against the run as an uncovered file of its own code lines, so the status bar, the thresholds, the badges, history and every export show the lower number. The file's rows are dimmed and marked ⊘ wherever they appear, its gutter bars say "not measured" rather than "never ran", and the mismatch check leaves it alone — there is no measurement for its contents to contradict.

Header files are the known false positive in C: they have the run's extension family and no tracefile will ever name one, because on its own it compiles to nothing. fuciniCoverage.unmeasured.exclude therefore leaves out **/*.h, **/*.hpp, **/*.hh, **/*.inl and **/*.d.ts by default, in the same glob syntax as fuciniCoverage.ignore; add a platform port that is not built here, or generated stubs, and they drop out of the list, the count and the status-bar state. A file fuciniCoverage.ignore names is never called unmeasured either — leaving it out was the point.

Ada (GNATcoverage)

gnatcov coverage --annotate=xml writes a directory, not a file: an index.xml that pulls in one <source>.xml per unit by xi:include. Load the per-source files — several at once, or point discovery at them:

"fuciniCoverage.coverageFile.paths": ["xml-report/*.adb.xml", "xml-report/*.ads.xml"]

Loading index.xml itself says so rather than failing obscurely, and a report whose includes have already been resolved (xmllint --xinclude) loads whole.

GNATcoverage states obligations rather than execution counts, so a satisfied line reads as one hit. A partially covered line (!) is a covered line with an uncovered decision — the shortfall shows up in branch and MC/DC coverage, where it belongs. Exemption regions (pragma Annotate (Xcov, Exempt_On)) become excluded lines, and an undetermined obligation (?) counts as uncovered, never as covered. With --level=stmt+mcdc, MC/DC coverage is counted per condition and feeds the MC/DC gate and the composite score.

.NET on MSTest / vstest

Coverlet's OpenCover and Cobertura output is read directly. For Visual Studio's own binary .coverage file, convert it first:

dotnet-coverage convert -o coverage.coveragexml TestResults/…/x.coverage

Partially covered lines survive the trip: the line counts as executed and the part of it that never ran shows up as branch coverage.

BullseyeCoverage

Bullseye's .cov database is binary; run its own covxml first, and load what that writes:

covxml -f test.cov > coverage.xml

Bullseye's headline metric is condition/decision coverage, not line coverage, and the two stay separate here: line coverage comes from the <block> records and from probes that were reached, while C/D coverage becomes branch coverage. Probe kinds are not all two-outcome — a switch-label, catch, try or for-range-body is reached or not, and is scored out of one rather than two. Function probes become per-function coverage, matching Bullseye's own separate fn_cov/fn_total.

Swift and Objective-C (Xcode)

xcrun xccov view --report --json is the one coverage document an iOS or macOS build produces with no extra tooling, and it loads directly:

xcodebuild test -scheme MyApp -enableCodeCoverage YES -resultBundlePath MyApp.xcresult
xcrun xccov view --report --json MyApp.xcresult > coverage.json

You get per-file percentages, a declaration per function with its real execution count, and everything built on those — the Coverage view, CodeLens, the worst-covered rankings.

It carries no per-line data, and this parser does not invent any. xccov reports how many of a file's lines are executable and how many were covered, but not which; that answer lives in the coverage archive, reachable only one source file at a time (xccov view --archive --file <path>), in a document that does not say which file it describes. So the gutters stay empty for these runs — guessing consecutive lines from each function's start would paint confident hit counts onto blank lines and comments, and a reader cannot tell an invented gutter from a measured one.

The .xcresult bundle itself is still not a tracefile; point the commands at it, not the extension. For line-level Swift coverage, produce a format with real per-line data instead:

xcrun llvm-cov export -format=lcov \
  -instr-profile Build/.../Coverage.profdata Build/.../MyApp > lcov.info
xcresultparser -o cobertura MyApp.xcresult > coverage.xml   # Cobertura
xcresultparser -o xml       MyApp.xcresult > coverage.xml   # SonarQube generic
slather coverage --cobertura-xml --scheme MyApp MyApp.xcodeproj

Features

Core

  • A Coverage view of every measured file, and in the editor's own gutter only what is worth a mark by default: MC/DC bars, since-last-run triangles, edited-line dots. Opt in to more with fuciniCoverage.editorGutter.mode — uncovered only (a bar on every line that never ran, an amber one where a branch was never taken, nothing on covered lines), coverage (those, plus a green bar on every covered line: the file in red, amber and green) or a hit-count heatmap (how often each line ran, not whether it is covered — it has no green) — and to inline hit counts (×12 after each line) with fuciniCoverage.hitCounts.inline. fuciniCoverage.lineHighlight tints the lines themselves instead of (or as well as) the gutter, and fuciniCoverage.overviewRuler decides the scrollbar stripe on its own — with the gutter off, that is a map of the file's gaps down the scrollbar and nothing at all beside the code.
  • Annotated Source — a side-by-side or in-place coverage-highlighted view of a single file, rendered by the extension itself.
  • VS Code's own Test Coverage view is fed too. Every load publishes the run through the editor's Test Coverage API, so the Test Explorer's Test Coverage panel, the per-file percentages it adds to the Explorer and the editor's Show Inline Coverage toggle all show the same numbers as the Coverage view — whatever tracefile they came from, and with no test adapter involved. Open it with Show in Test Coverage View (or the Test Explorer's coverage tab); fuciniCoverage.testCoverage.publish turns the automatic publishing off. When a tracefile names its tests (lcov's TN: sections), each test is an item under Fucini Coverage in the Test Explorer and the view can filter a file's coverage by test — the lines that test reached.
  • Per-line statement, branch, function, region, MC/DC and C++ instantiation coverage (whatever the tracefile provides).
  • Multi-format ingestion and deterministic merge of several runs.
  • Path remapping — CI build-machine paths → your local workspace.
  • Diff coverage — decorate only lines changed vs a git base ref.
  • A run-wide status-bar item ($(dashboard) NN.NN%), hover for a full metrics table, and — for anyone who wants them — Explorer badges (the composite score per file and folder in whole points, truncated, with the decimals in the tooltip), off by default so the Explorer stays as its owner left it. Every percentage shows two decimals and is truncated, never rounded: 100.00% means exactly 100, and the gates judge the exact value.
  • Watch mode — auto-reload when a tracefile changes, when a configured file appears outside the workspace, or when a task named in fuciniCoverage.watch.reloadAfterTasks ends.
  • Remote tracefiles — a coverageFile.paths entry may be an https:// URL: the CI artifact is fetched (conditionally, with the headers you configure) and loaded like a local file, and optionally re-fetched on a timer.
  • Variables in paths — ${workspaceFolder}, ${workspaceFolderBasename}, ${userHome}, ${env:NAME}, ${command:id} and ${config:section.key} in every path setting. ${command:cmake.buildDirectory}/coverage.info follows whichever CMake kit is selected: commands run before each discovery, for at most five seconds each, and only in a trusted workspace.
  • Multi-root workspaces — every folder is a project of its own. Discovery runs per folder from that folder's own settings (a folder's .vscode/settings.json, or its entry in the .code-workspace file), and the tracefiles of every folder are loaded and merged, exactly as several coverageFile.paths entries under one folder are. ${workspaceFolder} in an entry means the folder that entry is read for, and ${workspaceRoot} in a pathRemap rule means the folder the tracefile lives in (any other folder that has the file is tried after it). The Coverage view draws each folder under its own name. The settings marked per folder in the tables below take a folder-level value: coverageFile.*, watch.*, remote.*, pathRemap, ignore, externalHtmlReport.dir, htmlReport.outputDir, gitDiff.*, badge.*, editorGutter.mode, lineHighlight, overviewRuler, hitCounts.inline and problems.perLine. Whatever needs exactly one folder — a dialog's default folder, the badge, the report directories, Run Tests with Coverage — takes the active editor's, and the test run asks which folder when there is no editor to say.
  • Progress you can cancel — a large load or a full HTML report runs under a progress notification with a Cancel button; a small one only spins the status bar.

Composite score & compliance

  • A single Weighted Harmonic Mean score across lines, branches, functions, regions, MC/DC and instantiations, with weights aligned to IEC 61508 / ISO 26262 / DO-178C tiers (fuciniCoverage.whmWeights.*).
  • COMPLIANT / NON-COMPLIANT verdict driven by per-metric thresholds and a configurable fuciniCoverage.compliance.mode (strict, whm, both, either).

Excluding non-code lines

  • Comment-only and blank lines are auto-excluded from totals (handles a known llvm-cov/gcov quirk where a stray hit count lands on a comment). The comment syntax follows the file's own language — # in Python, Ruby and shell, all three forms in PHP, // and /* */ elsewhere.
  • Honors LCOV_EXCL_LINE / LCOV_EXCL_START / LCOV_EXCL_STOP markers — and writes them: select the lines and take Exclude Selection from Coverage from the light bulb or the context menu, and the marker goes in with the file's own comment sign; on a .NET class, method or property it is [ExcludeFromCodeCoverage] above the member instead.
  • Whole files nobody intends to test — vendored, generated, third-party — are left out of the run by fuciniCoverage.ignore glob patterns (vendor/**, **/*.generated.ts), so the totals, gates and exports never count them; the load result says how many were dropped and the output names each one.
  • The Coverage view's search box finds rows by name or path — every word typed has to be there — or by a comparison against the figures: < 80% on the score, lines >= 90, branches = 0, mcdc != 100, or both at once (invoice < 80). Methods in the namespace perspective and the risk hotspots are rows too, so functions are found where the run has them. Filtered in the page as you type; a folder holding a match opens for it and closes again when the box empties; Escape clears it.
  • For a .NET report that names its classes (Cobertura, Coverlet, OpenCover) the Coverage view's Folders / Namespaces dropdown draws the same run by namespace, class and method, each with its own numbers; a method row opens the file at the method. fuciniCoverage.coverageView.perspective keeps the choice.
  • Exclude from Coverage on a row of the Coverage view — right-click, or More Path Actions… — or Exclude This File from Coverage on the open file writes that file, or the folder's /**, into fuciniCoverage.ignore in the folder's settings, once, and the reload takes it out of every total and every page; the pattern is there to be taken out again.
  • For .NET sources two settings read off the code what the report cannot say. fuciniCoverage.ignore.attributes (ExcludeFromCodeCoverage by default) excludes the class, method or property below an attribute it names, as LCOV_EXCL_START/STOP would, whatever tool wrote the report — add GeneratedCode, Obsolete or your own. fuciniCoverage.ignore.namespaces leaves out every file whose namespace declaration matches a prefix, * as the wildcard: Acme.Generated, Acme.*.Migrations, *Tests.
  • Branches only. LCOV_EXCL_BR_LINE and LCOV_EXCL_BR_START … STOP take the branches on their lines out of the count and leave the lines in it; LCOV_EXCL_EXCEPTION_BR_LINE only the arms an exception reaches. Every marker is read under gcovr's GCOVR_EXCL_ and GCOV_EXCL_ prefixes too.
  • Justified exclusions. An exclusion can say why — the text after the marker, or the Justification of [ExcludeFromCodeCoverage] — and Export Coverage Report… lists every exclusion with its reason, as Markdown or CSV. fuciniCoverage.exclusions.requireReason makes one without a reason a warning at its line and a failed gate.

In-editor analysis

  • Function CodeLens — per-function coverage %, click to jump to its first uncovered line.
  • Branch & MC/DC hover — per-arm taken/not-taken counts and per-decision condition/vector breakdowns; optional coloured MC/DC margin gutter.
  • Dead-code warnings — Problems-panel warning for functions with measurable lines and zero coverage. With fuciniCoverage.problems.perLine on, every block of uncovered lines in an open document is a Problems entry too, with a squiggle on the lines themselves.
  • Next/Previous Uncovered navigation commands.
  • Per-file status bar — the active editor's own coverage percentage.
  • Pass or fail on the marks — with the runner's results file beside the report (TRX or JUnit XML), a line that a failing test ran gets a red bar, and its hover names the failing tests.
  • GCC 14 condition coverage, line by line — which outcome of which condition is missing, marked on its line and named in the hover.
  • The Coverage view follows the editor — the open file is highlighted in the tree and scrolled into view.

Finding what to test next

  • Show Worst-Covered Files / Folders / Functions — Quick Pick ranked lowest-first; a folder pick reveals it in the Explorer, where its badges say which child is the reason.
  • Copy Uncovered Lines — Markdown summary of every gap, ready to paste into a PR description.
  • Run Tests Affected by My Changes… — the tests that ran a line the working tree changes, the one that ran most of the change first; open one, or run only those.
  • Show Minimal Test Set… — the fewest tests that still run every line the suite runs, and the tests that add no line of their own.
  • Run Mutation Tests Here — whether a test would notice: the function the caret is in, mutated by Stryker.NET, StrykerJS or PIT, and the report loaded beside the coverage on screen. The demo's .NET sample ships such a report, with the surviving mutants of Quote.cs marked from the first open.
  • Run App with Coverage… — what a person reaches, not only what the tests do: the .NET app under dotnet-coverage, loaded when it closes. The demo's dotnet-app sample is a console app with no tests, for exactly this.
  • Show Indirect Coverage Changes… — the lines nobody edited whose coverage moved, against the CI baseline or the run before.

History & reporting

  • Optional local run history with a coverage trend (▲/▼) in the status bar, plus Compare Two History Runs and Compare with Last Run.

  • A gate on new code — fuciniCoverage.threshold.newCode asks that the lines changed against a ref (a branch, a commit, a tag) are tested, whatever the project's total; gitDiff.author and gitDiff.since make it the lines one person wrote, or the lines written since a day.

  • The CI baseline: where am I against main? fuciniCoverage.baseline.report names the report the build server publishes for main, and Compare with CI Baseline holds the loaded run against it, file by file, in the same words as Compare with Last Run; after every load the status bar's card carries the one-line delta (vs CI baseline: 84.20% → 85.10% (+0.90%), 3 file(s) moved). Three ways to name it, in .vscode/settings.json:

    1. A URL the CI serves. GitLab keeps a branch's last job artifacts at a stable address; the token stays out of the settings file through ${env:…} in remote.headers:
      {
        "fuciniCoverage.baseline.report": "https://gitlab.example.com/api/v4/projects/42/jobs/artifacts/main/raw/coverage/lcov.info?job=test",
        "fuciniCoverage.remote.headers": ["PRIVATE-TOKEN: ${env:GITLAB_TOKEN}"]
      }
      
      Jenkins: https://jenkins.example.com/job/app/job/main/lastSuccessfulBuild/artifact/coverage/lcov.info. TeamCity: https://tc.example.com/repository/download/App_Main/.lastSuccessful/coverage/lcov.info with Authorization: Bearer ${env:TC_TOKEN}. The URL is fetched and cached exactly as a coverageFile.paths URL is (ETag, remote.refreshMinutes).
    2. A file the pipeline copied onto a share — an absolute path such as \\build\drops\app\main\coverage.cobertura.xml.
    3. A downloaded artifact kept under the folder, say ci/main.lcov.info (put the folder in .gitignore) — a path relative to the folder.

    An artifact is a zip, and that is read too. GitHub and GitLab hand an artifact over as an archive, whatever was put in it. The report inside is named after a #:

    {
      "fuciniCoverage.baseline.report": "https://api.github.com/repos/acme/app/actions/artifacts/1234567/zip#coverage/lcov.info",
      "fuciniCoverage.remote.headers": ["Authorization: Bearer ${env:GITHUB_TOKEN}"]
    }
    

    GitLab's whole-job download is the same: https://gitlab.example.com/api/v4/projects/42/jobs/artifacts/main/download?job=test#coverage/lcov.info. The member is found by its path or by the end of it (#lcov.info finds coverage/lcov.info); without a # it is the archive's only file, or its only file that looks like a report, and where there are several the output names them. The same works for a zip on disk (ci/artifact.zip#lcov.info) and for a URL in coverageFile.paths. The token goes to the server it was set for and no further: the redirect to the storage that holds the archive is followed without it.

    Any format the reader knows will do. A path the CI wrote relative to its checkout (src/Billing/Invoice.cs) is placed under this folder before the diff, so it meets the file the local run resolved; an absolute path from another machine matches nothing, so have the pipeline write relative paths (--source-root, or Coverlet's default). A baseline that cannot be fetched or read is a line in the output, never a failed load.

  • Since last run, in the gutter — a red triangle on every line that was executed in the previous run and is not in this one, a green one for the reverse, with a stripe in the overview ruler and the hit counts on hover. The baseline is the run loaded before this one, or the newest run in history on a window's first load.

  • Edited buffers — every mark, hover and CodeLens moves with the text as you edit, and withdraws from a line you have rewritten, where a small grey dot says why; they come back with the next run.

  • Generate Full HTML Report — a self-rendered, multi-page, per-directory report; Open HTML Report / Open Annotated Source for a single file.

  • Export Fucini JSON — the full normalised model.

  • Export Coverage Report… — the same run as CSV (per file or per function), a Markdown summary, an LCOV tracefile, Cobertura XML, SARIF, or a self-contained SVG badge. Load anything the extension reads and emit LCOV or Cobertura from it: the six parsers plus these writers make it a converter.

  • Generate Coverage Badge — write that badge straight into the workspace (badge.outputPath, default coverage-badge.svg), showing lines, branches, MC/DC or the composite score, coloured by your own colorScale.*; with badge.autoUpdate on it is rewritten after every load. Embed it in a README with ![coverage](https://www.fucini.consulting/coverage-badge.svg) — no hosted service, no shields.io round trip, so it works in an air-gapped repository too.

  • Mismatch warning when a source file's contents no longer fit its run — kept current as files are saved or change on disk.

Commands

Command Palette (Ctrl+Shift+P) → type Fucini Coverage:

Command What it does
Load Coverage Discover & load coverage for the workspace. Alt+Shift+R (Ctrl+Alt+R on macOS).
Pick Coverage File Choose tracefiles — one, several at once, or a further one each time; picks add up, are merged, and are written to coverageFile.paths so they load by themselves from then on.
Show Loaded Coverage Files List the tracefiles in the active run. The Coverage view lists them too, each with an unload button and a pick button beside them.
Toggle Tracefile Watch Watch the tracefile(s) for changes and reload automatically.
Run Tests with Coverage Recognise the test runner from the workspace root, run it with coverage on in a terminal task, load what it writes, and remember that path in coverageFile.paths.
Forget the Test Runner Put the "which test run?" question back, for a workspace where more than one runner is recognised.
Run App with Coverage… The app itself, under coverage, for testing by hand: the folder's .NET app (an Exe, a WinExe or a web project; a pick where there are several) is started in the terminal under Microsoft's dotnet-coverage — dotnet-coverage collect -f cobertura -o coverage/app/coverage.cobertura.xml dotnet run --project … — and runs until you close it; what you walked through is then loaded like any run. In a coverage session (Start Coverage Session) it is added to what the tests ran. Needs dotnet tool install --global dotnet-coverage.
Run Mutation Tests Here Whether a test would notice this code being wrong: the function the caret is in — or the lines selected, or the file — is mutated by the folder's mutation runner, and the report is loaded beside the coverage on screen, where the surviving mutants are marked. Stryker.NET for C# (dotnet stryker --mutate "**/Quote.cs{512..918}", aimed at the test project that reaches the file's project), StrykerJS for JavaScript and TypeScript (npx stryker run --mutate "src/cart.ts:12-30"), PIT with Maven for Java (the whole class: PIT narrows no further). A run of one function takes a minute where the project's takes an hour. Also on the editor's context menu.
Copy MCP Server Configuration What an MCP client (Claude Code, Cursor, …) needs to ask about the coverage on screen: the server's address and your token, as the entry of an mcp.json, on the clipboard. With the server off (fuciniCoverage.mcp.enabled), the offer to turn it on.
Run This Test with Coverage What is under the caret — a test method, the test class or describe the caret is in, or the file — run by the folder's recipe narrowed to it (dotnet test --filter, jest/vitest -t, a pytest node id, go test ./pkg -run "^TestX$", cargo llvm-cov -- "mod::tests::x" --exact), then loaded like any run. Read off the text: C#, VB, F#, JavaScript, TypeScript, Python, Go and Rust. Also on the editor's context menu.
Run Tests Affected by My Changes… The tests that ran a line the working tree changes against fuciniCoverage.testImpact.baseRef (HEAD), read off the report's per-test lines and git's diff, the one that ran most of the change first. A test that only ran lines the change moved down or up is listed too — its report would name the wrong lines otherwise. Pick a test to open it; the first entry runs them — one dotnet test per affected class, where the reports came from a per-test run (testRun.perTest) — and loads their reports beside the rest.
Show Minimal Test Set… The fewest tests that still run every line the suite runs — a greedy cover of the report's per-test lines — and under them the tests that add no line of their own. Each says what it adds and how many lines only it runs; the list goes to the output too. Redundant by lines: a test that adds none may still be the only one that checks something. The test you pick opens where it is written.
Exclude Selection from Coverage Take the selected lines out of the count where they are written: [ExcludeFromCodeCoverage] above a .NET class, method or property, LCOV_EXCL_LINE at the end of one line, LCOV_EXCL_START/STOP around several — in the file's own comment sign. Also the light bulb on a selection or an uncovered line, and the editor's context menu. Never doubles a marker that is there.
Show Tests That Ran This Line The tests the report names for the caret's line — or the file, when the line names none — busiest first; the one you pick opens where it is written in the workspace. Needs a report that says which test ran what: coverage.py contexts, OpenCover, an lcov tracefile written per test, or a testRun.perTest run. Also on the editor's context menu.
Next Uncovered / Previous Uncovered Jump between uncovered regions. Alt+N / Alt+P by default (Ctrl+Alt+N / Ctrl+Alt+P on macOS); remap under Keyboard Shortcuts.
Next Partly-Tested Decision / Previous Partly-Tested Decision Jump between decisions some of whose arms ran and some of which never did — the gap uncovered-line navigation cannot show, since a line whose statement ran stays covered however many of its branches were never taken. Needs a report that records branches.
Reload Coverage Re-read the tracefiles already loaded, in place — including ones picked by hand, which discovery would not find again.
Choose Editor Gutter Mode... Off, uncovered-only bars, red-amber-green bars, or a hit-count heatmap in the editor's gutter — for this session; editorGutter.mode in the settings is what the workspace opens with.
Cycle Editor Gutter Mode One step on through off, uncovered, coverage and heatmap — Alt+G, or the mode button in the status bar (statusBar.showModeToggle) — for this session, like the picker.
Choose Coverage Source for This File... When more than one loaded report measures the active file, pick which one its numbers come from — each report is offered with its own figures — and whether the choice covers this file, its folder or a pattern. Writes a coverageFile.sources rule to the workspace.
Clear Coverage Source for This File... Take out the workspace's coverageFile.sources rules for the active file, so it goes back to coverageFile.mergeStrategy.
Exclude This File from Coverage Put the active file's workspace-relative path into fuciniCoverage.ignore in the folder's settings, so it leaves every total and every page at the reload. The Coverage view's rows have the same as Exclude from Coverage, where a folder row writes its folder/**.
Toggle Coverage Painting The gutter bars and the line highlight off and on together, from the editor title bar — for reading the code as written, then reading the coverage again. Turning them back on restores the modes each was last on. For this session, like the picker: the settings keep what the workspace opens with.
Toggle Inline Hit Counts Show or hide ×N execution counts after each line — flips hitCounts.inline.
Show in Test Coverage View Publish the active run to VS Code's own Test Coverage panel (even with testCoverage.publish off) and open it.
Suggest Path Remap Rule Infer the pathRemap rule that would match the active run's unmatched files to the workspace, and offer to add it.
Open HTML Report Open the rendered static report.
Generate Full HTML Report Build the multi-page report.
Open Annotated Source / Open Annotated Source to the Side Annotated coverage view for a file.
Toggle Annotated Source / Show Source Swap between source and annotated view. Alt+A (Ctrl+Alt+A on macOS).
Show Worst-Covered Files / Show Worst-Covered Folders / Show Worst-Covered Functions Ranked lowest-first Quick Pick; the folder one reveals the folder in the Explorer.
Copy Uncovered Lines Markdown summary of every uncovered range.
Copy Coverage Summary The whole run as a Markdown table, worst-covered file first, with a totals row — for pasting into a pull request. Branch and MC/DC columns appear only when the report measured them.
Compare Two History Runs / Compare with Last Run Per-file regression diff.
Compare with CI Baseline The loaded run against the report fuciniCoverage.baseline.report names — the main branch's from CI, a URL or a downloaded artifact — file by file, as Compare with Last Run lists them; the status bar's card carries the one-line delta after every load.
Show Indirect Coverage Changes… The lines whose coverage moved though nobody edited them: code that stopped running, or started, outside the lines the working tree changes against fuciniCoverage.gitDiff.baseRef. Against the CI baseline where baseline.report names one, else since the run before this one. Listed per file, in the output as well; the file you pick opens at its first such line.
Start Coverage Session / End Coverage Session While a session is on, every run loaded is added to the one before it as a union — the unit tests, then the integration suite, then a walk through the UI — and the gutter and the tree show what they reached together. A bar over the tree says how many runs since when, with the way to end it.
Check Coverage Thresholds Evaluate the compliance gates; with compliance.diagnostics on, the failures also stay in Problems.
Configure Coverage Compliance & Thresholds... Set the gates themselves, per metric.
Tune Colour Scale Try colorScale.* colours and thresholds live against the open Coverage view, then keep or revert.
Export Fucini JSON Export the current run as Fucini JSON.
Export Coverage Report… Pick a format — CSV (files/functions), Markdown, LCOV, Cobertura XML, SARIF or an SVG badge — and a destination.
Generate Coverage Badge Write the SVG badge for the active run to badge.outputPath, showing badge.metric, and offer to open it.
Show Status Bar Menu Open the quick-action menu.
Browse for External HTML Report Directory... / Browse for Full HTML Report Output Directory... Pick the folders those two settings name.
Enter Subscription Code Bought a subscription, or been given a seat? Paste the code from the email — the plan is known from it — and the licence key is issued and applied for you. No questionnaire. The same code activates Coverage Studio for Visual Studio on the same machine, or the other way round: one machine is one seat.
Request Activation Key Ask for an evaluation key — the short questionnaire. Also where a FCB1-… code can be redeemed alongside the form.
Enter Activation Key / Copy Activation Request Code Paste an FCK1.… key you were sent by hand, or copy the code support asks for.
Check for Updates Ask the update feed what the current version is.
Feedback Survey Open the survey in a browser.

CodeLens, branch/MC-DC hover, dead-code warnings, badges and the status-bar items all work automatically once coverage is loaded.

Keyboard shortcuts. The four defaults — Alt+N, Alt+P, Alt+A and Alt+Shift+R (Ctrl+Alt+… on macOS) — clash with nothing VS Code binds itself, but Alt+A and Alt+P are popular with other extensions. If one of yours already uses them, rebind under File → Preferences → Keyboard Shortcuts (search Fucini Coverage); the commands work from the palette and the status-bar menu either way.

Settings

Configure in the Settings UI (search Fucini Coverage) or settings.json. Every option, with its default:

Prefer a file you can copy straight into your own settings? Download the complete settings reference — the same options listed below, as commented JSONC with every one of them at the default the packaged extension uses, so leaving a block as-is is identical to not setting it at all. It is regenerated from this extension on every release and names the version it came from in its header comment.

Discovery & loading

Setting Default What it does
fuciniCoverage.coverageFile.defaultLocation "" Base folder for discovery and file-pick dialogs. Variables: ${workspaceFolder}, ${workspaceFolderBasename}, ${userHome}, ${env:NAME}, ${command:id}, ${config:section.key}. Per folder.
fuciniCoverage.language auto Language for the extension’s own text: auto follows VS Code’s display language.
fuciniCoverage.coverageFile.paths [] Tracefile(s) to load & merge, relative to the base folder, absolute (the same variables as above), or an http(s):// URL fetched on every load — add #lcov.info (or another file name) as the URL fragment when the URL itself has no extension. A relative * glob recurses under the base folder; an absolute entry may hold a wildcard too (${workspaceFolder}/build/coverage-merged/*.lcov.info, ** for any depth), searched under the folders before it. Per folder.
fuciniCoverage.coverageFile.rememberPicks true Remember every picked file, and every report a test run writes, for this workspace — kept for you, not written into the settings file.
fuciniCoverage.coverageFile.sources [] Which loaded report a file's numbers come from, when more than one measures it: an ordered list of { "files": glob, "report": glob } rules — e.g. llvm-cov's coverage.json (with MC/DC) for everything and a GCC gcov.info for a few files. The first rule whose files match and whose report measures the file decides. With "combine": "union" (covered where any report ran the line) or "agree" (covered only where every report did; the disagreements marked in teal) the matching reports are combined instead of one being picked. Paid plans. Per folder.
fuciniCoverage.coverageFile.mergeStrategy newest For files no rule decides: newest (the report written last wins) or order (the later entry in coverageFile.paths wins, whatever the times). Per folder.
fuciniCoverage.watch.enabled true Reload coverage when a tracefile changes — or appears, for an absolute entry outside the workspace. Per folder.
fuciniCoverage.watch.reloadAfterTasks [] Task names (label, or source: name like npm: test) whose ending reloads coverage, whether or not watch.enabled is on. Per folder.
fuciniCoverage.remote.headers [] Name: value headers sent with every URL fetch; variables expand, so Authorization: Bearer ${env:CI_TOKEN} keeps the token out of settings.json. Per folder.
fuciniCoverage.baseline.report "" The report to hold every loaded run against — the main branch's, as the build server publishes it: a URL (fetched with remote.headers and cached, as a coverageFile.paths URL is) or the path of a downloaded artifact, absolute or relative to the folder. The status bar's card then says where this run stands against it, and Compare with CI Baseline lists the files that moved. Empty: no baseline. Per folder.
fuciniCoverage.remote.refreshMinutes 0 Re-fetch URL entries every N minutes (conditional GET; 0 = only on load). Per folder.

Colour scale & badges

Setting Default What it does
fuciniCoverage.colorScale.preset default colorblind: vermillion, orange and blue (Okabe–Ito) instead of red, amber and green — for the gradient, gutter bars, line highlight, Explorer badges and since-last-run marks. A colour you changed below keeps precedence.
fuciniCoverage.colorScale.lowColor #8b1a1a Low end of the coverage gradient.
fuciniCoverage.colorScale.midColor #b8860b Pivot colour of the gradient.
fuciniCoverage.colorScale.highColor #228b22 High end (100%) of the gradient.
fuciniCoverage.colorScale.lowThreshold 80 % at which the badge steps low → mid.
fuciniCoverage.colorScale.highThreshold 90 % at which the badge steps mid → high (and gradient pivot).

Composite score (Weighted Harmonic Mean)

Setting Default What it does
fuciniCoverage.whmWeights.lines 1 Weight for line/statement coverage.
fuciniCoverage.whmWeights.functions 1 Weight for function coverage.
fuciniCoverage.whmWeights.regions 1.5 Weight for region coverage (llvm-cov).
fuciniCoverage.whmWeights.branches 2 Weight for branch coverage.
fuciniCoverage.whmWeights.mcdc 4 Weight for MC/DC coverage.
fuciniCoverage.whmWeights.conditions 3 Weight for condition coverage, between branches and MC/DC.
fuciniCoverage.whmWeights.instantiations 0.5 Weight for C++ template instantiations (llvm-cov).
fuciniCoverage.whmWeights.functionCalls 1 Weight for function-call coverage (VectorCAST, TRACE32, Cantata).
fuciniCoverage.whmWeights.objectCode 0 Weight for object-code coverage (TRACE32). Off by default: it subsumes statement coverage and has its own gate.
fuciniCoverage.whmWeights.mutation 0 Weight for the mutation score (PIT, Stryker). Off by default: it is not coverage and has its own gate.

Thresholds & compliance

Setting Default What it does
fuciniCoverage.threshold.profile standard standard | safety. What a threshold you have not changed stands for: the defaults below, or 100 for every gate bar the mutation score (the defaults up to 0.2026.81). A threshold you changed wins over the profile.
fuciniCoverage.threshold.statement 80 Minimum statement %. 0 disables the gate.
fuciniCoverage.threshold.branch 70 Minimum branch %.
fuciniCoverage.threshold.mcdc 0 Minimum MC/DC %.
fuciniCoverage.threshold.conditions 0 Minimum condition % (Bullseye, TRACE32 cond, GCC 14). 0 disables the gate; safety profile: 100.
fuciniCoverage.threshold.functions 80 Minimum function %.
fuciniCoverage.threshold.regions 75 Minimum region % (llvm-cov).
fuciniCoverage.threshold.instantiations 0 Minimum instantiation % (llvm-cov).
fuciniCoverage.threshold.functionCalls 0 Minimum function-call % (Testwell CTC++).
fuciniCoverage.threshold.objectCode 0 Minimum object-code %, where the report carries it.
fuciniCoverage.threshold.mutation 0 Minimum mutation score (PIT, Stryker). 0 disables the gate.
fuciniCoverage.threshold.weighted 75 Minimum composite score.
fuciniCoverage.threshold.newCode 0 Minimum line coverage (%) of new code: of the lines changed against gitDiff.baseRef that can be measured, the share that ran. Checked beside the gates on the whole run, which stays on screen — gitDiff.enabled shows the changed lines in its place instead. 0 does not check.
fuciniCoverage.exclusions.requireReason false Every exclusion written in the code has to say why: the text after LCOV_EXCL_LINE / LCOV_EXCL_START, or the Justification of [ExcludeFromCodeCoverage]. One that gives no reason is a warning in Problems at its line and fails Check Coverage Thresholds; Exclude Selection from Coverage asks for the reason and writes it.
fuciniCoverage.threshold.unmeasuredFiles -1 Maximum number of unmeasured files (see unmeasured.mode, which must be list or count). -1 does not check; 0 fails on the first file no test loaded.
fuciniCoverage.compliance.mode strict strict | whm | both | either.
fuciniCoverage.compliance.diagnostics false One Problems error per failing threshold, on the tracefile.
fuciniCoverage.problems.enabled false One Problems warning per source file below threshold.statement, on that file, with a quick fix that asks the installed chat extension for a test.
fuciniCoverage.problems.perLine off off | uncovered | uncoveredAndPartial: an Information entry (a squiggle in the editor) per block of consecutive uncovered lines in each open document, optionally plus one per line with a branch arm never taken. Independent of problems.enabled. Per folder.

Quick-action menu

Each switch puts one command in the status-bar menu (Show Status Bar Menu).

Setting Default What it does
fuciniCoverage.quickActions.toggleWatch false Offer Toggle Tracefile Watch in the menu.
fuciniCoverage.quickActions.openAnnotatedSource false Offer Open Annotated Source in the menu.
fuciniCoverage.quickActions.openAnnotatedSourceToSide false Offer Open Annotated Source to the Side in the menu.
fuciniCoverage.quickActions.toggleAnnotatedSource false Offer Toggle Annotated Source in the menu.
fuciniCoverage.quickActions.nextUncovered false Offer Next Uncovered in the menu.
fuciniCoverage.quickActions.previousUncovered false Offer Previous Uncovered in the menu.
fuciniCoverage.quickActions.worstCoveredFiles false Offer Show Worst-Covered Files in the menu.
fuciniCoverage.quickActions.worstCoveredFolders false Offer Show Worst-Covered Folders in the menu.
fuciniCoverage.quickActions.worstCoveredFunctions false Offer Show Worst-Covered Functions in the menu.
fuciniCoverage.quickActions.copyUncoveredLines false Offer Copy Uncovered Lines in the menu.
fuciniCoverage.quickActions.compareHistoryRuns false Offer Compare Two History Runs in the menu.
fuciniCoverage.quickActions.compareWithLastRun false Offer Compare with Last Run in the menu.
fuciniCoverage.quickActions.checkThresholds false Offer Check Coverage Thresholds in the menu.
fuciniCoverage.quickActions.exportJson false Offer Export Fucini JSON in the menu.
fuciniCoverage.quickActions.exportReport false Offer Export Coverage Report in the menu.
fuciniCoverage.quickActions.chooseGutterMode true Offer Choose Editor Gutter Mode... in the menu.
fuciniCoverage.quickActions.toggleInlineHitCounts true Offer Show/Hide Inline Hit Counts in the menu.
fuciniCoverage.quickActions.generateBadge false Offer Generate Coverage Badge in the menu.
fuciniCoverage.quickActions.showTestCoverage false Offer Show in Test Coverage View in the menu.

Diff, remap & reports

Setting Default What it does
fuciniCoverage.pathRemap [] Ordered {from,to} path rewrites; to may use ${workspaceRoot}. When a load leaves files unmatched, the load result offers Apply Suggested Remap with the rule inferred from the unmatched paths and their namesakes in the workspace (also Suggest Path Remap Rule in the palette); nothing is written until you click it. Per folder.
fuciniCoverage.ignore.generated true Leave out the files nobody writes and nobody tests: designer files, .g.cs and its kin, assembly attributes, Entity Framework migrations, and any *.generated.* file. Off keeps them in the run.
fuciniCoverage.ignore.namespaces [] Namespaces to leave out of every total and every page, for .NET sources: a file whose namespace declaration matches one of these is not counted. A pattern is a namespace prefix with * as a wildcard: Acme.Generated, Acme.*.Migrations, *Tests. Per folder.
fuciniCoverage.ignore.attributes ["ExcludeFromCodeCoverage"] Attributes that take a .NET class, method or property out of the count, read off the source: the member below one of these is excluded, as LCOV_EXCL_START/STOP would exclude it. Names with or without the Attribute suffix, with or without a namespace. Add GeneratedCode, Obsolete or your own. Per folder.
fuciniCoverage.testRun.skipWhenTestsFail false When Run Tests with Coverage ends with a failing test run, leave the coverage unloaded rather than show numbers from a red run.
fuciniCoverage.testRun.perTest false Run Tests with Coverage runs the .NET tests one class at a time and tags each result with the class, so the hover, the lens and Show Tests That Ran This Line can say which tests ran a line. Slower: one dotnet test per test class. Per folder.
fuciniCoverage.testRun.onSave false Saving a source file runs the tests that cover it, with coverage, and the gutter follows: for a .NET solution the test projects that reference the saved file's project, one dotnet test each; for any other runner the whole recipe. Debounced, and never while a run is already going. Per folder.
fuciniCoverage.testImpact.baseRef HEAD The git ref Run Tests Affected by My Changes compares the working tree with: HEAD for what is not committed yet, or a branch such as origin/main for everything the branch changes.
fuciniCoverage.testResults.enabled true Read the test results beside the loaded reports and mark the lines a failing test ran: a red bar at the start of the line, a stripe in the overview ruler, and the failing tests in the hover. Needs a report that says which tests ran a line. Run Tests with Coverage asks dotnet test for a test-results.trx in the report's folder.
fuciniCoverage.testResults.paths [] Further test results files to read, as paths or globs relative to the workspace folder: TRX or JUnit XML, e.g. build/test-results/**/*.xml. The files beside the loaded reports are read without being named here.
fuciniCoverage.ignore [] Glob patterns for whole files to leave out of the run (vendor/**, **/*.generated.ts, build/**) — dropped at load, so every view, gate and export counts without them. Matched against the workspace-relative and the absolute path with forward slashes; a pattern with no / matches a name at any depth, like .gitignore. **, *, ?, {a,b}, [abc]. Per folder.
fuciniCoverage.unmeasured.mode off off | list | count: what to do about workspace files of the run's kind that no test loaded (see Unmeasured versus uncovered). list names them in the Coverage view and the load result; count also scores each as an uncovered file of its code lines. Changing it reloads.
fuciniCoverage.unmeasured.exclude ["**/*.h", "**/*.hpp", "**/*.hh", "**/*.inl", "**/*.d.ts"] Files never called unmeasured, in ignore's glob syntax — header-only and declaration-only files by default. Per folder.
fuciniCoverage.gitDiff.enabled false Restrict coverage to lines changed vs a base ref. Per folder.
fuciniCoverage.gitDiff.baseRef origin/master Git base ref for diff coverage and the new-code gate: a branch, a commit or a tag (v1.4.0 for everything since that release). Per folder.
fuciniCoverage.gitDiff.author New code by who wrote it: part of an author's name as git has it. Diff coverage and the new-code gate then count the lines git blame gives to that author, instead of the lines changed against gitDiff.baseRef. Lines not committed yet count too. Per folder.
fuciniCoverage.gitDiff.since New code by date: a day as YYYY-MM-DD. The lines last changed on that day or later, by git blame; with gitDiff.author, that author's lines since that day. Per folder.
fuciniCoverage.externalHtmlReport.dir "" Read a pre-built llvm-cov show HTML dir for Annotated Source. Per folder.
fuciniCoverage.htmlReport.outputDir "" Output dir for Generate Full HTML Report. Per folder.
fuciniCoverage.badge.outputPath coverage-badge.svg Where Generate Coverage Badge writes the SVG — relative to the workspace folder or absolute, same variables as coverageFile.defaultLocation; parent folders are created. Per folder.
fuciniCoverage.badge.metric lines lines | branches | mcdc | conditions | weighted: the run-wide number on the badge (weighted is the status bar's composite score). No data for it → a grey n/a. Per folder.
fuciniCoverage.badge.autoUpdate false Rewrite the badge after every successful load, silently (logged as [FUCINI_BADGE]). Per folder.

The badge is a plain file in your repository, so a README embeds it with no service behind it:

![coverage](https://www.fucini.consulting/coverage-badge.svg)

Commit the SVG with the code and the picture on the repository page follows the number in the editor — coloured by the same colorScale.* thresholds, and kept current by badge.autoUpdate if you would rather not run the command.

Display & analysis

Setting Default What it does
fuciniCoverage.mcdcGutter.enabled false Coloured MC/DC margin gutter.
fuciniCoverage.statusBar.enabled true Show run-wide & per-file status-bar items.
fuciniCoverage.statusBar.showLauncher true Always-available quick-menu launcher.
fuciniCoverage.statusBar.showModeToggle true A status-bar button naming the gutter mode; a click moves it on by one (off → uncovered → coverage → heatmap).
fuciniCoverage.codeLens.enabled true Per-function coverage CodeLens.
fuciniCoverage.deadCode.enabled true Warn on zero-coverage functions.
fuciniCoverage.staleWarning.enabled true Mark files whose contents no longer match their run.
fuciniCoverage.explorerBadges.enabled false Coverage badges and hover next to files in the Explorer. Off: the Coverage view lists the same files, and the Explorer stays as its owner left it.
fuciniCoverage.coverageView.sortBy name How the Coverage view orders folders and files: name groups folders before files alphabetically; score/lines/branches/mcdc interleave them worst-first by that metric. Also changeable from the view's own toolbar, which writes the choice back here.
fuciniCoverage.coverageView.perspective folders Which tree the Coverage view shows: the workspace's folders and files, or — for a .NET report that names its classes (Cobertura, Coverlet, OpenCover) — namespaces, classes and methods, each with its own numbers. The dropdown in the view writes this; offered only when the run carries class names.
fuciniCoverage.coverageView.hideColumns [] Metric columns to leave out of the Coverage view's table — any of lines, functions, regions, branches, conditions, mcdc, instantiations, functionCalls, objectCode, mutation, maxCrap. The columns are ordered heaviest-first by your whmWeights, so the metrics that matter most stay visible on a narrow panel; hide the ones you never read to make room for the rest. Anything hidden is still in every row's tooltip, and the composite score is unaffected. Paid editions and student licences; the free tier shows the columns once the panel is wide enough.
fuciniCoverage.hotspots.crapThreshold 30 A function with this CRAP score or more is listed under Risk hotspots in the Coverage view: complex code with little coverage, where a change is most likely to break something no test will notice. 30 is the figure the metric's authors proposed; 0 turns the CRAP rule off. Community sees the count and the worst score; the ranked list is Pro.
fuciniCoverage.hotspots.complexityThreshold 0 A function with this cyclomatic complexity or more is listed under Risk hotspots as well, whether or not its tests reach it. 0 lists by CRAP alone.
fuciniCoverage.coverageView.followActiveEditor true The Coverage view follows the editor: the file in front is highlighted in the tree, its folders opened and its row scrolled into view. Off leaves the tree where you put it.
fuciniCoverage.coverageView.mergedMarks true With several tracefiles loaded: outline the tracefile badge on every file whose numbers overruled an older report, and let a click on a tracefile row highlight the files it measured. Off: plain badges, no highlight; the tracefile rows still count what was overruled.
fuciniCoverage.sinceLastRun.enabled true Gutter marks on lines whose coverage changed since the previous run.
fuciniCoverage.dirtyMarks.enabled true Grey gutter dot on lines edited since the run was loaded.
fuciniCoverage.editorGutter.mode off off | uncovered | coverage | heatmap: nothing beyond the findings above, bars on uncovered (and partially covered) lines only, those plus a green bar on every covered line (red, amber and green in the colour scale's colours), or those plus a warm shade on every executed line by hit count. Per folder.
fuciniCoverage.lineHighlight off off | uncovered | coverage | heatmap: a translucent wash behind the line itself, for what the gutter marks. Its own setting, so the gutter can stay clean while the gaps are tinted, or the other way round. Per folder.
fuciniCoverage.heatmap.colors five shades, pale yellow → brown The heatmap's shades on a light theme, from the line that ran least to the line that ran most (#rgb/#rrggbb). An entry that is not a colour keeps its shipped shade. Set in the settings panel with a swatch per shade and a live preview. Per folder.
fuciniCoverage.heatmap.colorsDark five shades, dim olive → vivid orange The same for a dark theme, where brighter reads as more. A changed shade is also the colour of that level's line wash. Per folder.
fuciniCoverage.overviewRuler follow follow | uncovered | off: whether uncovered lines get a stripe in the scrollbar's overview ruler. uncovered paints it even with the gutter off — a map of the file's gaps down the scrollbar and nothing beside the code; off keeps the bars and drops the stripe. Per folder.
fuciniCoverage.hitCounts.inline false ×N after every line a statement starts on, ×0 included. Per folder.
fuciniCoverage.testCoverage.publish true Publish every loaded run to VS Code's own Test Coverage view (Test Explorer panel, Explorer percentages, Show Inline Coverage). Off: no Fucini Coverage entry in the Test Explorer; Show in Test Coverage View still publishes on request.
fuciniCoverage.excludeComments true Exclude comment/blank & LCOV_EXCL_* lines (GCOVR_EXCL_* and GCOV_EXCL_* read the same; LCOV_EXCL_BR_* takes the branches on its lines and leaves the lines counted, LCOV_EXCL_EXCEPTION_BR_* only the arms an exception reaches), and preprocessor directives in C, C++, Objective-C, CUDA and C# — gcov charges a macro's uses to its #define, which made a header of constants read 0% or 100%. Branches and functions wholly on such lines go with them, and a file left with nothing measurable is not shown (named in the output as FUCINI_EXCLUDE_001).
fuciniCoverage.excluded.includeInTotals false Count excluded lines toward totals.

History

Setting Default What it does
fuciniCoverage.history.enabled false Store runs locally for trend & comparison.
fuciniCoverage.history.retention 20 Max stored runs to keep.

AI agents

Setting Default What it does
fuciniCoverage.mcp.enabled false Run an MCP server for AI agents on this machine: the gaps of a file or function, the tests that ran a line, the coverage of a diff, what moved since the last run. 127.0.0.1 only, and only for a call that carries your token (Copy MCP Server Configuration). VS Code's own agent has the same tools without it.
fuciniCoverage.mcp.port 39217 The port it listens on. Where that is taken — a second window — the next free one is used, and Copy MCP Server Configuration says which.

Status-bar quick actions

All default false except the last two; enable to add the matching action to the quick menu: quickActions.compareWithLastRun, openAnnotatedSource, openAnnotatedSourceToSide, toggleAnnotatedSource, toggleWatch, worstCoveredFiles, worstCoveredFolders, worstCoveredFunctions, nextUncovered, previousUncovered, checkThresholds, copyUncoveredLines, compareHistoryRuns, exportJson, exportReport, generateBadge, showTestCoverage, chooseGutterMode, toggleInlineHitCounts.

Telemetry

Setting Default What it does
fuciniCoverage.telemetry.enabled false Anonymous usage telemetry. Off by default; apart from the one-time install ping and the daily licence message described under Privacy, nothing is sent, and nothing is counted locally, until it is turned on. When on, a summary of each day of use goes out, including which extension settings you changed from their defaults (never paths or other text). The daily summary, the removal notice and the daily licence message are also gated by VS Code's own telemetry.telemetryLevel, which must be all; the install ping is not. Separately, a content-free error report is sent when something fails whenever telemetry.telemetryLevel is all or error, even with this setting off — see Privacy.

Updates

Setting Default What it does
fuciniCoverage.update.checkForUpdates true Periodically check both fucini.software and the VS Code Marketplace for a newer release, and offer to install whichever is newer. A newer release also shows as a bar at the top of the Coverage view and a badge on its icon until it is installed or skipped; turning this off removes both.
fuciniCoverage.update.preRelease false Be offered pre-release builds from fucini.software as well as stable ones. Pre-releases carry new work before it reaches the Marketplace: they are tested, but they are the builds we are least sure of, and you are volunteering to find what we missed. Joining tells us this install is on the pre-release channel, so we know how many testers there are and can say what changed. Join or leave from the Test pre-release builds… button in the Coverage view's licence panel; leaving puts you back on the ordinary path at the next stable release.

Licensing

Setting Default What it does
fuciniCoverage.license.key "" Activation key for the evaluation pre-release. Paste the FCK1.… key you receive (see the evaluation note at the top). Bound to this machine/user.

See the User Guide for worked examples, path-remapping recipes, CI/CMake integration, and the full FUCINI_* error-code reference.

Privacy

A few small pings go out regardless of this extension's telemetry.enabled setting: one the first time the extension activates after being installed, and a licence message once a day from every install. The daily message says which licence the install holds and how many days its trial or key still runs — its state, and for a key its plan, request code and expiry; a Community install without a key says only that it is Community — plus what changed since the last one, when something did: a set of formats loaded for the first time, a tracefile that would not parse, a licence change, an update. It carries no usage counts and no settings. These pings carry a random install id, the extension and VS Code versions and edition, and the operating system; the install ping adds whether that is the desktop or the web build, the kind of remote window if there is one (WSL, SSH, a container — the kind, never an address or a host name), your editor's display language, the extension host's Node.js version, and how this build was installed. Which editor you chose and how you have set either telemetry option are not sent. The server also records the address the ping came from.

Telemetry is deleted within 90 days, with one exception: a single row per install is kept, so that the install history does not vanish every 90 days along with the pings it was counted from. That row holds the install id, the day it first reported, the country, region and city the address resolved to, the versions, edition and operating system, and how the build was installed — not the address itself, and nothing about your work. Ask and it is deleted.

Error reports. When something fails — a tracefile that will not parse or cannot be read, a remote fetch, path remapping, the unmeasured-file search, the badge, chat, git diff, and every other FUCINI_* error — a content-free error report is sent, whatever telemetry.enabled says, as long as VS Code's own telemetry.telemetryLevel is all or error (never at crash or off). A source file that no longer matches its coverage (FUCINI_MISMATCH_001 and _002) is not a failure — editing covered code is normal use — so it is written to the output channel and marked in the editor, but never reported. A report carries the error code and where it happened; the error's message with every path, URL, file name, workspace or folder name, user and computer name, email address and number longer than six digits replaced by a placeholder such as <path>; the parser that claimed the file, and — for a file nothing claimed — how each parser turned it down, as codes; and facts about the file, never its content: its extension, a size range, its encoding, whether it was gzipped or binary, the XML root element's name or up to twenty top-level JSON key names (sanitised the same way), and the shape of its first line with every letter replaced by a and every digit by 9. Reports are batched — at most one post every ten minutes and fifty distinct problems a day, each counted rather than repeated. An install that made the Unlock Full Features trade, or took the student grant, and accepted the current terms also sends the first 4 KB of a coverage file that fails to load, as those terms say; no other install ever sends file content on its own.

Error notifications also offer Send diagnostic report. It opens a preview of exactly what would be sent — the same content-free facts — with an unticked option to include the first 4 KB of the file, an optional comment and an optional reply address, and nothing is sent until you click Send. It works whatever telemetry.enabled says, but not while telemetry.telemetryLevel is off: then the preview offers Copy to clipboard instead, for an email to support@fucini.software.

Your computer name, user name, home directory and network interfaces are never sent, at any setting, and neither are your project name, your licence key, the remote.headers you configure or the text of any setting. Your source code and file paths are not sent either, with two exceptions: the Community tier's Unlock Full Features trade sends the 4 KB error sample described above, and a diagnostic report carries the first 4 KB of a file when you tick that box. That trade used to send one randomly chosen file's annotated source, with its path, every month; the collection was withdrawn in September 2026 — no install sends it, nothing new is stored, and what had been kept has been deleted. An install that made the trade — or took the student grant — under the earlier terms keeps a monthly summary without settings until it accepts the current ones; it is asked once.

Checking for updates asks both fucini.software and the VS Code Marketplace for the newest version, so both see the address the request comes from; turn update.checkForUpdates off to stop it.

Everything else is controlled by the telemetry.enabled setting above and is off by default — nothing further is sent, and nothing is counted locally either, until you turn it on.

With it on, the install ping adds your hardware (CPU and memory), and a summary of each day the extension was used goes out the day after: how often coverage was loaded, in which formats, for which languages (by file extension), how often the same tracefile was reloaded, which commands were run, and which extension settings you changed from their defaults. A setting that is a switch, a number, a choice from a list or a colour is sent with its value; one that holds text, a path or a list — a folder, a glob, a branch or task name, a path mapping — is sent only as "changed", with the number of entries for a list. Your licence key and remote.headers are not mentioned at all. A day without use sends no summary. Coverage loads are counted locally and reported once — a ping per load meant a ping per save with watch mode on. Uninstalling sends a removal notice with how many days the extension was installed. The daily summary, the removal notice and the daily licence message also need VS Code's own telemetry.telemetryLevel to be all; the install ping does not.

Seat-pool licences (Pro Plus, Business, Enterprise) additionally check in every twelve hours so the seat can be verified and its key renewed; that is licence administration and is not affected by this setting. An individual Pro licence holds no seat and checks in nowhere — it works offline — with one exception: for the seven days before its key expires and the forty-five after, and only then, it asks the licence server every twelve hours for the renewed key, sending its request code, machine fingerprint, extension and VS Code versions, operating system and editor language.

The trial and the Community tier keep a usage ledger with the licence server: a hash of VS Code's machine id — taken together with a fixed label of ours so that it matches no value another product holds, that label being part of the extension rather than a secret — the edition, the day the evaluation began and the counts of the free tier's metered actions, so that a new VS Code profile is neither a new trial nor a new week. No name, email, path or coverage figure. The ledger has no field for the address a report arrives from and never reads it; the web server's access log records it as for any request. A paid licence never reports to it. From 2.2026.2 an install also sends a second hash, made the same way, of the operating system's own machine identifier with the computer's name — or of its network cards' hardware addresses where the system has none — because VS Code's own identifier is a value in a settings file; only the hash leaves the machine, and none is sent from a container. A new install's trial begins with the ledger's first answer, and until then it runs on the Community tier; after that nothing waits for the ledger: offline, the extension runs on its own counters. In a development container or a Codespace no trial begins.

Every install, on every plan, reads the minimum supported version from the licence server about once a week (from 1.2026.1). It is a download of one public, signed file and sends nothing: no request code, no install id, no version, no key — the server cannot tell a paid install from a free one, or either from a browser. When a build is older than the oldest version still supported, the trial and the free tier stop and ask for an update, and a paid licence is told the date and keeps working for a grace period first. An install that cannot reach the server is never stopped for that reason; a policy it has already read stays in force offline. This is not an update check and is not switched off by update.checkForUpdates.

The full list is in the privacy policy, alongside NOTICE.

Contact

Questions, bug reports, or alternate licensing terms — reach Coverage Studio support via the contact page or by email at support@fucini.software.

License

Proprietary — © Fucini Consulting, all rights reserved. You may install and use the published build within the limits of your plan; you may not copy, modify, redistribute or reverse engineer it. What the extension produces from your own data is yours, with no attribution required. See LICENSE and the Terms. The open-source packages bundled into the extension keep their own licences; they are reproduced in THIRD-PARTY-NOTICES.txt, which ships with it.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft