Repository navigation
Compared with 5.6.0. Check and test answers use new output shapes, and the engine does more work in the background and keeps new state files. fuse check accepts directories and patterns, a launcher can register an external writer as a session, and the stop hook checks only the stopping session's files. fuse test reruns the tests that failed last time and runs them first. fuse build gains --changed and --warnings, and fuse status is new.
Update with dotnet tool update -g Fuse. The hook settings fuse init writes are unchanged, so you do not need to run it again. Run fuse init --instructions again to refresh the Fuse section in AGENTS.md. The first command after updating restarts the engine for the new protocol, and no request fails. A script that parses fuse check or fuse test output must accept the new group headers and the tree stamp at the end of the summary.
Added
- Every command takes
--helpand-h, which print that command's usage to standard output and exit 0, running nothing.fuse hook --helpprints its usage to standard error and exits 0 without reading stdin. A command refuses an unknown option with exit 2 and its usage. Forfuse buildandfuse test, the command refuses only a near miss of a Fuse option (fuse: '<token>' is not a fuse build option; did you mean '<option>'?for builds, and the same form for tests), and passes every other token todotnetunchanged. Commands shows each usage. fuse buildandfuse checktake--max-errors <n>,--all-errorsand--json. The default answer shows 20 errors;--max-errorstakes 1 to 100000, and--all-errorsshows every error.--jsonprints one JSON object per error, with the text summary as the last line. Each object holdsfile,line,column,codeandmessage, leaving outlineandcolumnfor a general error; a check addscauseandgeneratedwhen present, and a locked build error addslockedBy. A JSON summary never names a file. A capped build summary ends with; all N in <state>/last-build-results.txt. That file holds every error and warning, and in JSON mode the errors are also inlast-build-results.json. A failed build with no parseable error prints the output tail before the summary, as text mode does. Thefuse_checktool takesmax_errors(1 to 100000, default 20). Commands shows the check forms and the build forms.fuse test --failedreruns the tests that failed in the last recorded run.fuse testrefuses--failedwith--allor any other argument, exiting 2 and running nothing. It also refuses a rerun with nothing recorded to rerun:fuse: no test run is recorded yet; run fuse test firstandfuse: the last test run was from dotnet test arguments, so its failures cannot be rerun; run fuse testexit 2. When the last recorded run had no failures,fuse: the last test run had no failuresexits 0. A rerun printsreran N test(s) that failed last time in M test project(s), names each dropped project with; left out X, which is not a test project in this repository, and says when the rerun widened past the failures. When none of the recorded tests still exists, it printsfuse: none of the N test(s) that failed last time ran; they may have been renamed or removedand exits 1. After--, every token goes todotnet testunchanged, even--alland--failed. Thefuse_testtool takesfailed, and refuses it together withall. The record islast-test-results.json, besidelast-test-results.txtin the state directory, holding the last run's per-test verdicts and durations for at most 20,000 tests. A corrupt file counts as no record. Commands shows the forms (#125).fuse build --warningslists the build's warnings after its errors, at most 20 of them, each indented. The summary counts the rest as... N more warning(s) in <state>/last-build-results.txt, and the file holds every warning under## warnings. A warning that is also an error prints once, as an error, and the warning count in the summary stays MSBuild's.fuse checkhas no--warnings: the check refuses it, since warnings come from the build. Commands shows the form.fuse build --changedbuilds only the projects that own files changed since HEAD and their dependents. Fuse reduces the set to the highest projects, and eachdotnet buildgets the same extra arguments. It first prints which projects it builds (fuse: building M project(s) for N changed file(s) (tree H): names), then onebuild (<name>)summary per project, andF of M project(s) failedwhen several build. With no C# changes it builds nothing and printsfuse: no C# changes since HEAD; nothing to build (tree <hash>). When the selection cannot be trusted, because project files changed or no project owns the changes, it says so and builds the whole solution instead. Commands shows the form.fuse statusshows the engine and the changed files: whether an engine runs and its memory, how many files differ from HEAD with their tree stamp and HEAD, and the large-tree limits. It never starts an engine. Past a short wait it reads the tree from git instead.fuse status --jsonprints one object withengine,pid,memoryMB,changedFiles,head,tree,reloadAtandwholeProjectAt. It exits 0 in a repository and 2 outside one. Commands shows both forms.fuse session begin <name> --paths <pattern> [<pattern>...]registers an external writer a launcher starts as a session for its path patterns, so a judge's stop leaves that writer's half-written errors to it. It printsfuse: session <name> registered for N path pattern(s); files under them count as that session's until fuse session end <name> or 4 hours pass. Beginning the name again replaces its patterns and restarts the 4 hours.fuse session end <name>printsfuse: session <name> ended; its files now count as no session's, and refuses an unknown name withfuse: no registered session named <name>and exit 2. A name holds 1 to 64 letters, digits,-,_or.. A pattern is repository-relative, with**as a whole segment and*and?inside a segment, at most 256 characters and 32 patterns per session, with 64 sessions live at once. Registrations live inregistered-sessions.tsvin the state directory and survive an engine restart.fuse uninitdeletes them, butfuse stopkeeps them. A plainfuse checkstill reports every error. Commands shows both forms, and Subagents and parallel agents explains external writers (#129).fuse checktakes directories and glob patterns as well as files. Any existing directory or argument with*or?makes the whole call scoped. You can mix files, directories and patterns. An error summary adds; checked <paths> and what it reaches, and a clean answer readsfuse: no errors introduced in <paths> and what it reaches (N file(s) checked). The check refuses a spec that matches no changed C# source, as well as a scope expanding past 2000 source files, each with exit 2 and running nothing. Thefuse_checktool stays files-only. Commands shows the forms (#127).fuse testleaves out test projects that need a container runtime when none answers. A project is container-gated when any of its package references isTestcontainersor starts withTestcontainers.. Fuse probesdocker infothenpodman info, 2 seconds each, and caches the answer for 5 minutes per engine process. The summary names tests in container-gated projects as; skipped N test(s) in P1, P2 (no container runtime). When no test can run, Fuse starts no process and printsfuse: no test ran: N test(s) selected in P need a container runtime, and none is running; start Docker or Podman and run fuse test again, exiting 1. A run that passes arguments todotnet testruns without that check. Commands shows the lines, and Limits covers which projects are container-gated.- When the pre-shell hook rewrites an agent's
dotnet testordotnet build, the rewritten command prints a notice on standard error first:fuse: dotnet test routed to fuse test (tests affected by your changes; fuse test --all runs every test)andfuse: dotnet build routed to fuse build (first 20 errors; fuse build --all-errors for every error). A command you type yourself prints none. Connect your agent explains the rewrite. - When a build error says a file is locked (MSB3021, MSB3026 or MSB3027), an indented line follows the error, naming the file and the process that holds it (
locked: <file> is held by <name> (pid <n>); stop that process, then run the build again), ordotnet build-server shutdownwhen the holder is a build server. In JSON mode Fuse leaves the line out and carries the holder inlockedBy. Commands shows the line, and Troubleshooting covers the fix. fuse checknames the changed files in other languages that it does not check. The summary ends with; N non-C# file(s) changed, which Fuse does not check: <paths>, naming the first 5 and counting the rest, on clean and error answers alike. With named files it covers the whole tree's delta, not the named scope. The post-edit hook never shows it, and the stop hook shows it only when it already blocks. Commands shows the line, and Limits covers what a check does not see.- The evals gain a
scalesuite and astalesuite.scalemeasures one cold check of 1,500 changed files in 6,000 over 30 projects plus one test project on a generated fixture, resizable with--projects,--filesand--changed. The result's factor extrapolates full-scale numbers; never quote them as measured.stalebreaks one file through the post-edit hook, fixes it, and checks whether the stop hook delivers a stale error. The selection suite takes--failed-roundsfor fix-and-rerun rounds withfuse test --failed, and the result files record kept answers, reused errors, result-file completeness and member-walk scope. Evals describes the suites and fields.
Changed
- Check errors that share a cause print under one header naming it once:
changed: <declaration> (N error(s)), or(N error(s), first M shown)when the cap cuts the group. Member error lines keep their shape at column 0, andlast-check-results.txtstays flat with every error. A script that reads check output line by line must accept the header lines. How it works explains the groups (#126). - Test failures that share their message's first line print under one header:
N failure(s) with the same first message line: ..., with the first line cut at 160 characters in the header only. Members keep their fullFAILEDdetail, and past 10 failures in detail the rest are names underalso failed (N):.last-test-results.txtstays flat and full. Commands shows the form (#126). - On the command line, when the last run recorded 1 to 100 failures inside a VSTest method selection, those tests run first as their own stage, and their failures print before the rest runs; the final summary counts each test once. Platform runs, whole-project runs, selections with no recorded failure, and projects whose recorded failure is a theory row run unchanged in one stage. The MCP answer arrives once with the full text. How it works explains the stages (#115).
- A stop answer for a session checks only its own files and what they reach: it binds the files the session wrote alone or together with another session, and files no session wrote, then attributes as before.
N error(s) from other sessions' edits left outnow counts only in-reach errors from other sessions.fuse check, the MCP check, and a stop whose harness names no session are unchanged. How it works and Subagents and parallel agents explain the scope (#104). - When a check binds whole projects, the summary names the projects and the changed files that forced it:
checked whole projects: Lib because Lib/Calc.cs (removed: ...); App because ..., at most 3 projects and 2 files per project, sorted, with; and N more in the engine logpast that. Thecheck:line inengine.logholds the full list afterwhole projects:. The summary names a dependent that owns no candidate with its forcing files and no per-cause lines. Commands shows the form (#128). - An edit whose declarations compile to the same code as HEAD's selects no tests: a local rename or
varagainst the explicit type seeds nothing. Anything Fuse cannot read counts as changed. When every changed declaration compiled to the same code,fuse testruns nothing and printsfuse: no test is affected by the changes (out of T); the changes compile to the same code as HEAD; fuse test --all runs everything. Partial drops are engine-log only. How it works explains the comparison (#109). - Member-level selection now applies when at most 100 test classes are reachable, up from 40. Each plan logs its walk statistics and the level it selected. How it works explains the walks (#80).
- A run without MSBuild emits a dependent again only when a project it depends on changed its declarations; otherwise the copy keeps its image.
engine.lognames each skipped dependent. How it works explains the runs (#74). - The first
fuse checkafter an engine restart reuses HEAD's finished baseline results from the state directory instead of binding HEAD again, keyed by the commit and the inputs the result depends on. There is no output change.engine.loggainsbaseline: <n> stored result(s) read for HEAD <12 hex>; <m> computedper check that reads the store. How it works explains the store (#96). - After HEAD moves, the engine warms the baseline compilation-wide analyzer results in the background between requests. There is no output change.
engine.loggainsbaseline warm: <project> compilation-wide analyzers done in <ms> msandbaseline warm: skipped, HEAD moved again. How it works explains the warming (#99). - The background load of dependents also builds their compilations, so the next check reuses them. There is no output change.
engine.loggainspreloaded <project> compilation in <ms> msper project. How it works explains the preload (#67). fuse buildandfuse checksummaries end with; tree <12 hex> over N changed file(s), naming the tree the command ended on;last-check-results.txtandlast-build-results.txtheaders carry the same stamp. Hook and MCP answers carry no stamp. Commands shows the stamp.- On large uncommitted trees Fuse bounds 2 costs. Past 500 changed files a check skips hashing for a kept answer and answers fresh. Past 2000 touched paths the next sync re-evaluates the projects and resets the set. The
check:line inengine.loggains per-session file counts. Limits covers the bounds, and Troubleshooting the log lines. fuse initprints the version that registered the hooks:fuse: hooks registered by Fuse <version>; ..., orfuse: MCP server registered in .vscode/mcp.json by Fuse <version>; .... When you rerun it after an upgrade, it prints the new version even when every file isunchanged. The--instructionssection gains the container pre-flight bullet and the C# scope bullet. Runfuse init --instructionsagain to get them. Connect your agent shows the section.
Fixed
- Before the hook returns a background post-edit answer with errors, a second check against the current tree confirms it. When the tree no longer reports the errors, the hook exits 0 silent instead of delivering an already-fixed error. The hook logs deliveries and drops in
hook.log. The hook wait now covers at most 2 checks: the first answer plus one confirmation. How it works explains the confirmation. - When a reached project is missing from the engine's views after one recovery rebuild, the check ends unanswered with
fuse: could not load <project>: the check reached <name> but the engine's view does not hold it (the projects the view holds are named in engine.log)and exit 2, instead of answering without that project's errors. Hooks stay silent on it.engine.loggains acheck coverage:line naming which reached projects each view held. Troubleshooting explains the line (#82). - A scoped check refuses a file spec that is not a C# source of a project, even when mixed with checkable specs, with
"<spec>" in the check matches no changed C# source file of a project in this repository. Without the refusal the mixed case would silently check only the other specs. Commands and Messages cover the refusal. - When
fuse testpasses the command through todotnet test, a positional directory stays positional on VSTest, which refuses--project. Only on Microsoft.Testing.Platform does Fuse spell a first argument naming a project, solution or directory as--projector--solution. In 5.6.0 the spelling applied on VSTest too. Commands describes when arguments pass through (#122). - For a Microsoft.Testing.Platform project with a .NET Framework target in a repository without the
global.jsonopt-in,fuse testruns the project throughdotnet test, which starts each target correctly. A .NET Framework target builds an assembly with noruntimeconfig.json, whichdotnet execcannot start. In 5.6.0 every target started directly, so the project produced no results. Commands describes when a run goes throughdotnet test. - In a repository without the
global.jsonopt-in, a Microsoft.Testing.Platform project whose built target exits without printing results now runs throughdotnet test. This covers an NUnit or MSTest project that references the platform without being a test application that runs itself. In 5.6.0 such a target counted as a pass with no tests, and another project's results could hide that. When a platform project goes throughdotnet testafter Fuse built it,dotnet testno longer builds it again. Commands describes when a run goes throughdotnet test. - A Microsoft.Testing.Platform project that Fuse runs through
dotnet testin a repository without theglobal.jsonopt-in now gets the project positionally and reports to TRX, reading the filter from the run settings. Fuse leaves a framework's own filter arguments out, so an xUnit v3 or TUnit project runs whole. In 5.6.0 the fallback passed--project, which only the platform'sdotnet testaccepts, so VSTest's refused it and the project produced no results. Commands describes when a run goes throughdotnet test.
Documentation
- Commands documents every new flag, command and output form. How it works explains grouping, container gating, the stop scope and same-code selection. Limits covers the new bounds and the files a check does not see. Troubleshooting adds the new engine-log lines, the locked-file holder and the missing container runtime. Messages covers the new refusals. Evals documents the scale and stale suites, the failed rounds and the new result fields. Connect your agent and Subagents and parallel agents cover the refreshed instructions and external writers.
- The evals measure Polly in place of the .NET Community Toolkit: a layered library whose 6 xUnit v3 test projects run on Microsoft.Testing.Platform. Its first results ship with this release, and Benchmarks quotes them. Evals lists the pinned repositories.