A test that something might run is not a guarantee. If a property has to hold for an artifact to be correct, the check for it belongs in the artifact’s dependency graph, so producing the artifact is impossible without it.

Nix makes the distinction unusually crisp, and gets it wrong by default. passthru.tests is the idiomatic place to hang a package’s tests, and it is eval-only: it never reaches the .drv. pkgs.hello declares two tests and neither appears in the inputDrvs of its own build, so building hello runs nothing. That is deliberate, to keep test cost off every build, and it means the tests only run when ofborg, nixpkgs-review, or a person asks. nix#7662 is the standing request to make test derivations first-class.

The stronger construction is to interpolate the check derivation’s store path into a derivation attribute. String context carries the reference into inputDrvs, so Nix builds the check first and the package cannot exist without it. Verified both directions on one hook: stdinDrained and resholveVerified both show up as inputDrvs, and deliberately breaking the checked property makes the check fail and Nix then refuse to build the hook at all.

The generalisation is not about Nix. It is the difference between a property you assert somewhere and a property the build system will not let you violate, and it is the same move as a smart constructor: don’t validate after the fact, make the invalid thing unconstructible. The cost is real (every build pays for the check) and is the reason the default goes the other way, so spend it on invariants whose violation is silent. A hook that fails to drain stdin is exactly that shape: it exits 0, the symptom appears only past a buffer threshold, and it surfaces as an error in someone else’s process.