Files
gadfly-reports/main.go
T
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

75 lines
2.1 KiB
Go

// Command gadfly-reports is a small, durable store + scoreboard for Gadfly's review
// findings. Gadfly (and CI) report each model's findings and per-review timing
// here; a human or Claude later grades each finding (is_real + severity +
// usefulness). gadfly-reports stores only those RAW facts — it deliberately does NOT
// compute points or rankings, so the dashboard/client owns the scoring curve
// (severity -> points, value-per-minute, value-per-token) and can retune it
// without migrating or re-scoring stored data.
//
// Subcommands:
//
// gadfly-reports serve [flags] run the HTTP + SQLite store (the long-running daemon)
//
// The MCP server Claude calls to record grades lives in ./cmd/mcp, so the daemon
// stays lean; both are launchable with `go run <module>[/cmd/mcp]@latest`.
package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
)
func main() {
if len(os.Args) < 2 {
usage()
os.Exit(2)
}
switch os.Args[1] {
case "serve":
serveCmd(os.Args[2:])
default:
usage()
os.Exit(2)
}
}
func usage() {
fmt.Fprint(os.Stderr, `gadfly-reports — durable store + scoreboard for Gadfly review findings
Usage:
gadfly-reports serve [flags] run the HTTP + SQLite store
Run "gadfly-reports serve -h" for flags.
`)
}
func serveCmd(args []string) {
fs := flag.NewFlagSet("serve", flag.ExitOnError)
addr := fs.String("addr", envOr("GADFLY_REPORTS_ADDR", ":8090"), "listen address")
dbPath := fs.String("db", envOr("GADFLY_REPORTS_DB", "gadfly-reports.db"), "SQLite database path")
token := fs.String("token", os.Getenv("GADFLY_REPORTS_TOKEN"), "bearer token callers must present (empty = open)")
_ = fs.Parse(args)
store, err := Open(*dbPath)
if err != nil {
log.Fatalf("gadfly-reports: %v", err)
}
defer store.Close()
log.Printf("gadfly-reports: serving %s (db=%s, auth=%v)", *addr, *dbPath, *token != "")
srv := &http.Server{Addr: *addr, Handler: newServer(store, *token)}
if err := srv.ListenAndServe(); err != nil {
log.Fatalf("gadfly-reports: %v", err)
}
}
func envOr(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}