c60ec059

Suppress a finding, and turn a rule off

Stacked on #5. Review that first; this diff is against it.

Adds `CommentBareTodo` from `comments.md`, and with it the two things that make
a checker adoptable at all: a way to say no to a finding, and a way to turn a
rule off.

110 files, 3,359 lines. 226 tests.

## The keystone

**A suppression that answers for nothing is an error.** That is what makes
on-by-default with unlimited local escapes safe, and it is why there is no
config file of exceptions that nobody reads. All of these fail the run in their
own right:

- one that has outlived the finding it was written for
- one that answers for more than one finding at once
- one naming a rule nothing answers to, or a rule this repository turned off
- one with no reason
- one written where it cannot attach to anything
- one in a file no cabal component claims, which is watched by nobody

**A suppression the report offers must be one the parser accepts.** The report
tells you what to write and where. If the next run then rejected it, the tool
would have lied.

## Which rules run

`hopinion.yaml` at the repository root, not a flag, and there is no flag
anywhere. The development loop and CI read the same file, so they cannot
disagree about which rules they ran. Every way of getting that file wrong is
refused rather than ignored, because each one reads as a rule turned off and
behaves as a rule still running: an unknown key, a name that is not a rule id,
a rule nothing answers to, a document that is not a mapping.

## What this rests on

The comment attachment pass, computed here in an explicit pass over spans
rather than taken from where exact-print annotations put a comment. That
placement answers "where must this be reprinted", not "what is this about", and
the two differ exactly where it would matter. The most consequential decision
in it: **a blank line between a comment and the code below means the comment is
not attached to that code.**

## Still not here

One level, no fact store, nothing reads what the compiler wrote down. Neither
rule needs any of it. weeder removed `readChoicesFrom` on the way in, because
the flag it exists for belongs to a command that does not exist yet.

## Review loop

```
nix flake check
nix develop --command cabal test hopinion-test --test-options="--ai-executor"
```

Suite timing

Time to Start Worker time Duration Time to finish Idle
Config 0s 1s 1s 1s 0s
Eval 11s 12s 12s 24s 10s
Build 24s 11s 17s 41s 5s
Suite 0s 26s 41s 41s 15s

Timeline

0s20s40s