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.