A rule that sees every package at once, and the build that feeds it Replaces #8, which GitHub will not let me reopen: I merged it by mistake and then took it back off master. Stacked on #11, and the last of the five. Review that first; this diff is against it. Adds `TestGenValidSpecPerGenValid` from `testing.md`, the flagship: for every `GenValid` instance, a `genValidSpec` call in the matching gen package's test suite. An obligation made and an obligation met are different facts, found in different packages, so the check between them is a left join and a null check. 130 files, 6,432 lines. 327 tests. ## Two things this rule needs that nothing before it did **The compiler.** A `GenValid` instance a splice produced appears in no source, and a `genValidSpec` call a splice produced appears in no source either, so each unmet obligation is put to the artifacts before it is reported. Reading them is its own package, because the `ghc` library and `ghc-lib-parser` both expose modules under `GHC.` and one component cannot import both. The artifacts are not optional. Given any `.hie` directories, every module read must have both a `.hie` and a `.hi` or the run fails naming the module, because a tree missing one module is indistinguishable at the point of use from no tree at all: both say nothing. **Every package at once**, which is what the process boundary is for. Facts cross it; source does not. A package is read once into a SQLite store and the project layer answers from those stores alone, so the Nix side is one derivation per package plus one for the repository, and changing one package rebuilds three derivations rather than all of them. That the two paths agree is a property in the suite, per project rule, so a fact a project rule reads out of memory and extraction never serialises fails the tests rather than the build. ## What the second process can find wrong `StoreProblem`, six constructors, every one of them an obligation that would otherwise be quietly satisfied: a package whose facts never arrived, facts from another build of the tool, a module a cabal file declares and the facts do not cover, a module that did not parse, a suppression naming a rule this half does not run, and a module a build was held to covering and did not. All six are about names rather than about paths on this machine, so all six are typed and the renderer is the only place any of them becomes a sentence. What a package output directory can be wrong about is one constructor carrying text, because what it is about is an absolute store path. The two readers in `hopinion-hie` take a `Path Abs File` and fail with an `ArtifactUnreadable` rather than a bare `Text`, and the source path a `.hie` file records is parsed into a `Maybe (Path Rel File)` at that boundary: GHC records what it was handed, and a build that hands it absolute paths is one whose two `Main`s can be told apart by nothing, which now reads as `Nothing` rather than as a string nothing can do anything with. ## What else arrives here The Nix builders a consuming flake calls, the end to end tests over them, and `plan/`, which describes this architecture and so is only true once it exists. ## The corpus `plan/context.md` holds the table and accounts for every number in it by hand. nix-ci's 22 `HsNoCustomShowRead` findings were all `Show` written to keep a secret out of a log line, which is the exception `haskell-style.md` names, so they are now 22 suppressions each naming the secret it hides, and the row reads 0. NorfairKing/nix-ci#527 is that work, and its `hopinion` check passes against this branch. ## What arrives with the report codec `judge` is the first command that reads a report back, so the report's own codec arrives with it: `encodeReport`, `decodeReport` and `ReportError`, and with them the `HasCodec` instances on `Complaints` and everything a complaint is made of. #10 and #11 write no report to disk and carry none of that. `ClassRatchet` is the first rule class other than `ClassError` that any rule actually has. The constructor is declared earlier in the stack, which is the one piece of this kind I left where it was: taking it out would make `Class` a one-constructor type in #10 and #11 and change what `explain` prints twice over. Worth a second opinion. ## Review loop ``` nix flake check nix develop --command cabal test hopinion-test --test-options="--ai-executor" nix develop --command ./scripts/corpus-run.sh ./plan/scripts/check-plan.sh ```