499671eb

By: Tom Sydney Kerckhove <syd@cs-syd.eu>

hopinion: static analysis that enforces the code review standards

A tool that mechanically checks what the comment, style, testing and process
guides ask for, and a Nix layer that runs it over a repository as one check.

Three rules ship, one at each level, which is what makes the level machinery
real rather than designed: CommentBareTodo over one module, HsGenValidInGenPackage
over one package, TestGenValidSpecPerGenValid over every module of every package.
Adding a fourth is a module and one line in a list.

The invariants it is built on, each of which fails the run when broken:

A rule says there is something to fix or there is not. There is no third answer,
because a checker whose silence can mean "I could not tell" is a checker whose
silence means nothing.

A suppression that answers for nothing is an error. That is what makes
on-by-default with unlimited local escapes safe: one that suppresses nothing,
one that suppresses more than one finding, and one written where it cannot
attach all fail, so suppressions cannot accumulate in silence.

A suppression the report offers must be one the parser accepts. The report tells
a reader what to write and where; if the parser then rejected it, the tool would
have lied.

The artifacts are not optional. Given any .hie directories, every module read
must have both a .hie and a .hi file or the run fails naming the module, because
an artifact tree missing one module is indistinguishable at the point of use
from no artifacts at all: both say nothing.

Facts cross a process boundary; source does not. A package is read once into a
SQLite store and the project layer answers from those stores alone, which is
what makes the Nix side one derivation per package plus one for the repository,
so changing one package rebuilds three derivations rather than all of them.

A rule owns its own table, its own metadata and its own query, so adding one
adds no case to anything central.

A repository decides which rules it runs, in hopinion.yaml at its root, and adds
rules of its own by writing an executable that calls hopinionWith. One file
serves both the development loop and the derivations, so the cheapest feedback
loop and the slowest cannot disagree about which rules they ran. Nothing about a
run is configured on a command line.

Where to start reading: README.md, then plan/context.md for what the current
shape came from and what is deliberately not built. CLAUDE.md has the feedback
loops, in order of increasing coverage and cost. plan/ is scaffolding for the
bootstrap and goes when the first version does.