94a55913

Give the facts somewhere to live

First of three. #11 and #12 sit on this.

A rule that cannot answer from one module needs the facts of more than one, and they have to be written down somewhere between the reading and the asking. This is that somewhere: one SQLite database, one table per fact, owned by the rule that writes it.

14 files, 963 lines. 220 tests.

## Why a database

What a rule above the module level does with facts is join them. So a rule brings its own schema the way it brings its own check, and the envelope never learns what is in that table.

The envelope tables are the ones every run writes whatever its rules are: which packages there were, which modules each was held to covering, what each module turned out to be, and which suppressions were written in it.

A fact is a row rather than a keyed entry. Two instances in one module are two facts, and there is nothing to key them on that is not invented.

Where the store lives is a two-constructor type rather than a path, because `:memory:` is not a file name: it is SQLite's word for having no file at all.

## Merging says what it cannot do

A store written by an executable with a rule this one lacks is a real thing, and SQLite's answer to it is `no such table` — a sentence about a name nobody chose, from a run that has not written its report yet. So the merge names the tables it has nowhere to put and the run reports them.

The format stamp is keyed on the version, so a store merged from one written in another format shows up as a second row and the run can say so. A tool version beside it would not work: keyed on the format, a second store's row at this same format is dropped by the insert, and two versions where one is never read is a check that reads as stronger than it is.

## No rule uses it yet

That is #11, and it is separate because this is a thing that can be tested on its own.

Worth knowing how the boundary got drawn: `weeder` decided it. Three store functions — `writePackageEnvelope`, `writeModuleEnvelope` and `annotationsOfPackage` — were reachable only from the level machinery, so a store-only PR failed `weeder-check` on exactly those three. Rather than defer them, `StoreSpec` now tests them: writing a package and finding it by name, and writing two packages' suppressions and reading back only one package's. The split made the store better tested rather than worse, which is the argument for it.

## 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 1s 11s 11s 13s 0s
Build 12s 0s 0s 12s 0s
Suite 0s 13s 13s 13s 0s

Timeline

0s10s