Files
steve ddcf42a3ce
Build & push image / build-and-push (push) Successful in 1m13s
CI / test (push) Successful in 10m39s
feat: gadfly-reports — findings store + scoreboard daemon
SQLite-backed HTTP store for Gadfly review findings, per-review run timings, and human/Claude grades, with a points-free per-model scoreboard. Pure fact store: it computes no points or rankings (the dashboard maps severity->points client-side and retunes without re-scoring). Findings are content-addressed by location so cross-model reports collapse for consensus; one grade per finding, latest wins. Pure-Go SQLite (CGO-free) + Docker image CI + tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 23:55:24 -04:00

3.3 KiB

gadfly-reports — Developer Guide

A small Go + SQLite HTTP daemon that stores Gadfly review findings, the per-review run timings, and human/Claude grades — and serves a points-free per-model scoreboard. The companion MCP client is gadfly-mcp.

This is a public, vibe-coded project (built largely by an AI agent). Keep that framing honest in the README; don't oversell it — it's a homelab-grade store, not a hardened product.

Core principle: store raw facts, score on the client

gadfly-reports records only facts: runs (timing/tokens), findings (content-addressed by location), reports (which model raised which finding), and grades (is_real + severity + usefulness). It never stores points or computes rankings. Mapping severity → points and any "value per minute / per token" ranking is the dashboard's job. This is deliberate — keep it that way: do not add a points column or a weighting config to the store. Retuning the curve must never require a migration or a re-score.

The severity vocabulary (trivial|small|medium|high|critical) in store.go is the only scoring-adjacent contract, and it's a closed set validated on write.

Architecture

main.go     subcommand dispatch (serve) + flags/env
store.go    SQLite schema + types + queries (runs/findings/reports/grades + latest_grades view)
server.go   net/http API (ServeMux method+path routes) + optional bearer auth
*_test.go   store + server end-to-end tests (consensus, latest-grade-wins, validation, auth)
Dockerfile  CGO-free build (pure-Go modernc sqlite) -> small alpine image
.gitea/workflows/  ci.yml (build/vet/test) + build-image.yml (publish :latest + :sha-<short>)

Data model. A finding is identified by sha256(repo|pr|lens|file|line)[:16]not by wording — so the same issue from different models (or a re-review) collapses to one finding with many reports. One grade per finding (history kept, latest wins via the latest_grades view).

Dependencies

  • modernc.org/sqlite (pure Go) — chosen so the binary is CGO-free and go run …@latest/the Docker build need no C toolchain. Don't swap in a cgo driver.
  • Otherwise stdlib only. The MCP SDK lives in gadfly-mcp, not here — keep this daemon lean.

Build / test

go build ./...
go vet ./...
gofmt -l .        # must be empty
go test -race ./...

Release / deploy

  • Push to main → CI builds and publishes :latest (+ :sha-<short>) to gitea.stevedudenhoeffer.com/steve/gadfly-reports.
  • Tag v* → publishes :<tag> (+ :latest).
  • CI needs repo secrets REGISTRY_USER / REGISTRY_PASSWORD to push the image (the Go build itself uses only public modules — no private-module creds needed).

When making changes

  • Keep the README API table in sync with server.go routes and store.go JSON tags — it is the contract gadfly (emit) and gadfly-mcp rely on. Stale docs are a bug.
  • Preserve the store-no-points principle (see above).
  • Add a test when you add logic. Keep gofmt clean and go vet quiet.
  • The schema uses CREATE TABLE IF NOT EXISTS migrations applied on Open; additive changes are fine, destructive ones need a real migration story (there isn't one yet — it's a homelab store).