Bootstrap: the spine, three checks, and the nix-ci integration The bootstrap from `plan/bootstrap.md`: the whole spine, three checks, one per level and per family shape, running over nix-ci, sydtest and itself. `nix flake check` is green. 178 tests pass. `scripts/corpus-run.sh` exits 0. ## The three checks | Rule | Level | nix-ci | sydtest | Expected | |---|---|---|---|---| | `comment/bare-todo` | module | 33 | 2 | 33 and 3 | | `hs/genvalid-in-gen-package` | package | 0 | 7 | 0 and 7 | | `test/genvalidspec-per-genvalid` | project | 122 | 0 | about 115 | Plus 114 modules abstained on nix-ci for Template Haskell, against an estimate of about 100. Every number is explained in `plan/corpus.md`. **39 findings audited by hand, 0 false positives.** **sydtest is 2 rather than 3** for `comment/bare-todo`, and that is the one number that disagrees. The comment hopinion stays quiet about heads a five-line block whose later lines carry two documentation URLs. The survey pattern looks for a reference on the marker's own line; hopinion looks in the whole comment block, because a reason spanning three lines is one comment. Worth a look: the disagreement is a judgement call, not a bug. ## What to review hardest `Hopinion/Comment.hs` is the critical path and where the judgement calls concentrate. `Hopinion/Extract.hs` is the other one: a fact that is wrong there is wrong for every rule that will ever read it. ## Self-weeding A suppression that has stopped being relevant fails the run. Every way one can stop being relevant is enumerated in the README table, with its exit code. The one that is easy to miss: **exactly one suppression answers for each finding**, so a second suppression over the same code is reported as suppressing nothing rather than riding along on the first one's back. Without that, suppressions accumulate silently, which is the failure the whole mechanism exists to prevent. ## What contact with real code settled Ten things, listed in the README. The ones that changed the design rather than confirming it: - A deriving clause with no strategy is a seventh instance origin, not stock. - `AttachedToStatement` has to carry the enclosing declaration, or the portable scope key is unrecoverable for a comment inside a declaration. - A suppression marker has to start a comment block, or a suppression written under the comment it is about is swallowed into it. - An inserted suppression goes below any comment already there and directly above the code. Above an existing comment its reason runs on into that comment; above a Haddock comment ormolu inserts a blank line and detaches it. This answers annotations.md's open question about surviving ormolu. - A comment at the end of a `do` block falls outside its declaration's span. ## Deliberately not done Fact fields no rule reads, the `RuleId` enum for all 125 rules, and the guide files moving into this repository. All listed in the README with why. ## Open, and blocking **The annotation syntax is not settled.** `[ok:rule-id] reason` is what annotations.md proposed and what this implements, but it is load-bearing enough that changing it later touches the parser, the matcher, `annotate`, the fixtures and 155 lines of nix-ci. Worth agreeing before the rule count grows. ## Trying it out ``` nix develop --command cabal test hopinion-test --test-options="--ai-executor" nix flake check scripts/corpus-run.sh ``` The nix-ci integration is on its `hopinion-bootstrap` branch: 15 lines of flake, 23 check derivations, and 155 annotations inserted by `hopinion annotate`, whose diff touches only comments.