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.