4.1.0: one package to install, two folded into core, and the security findings - #48
Merged
Conversation
The alert history's durability was asserted against the in-memory provider only, which proves the logic and nothing about serialization -- and the log is the one entry a state provider holds that is not a PulseCheckerState, under a key that is not a checker's name. Either could have been an assumption a provider quietly relied on. Now round-tripped through a real Redis and a real PostgreSQL, asserting the nullable enum and the delivery flag survive, and that the log does not read back as a checker. MeterMetricsInsights had no tests at all. It reads instruments that are internal to another assembly by meter name, so nothing about it is checked by the compiler: rename an instrument and it keeps building and silently counts nothing. These run real checks and assert the counters moved, that the healthy share is null before anything has run rather than a confident zero, and that disposal detaches the listener.
Scorecard raised four Pinned-Dependencies findings against the two sample Dockerfiles: both took their .NET base images by tag. A tag is mutable, so "sdk:10.0" is a different image next week and the build that passed review is not the build that ships. Both are pinned by digest now, which Dependabot updates the way it updates a package version. The repository root had collected forty-four screenshots from unrelated local work -- admin UIs, an email preview -- untracked but one `git add -A` from being published. They are deleted, and root-level PNGs are ignored so it cannot happen again. Anchored to the root rather than a bare `*.png`, which would also have swallowed docs/assets and the sample's favicon; both directions are asserted rather than assumed.
… guess Both packages carried zero external dependencies, so splitting them out bought a consumer nothing -- there was no driver to keep off anyone's machine -- and cost them two more things to find and install. That is the drift: the provider packages exist to keep Npgsql or Temporalio away from people who do not use them, and these two were not providers. The code now lives in Healthie.NET.DependencyInjection, under the namespaces it always had, so no `using` changes. The old packages remain published as assemblies of type forwards. That matters more than it looks: moving a type between assemblies is a binary break the compiler cannot see, because source keeps building and only the runtime finds out. With the forwards, an application built against 4.0.0 keeps working untouched -- which is what makes this a minor release rather than a major. Healthie.NET is new and is the package to install first. It carries no code, only a dependency on the core package: the name people guess did not exist, and the real entry point was called DependencyInjection, which reads like plumbing rather than the way in. Deliberately not a bundle -- it must not drag a dashboard into a console app. The tests assert against assembly-qualified names rather than the C# types the compiler has already bound, because binding is exactly what hides a missing forward.
… the suite The README listed twenty packages as one flat table of equal weight, which is what made this look like a twenty-package install when it has always been a one-package install. Grouped now by what you are actually deciding -- swap the storage, swap the scheduler, add a surface, add a capability -- behind collapsed sections, with the single install and its two-node dependency chain stated up front so nobody has to wonder whether the meta-package is a bundle. The metrics test double repeated a mistake this branch had already fixed once: it derived from PulseChecker with a constructor the container cannot satisfy, so assembly scanning failed twenty registration, MCP and AI tests that have nothing to do with metrics. It went unnoticed because the tests were run filtered rather than as a suite. Its assertions were wrong in a second way. A MeterListener hears every measurement in the process, so exact counters passed alone and failed under the full suite. They are deltas with a floor now, and the healthy share is asserted against a snapshot built directly, since the arithmetic is what is under test rather than whatever else the suite was running.
CI could not find src/Healthie.Meta/Healthie.Meta.csproj because it was never committed: the standard Visual Studio ignore block carries `*.meta` for build artefacts, and that pattern matches a directory component too, so the whole project folder was invisible. `git add -A` reported nothing, the local build was happy because the files were on disk, and the first sign of trouble was a restore failure on the runner. Renamed to src/Healthie.NET.Package, after the package it produces, which also reads better than "Meta". The ignore block is left alone: it is the standard template and weakening it to rescue one folder name would trade a naming collision for unignored build output. The guard test follows the new path, and a locked-mode restore now succeeds locally, which is the gate that actually failed.
… first CI found this against a real PostgreSQL: two alerts recorded back to back left one in the store. Record captured a snapshot and started a write without waiting for the one before it, and nothing ordered them -- so the older, shorter snapshot could land last and silently drop the newer alert. It passed locally only because two writes to a fast disk happened to finish in order. The snapshot is now taken inside a write gate rather than at call time, so whichever write goes last writes the newest state and the store converges on what is actually held. The regression test needed correcting twice before it was worth trusting. It first polled until the store held two alerts and asserted -- which passes the instant the fast write lands and looks away before the slow one overwrites it, observing the very failure under test and calling it success. It waits a fixed settle past both writes now. With the gate removed it reports 2 expected, 1 actual.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Twenty packages was too many, and two of them had no business being separate. This fixes that without breaking anything — an application on 4.0.0 upgrades by changing nothing.
Packaging
Healthie.NETis new and is the package to install. It carries no code, only a dependency onHealthie.NET.DependencyInjection. Deliberately not a bundle: verified from the packed.nuspecthat it declares exactly one dependency and ships zerolib/assets, so no provider, scheduler or UI framework arrives with it.Healthie.NET.UptimeandHealthie.NET.LeaderElectionare deprecated but still work. They ship as assemblies of type forwards, so an application referencing either keeps compiling and keeps running untouched. That is what makes this a minor release rather than a major: moving a type between assemblies is a binary break the compiler cannot see, because source keeps building and only the runtime finds out.Why not 5.0.0, and why not a rewrite of 4.0.0: nuget.org versions are immutable, so 4.0.0 cannot be republished. Making the fold non-breaking removes the need for a major at all.
Security
ee0c78e, inert.docs/assets, the banner and the sample favicon stay trackable.Docs
README package section retiered from a flat 20-row table into what you are actually deciding — swap the storage, swap the scheduler, add a surface, add a capability — with the one-line install and its dependency chain up front. Plus a
docs/migration-v3-to-v4.md.Tests
New: type-forward resolution for all 13 moved types (asserted against assembly-qualified names, since C# binding is exactly what hides a missing forward), a guard that the meta-package cannot become a bundle, alert-history durability against real Redis and PostgreSQL, and the metrics collector, which had none.
Verification