v1.4.1: the first published release is a patch
Released 2026-07-23. The oldest release this journal covers, and it is a patch, because the version number was never what this release was announcing.
What we were trying to fix
Start with the thing that looks like a mistake and is not. This journal begins at v1.4.1. The kit had already shipped 1.0.0 through 1.4.0, and you can read every one of those in CHANGELOG.md. None of them is tagged and none has a GitHub release, so none has an article here.
v1.4.1 is the first published release because it is the first moment the claim was provable. Four applications on dead platforms, WinRT 8.x, Windows Phone and UWP, had been audited, migrated to Blazor WebAssembly and verified in production. The commits around this tag say exactly that: the portfolio is closed, the proof is made. Everything before it was a tool being built. This is where it became a tool with evidence behind it, and the evidence, not the version, is what was being published.
Which is why the release is a patch and not a 1.5.0 or a 2.0.0. The version tracks the code. The code change here is nine files. The announcement is about the four applications, and a reader coming to this release’s notes should know that the two are describing different things: the notes lead with the portfolio table, and the diff is the wave 3 lessons underneath it.
Those lessons came from the fourth and hardest application: a 2016 UWP app with what its own repository described as a backend, migrated to Blazor WebAssembly with a static API.
What we decided
Everything in this release is one correction applied in four places: read the artifact, not the sign that stands for it.
Zombie project detection came first, and it is the sharp one. The audit inventory now reads targetFramework for each project directly from the csproj, and marks a project zombie: true when an old target framework (netcoreapp1 or 2, netstandard1, PCL, UAP) is receiving 10.x packages. The rule underneath it is that an update robot is not a sign of life. The wave 3 audit had looked at a netcoreapp1.0 web service that Renovate had been faithfully watering for years, seen current package versions, and concluded the backend was already modern. It was not modern. It was abandoned, on a runtime that had been out of support for years, with a bot dutifully bumping its dependencies.
Premises verified, never inferred, is the same correction generalised into the executive audit guide. Target frameworks are read from the csproj rather than assumed from a solution’s shape. Tests are proven by their attributes and never by a project’s name in a solution file, because the wave 3 target had a dangling solution reference to a test project that no longer existed. And a data flow is proven by looking at the caller, at the actual HttpClient, before anybody writes the word “wired” in a report.
Five rewrite protocols were codified from the same wave: legacy SQL onto modern SQLite (reordered joins, and a rebuild rather than a translation), out of project assets copied by an MSBuild target and never by a content link, the Tailwind cascade, a service worker precache treated as the contract of an installed application, and offline behaviour proven by killing the server when there is no production environment yet to test against.
The content link one deserves its own sentence, because it is the best failure of the wave. A linked asset served with HTTP 200 and zero bytes. Not a 404, which anybody would have caught. A successful response containing nothing, which every automated check reads as fine.
And report-dashboard.py now writes its output next to the report.json it was given rather than into the current working directory, because wave 3’s dashboard had landed at the root of the migrated repository. That is the same class of error one level down: a tool trusting where it happens to be standing instead of what it was actually handed.
What got cut
No attempt was made to explain away the gap between the audit estimates and the pipeline’s actual times. The release published both columns side by side: thirteen, thirteen, eighteen and twenty-nine days estimated, against eighteen minutes, about thirty minutes and about an hour for the three that were timed. The fourth, the first application through the pipeline, has no measurement at all in that table; its cell just names the wave it belonged to, because nobody was timing anything yet. Printing an incomplete column was better than back-filling a number for it, and it is worth noticing that the release resisted doing so.
No general claim was made from four applications either. They were four applications by one author, in one family of technologies, and the case studies say so. The measured times are facts about those four runs.
And the version was not inflated to mark the occasion. It stayed a patch.
What bit us
Both of the audit’s errors on wave 3 were the same error wearing different clothes, and both are errors of trusting metadata over behaviour.
A project listed in a solution file is a statement of intent by whoever last edited the solution. A package version bumped by a bot is a statement about the bot’s configuration. Neither is evidence about the program. The audit had been reading both as evidence, and produced a confident and wrong recommendation from them, which is worse than producing nothing: an audit that says “this part is already modern” removes a component from the migration plan entirely, and nobody looks at it again.
The lesson that carried forward past this release is that an assessment step must read the thing itself, and that where it cannot, it must say so rather than fall back on the nearest available signal. Almost every gate the kit has added since is a version of that sentence.
Five other releases went out on this same day, all of them about the kit’s own workflow rather than about anything it had migrated. This one is the odd member of that set: it is the only one of the six that came out of a migration itself, and it went out within minutes of the commits that closed the portfolio, which is roughly what finishing something looks like.