feat: built-in read-only dashboard at /ui + GET /runs
Serves a self-contained vanilla-JS dashboard (embedded via go:embed): a per-model performance table — runs, minutes, findings, confirmed/false-positive/ungraded, points, points-per-minute, points-per-run, by-severity — with drill-down filters (date range, repo, provider, model, lens, grade/severity), free-text search, and a click-to-scope findings detail table. Scoring stays client-side: the page has an editable points curve and computes points + value-per-minute in the browser, so the store remains point-free. Adds GET /runs (lists all runs, incl. zero-finding ones) so minutes/runs are filterable. The /ui shell is public (carries no data); data endpoints stay token-gated and the JS sends the token. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -137,6 +137,7 @@ type Run struct {
|
||||
InputTokens *int64 `json:"input_tokens,omitempty"`
|
||||
OutputTokens *int64 `json:"output_tokens,omitempty"`
|
||||
CostUSD *float64 `json:"cost_usd,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty"` // set on read by ListRuns; ignored by AddRun
|
||||
}
|
||||
|
||||
// AddRun upserts a run by run_id (a re-posted run overwrites timing/tokens).
|
||||
@@ -156,6 +157,44 @@ ON CONFLICT(run_id) DO UPDATE SET
|
||||
return err
|
||||
}
|
||||
|
||||
// ListRuns returns every run (oldest first), including runs that produced no
|
||||
// findings — so a dashboard can charge a model for all the time it spent, not
|
||||
// just the runs that surfaced something. Read-only.
|
||||
func (s *Store) ListRuns() ([]Run, error) {
|
||||
rows, err := s.db.Query(`
|
||||
SELECT run_id, repo, pr, model, provider, lenses, duration_secs, input_tokens, output_tokens, cost_usd, created_at
|
||||
FROM runs ORDER BY created_at, run_id`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var out []Run
|
||||
for rows.Next() {
|
||||
var r Run
|
||||
var in, outTok sql.NullInt64
|
||||
var cost sql.NullFloat64
|
||||
if err := rows.Scan(&r.RunID, &r.Repo, &r.PR, &r.Model, &r.Provider, &r.Lenses,
|
||||
&r.DurationSecs, &in, &outTok, &cost, &r.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.Valid {
|
||||
v := in.Int64
|
||||
r.InputTokens = &v
|
||||
}
|
||||
if outTok.Valid {
|
||||
v := outTok.Int64
|
||||
r.OutputTokens = &v
|
||||
}
|
||||
if cost.Valid {
|
||||
v := cost.Float64
|
||||
r.CostUSD = &v
|
||||
}
|
||||
out = append(out, r)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// ReportIn is one finding as a single model reported it.
|
||||
type ReportIn struct {
|
||||
Repo string `json:"repo"`
|
||||
|
||||
Reference in New Issue
Block a user