The nugetfuzz package cache is usually where the corpus itself came from,
so it holds exactly the dependency closure a corpus assembly references;
probing it resolves references the machine-wide NuGet cache does not have.
Reference assemblies name GAC-only internals such as SMDiagnostics and
System.ServiceModel.Internals, which ship in no pack and no package.
Reporting them as missing suggested --refs could supply them, so the
staging walk now tracks whether a name was reached through a reference
pack and drops those from the report.
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
Handing the corpus over as `$(cat list)` is a bourne shell construct, so the
one invocation the tools print and the README documents did not work on
Windows. nugetfuzz already reads its package list from an @file for the same
reason; decompdiff now takes its corpus entries the same way, leaving the
shell out of it entirely.
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
Comparing two commits meant preparing a worktree for each by hand before the
tool could be called, which is most of the work of running it and easy to get
wrong: a checkout carrying a stale Release build is reused silently, and the
timestamp in the header line was the only thing that said so.
--old and --new now also take anything git can resolve to a commit, checked
out into a worktree under ~/.cache/decompdiff keyed by that commit. The
worktrees are kept because the Release build inside one is what a rerun would
otherwise repeat: a second run of the same pair drops from minutes to seconds.
Paths keep priority over refs, so an existing directory never changes meaning.
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
decompdiff indexed the Microsoft.NETFramework.ReferenceAssemblies packs into
one flat name map shared by the whole corpus, and never indexed
Microsoft.NETCore.App.Ref at all. Ordering the packs by name let net45 claim
mscorlib and System.Runtime, so a net9.0 assembly resolved its BCL against
.NET Framework 4.5: Task, ValueTask and the async method builders came back
as UnknownType, AsyncAwaitDecompiler could not match a state machine, and
every async method decompiled as a raw MoveNext.
Both sides of a diff degraded identically, so comparisons stayed valid, but
the corpus stopped representing modern code. Over 28 nuget assemblies the
same run reports 338 //IL_ warnings instead of 18202, 237 leaked <> names
instead of 15654, and 116k fewer lines.
The pack is now chosen per assembly from its TargetFrameworkAttribute and
searched before anything else, matching how nugetfuzz already resolves them.
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
Both tools have found real decompiler defects (several merged fixes came out of
nugetfuzz sweeps), but they only existed in a private checkout, so nobody else
could run them and their setup knowledge lived in one head. They complement the
fixture suite from the other side: it decompiles code we wrote, these decompile
what the world ships.
They stay outside the solution - file-based apps, run by hand, never by CI - and
the near-empty Directory.Build.props/Directory.Packages.props keep the repo-wide
warnings-as-errors, lock-file and central-package-management settings from
reaching them.
The catalog sweep driver is PowerShell rather than bash so it runs on Windows as
well, which also drops its curl/jq dependency; staging falls back to copying when
Windows withholds symlink privileges, and report file names are hash-truncated to
stay inside the 260-character path limit.
Assisted-by: Claude:claude-opus-5[1m]:Claude Code