Skip to content

Migration v5: reviews, findings, finding_events tables #396

Description

@ajianaz

Problem

Cora Code stores review/scan results as:

  • .cora/history/*.jsonDebtSnapshot only (aggregate counts, no per-finding detail)
  • CLI stdout (--format json) — full ReviewResponse/ScanResponse, but never persisted
  • cora scanno history at all (zero calls to save_snapshot)

This means there is no persistent, queryable record of individual findings. No way to track status (solved/false_positive/dismissed), no dedup, no trend per-file.

Solution

Add 3 new tables to cora.db via migration v5.

Schema

-- Header: one row per review/scan runCREATETABLEreviews (
id TEXTPRIMARY KEY, -- UUID v7 (time-sortable)
project_id INTEGERNOT NULLREFERENCES projects(id) ON DELETE CASCADE,
command TEXTNOT NULLCHECK(command IN (review,scan)),
commitTEXT, -- git short hash
branch TEXT,
reviewed_at TEXTNOT NULL DEFAULT (datetime(now)),
quality_score REAL,
gate_status TEXT, -- passed, failed, disabled
files_scanned INTEGERNOT NULL DEFAULT 0,
lines_scanned INTEGERNOT NULL DEFAULT 0,
provider TEXT, -- zai, openai, anthropic
model TEXT, -- glm-5-turbo, gpt-4o, etc
input_tokens INTEGER DEFAULT 0,
output_tokens INTEGER DEFAULT 0,
cost_usd REAL DEFAULT 0.0,
duration_ms INTEGER,
summary TEXT-- LLM-generated summary
);
CREATEINDEXidx_reviews_projectON reviews(project_id);
CREATEINDEXidx_reviews_commandON reviews(command);
CREATEINDEXidx_reviews_atON reviews(reviewed_at);
-- Per-finding detailCREATETABLEfindings (
id TEXTPRIMARY KEY, -- UUID
review_id TEXTNOT NULLREFERENCES reviews(id) ON DELETE CASCADE,
file TEXTNOT NULL,
lineINTEGER, -- NULL = file-level finding
end_line INTEGER, -- NULL = single-line
severity TEXTNOT NULLCHECK(severity IN (critical,major,minor,info)),
category TEXT, -- security, bug_risk, performance, best_practice, style, suggestion
title TEXTNOT NULL,
body TEXT,
suggested_fix TEXT,
status TEXTNOT NULL DEFAULT open
CHECK(status IN (open,solved,false_positive,wontfix,dismissed)),
fingerprint TEXT, -- hash(file + line + title_normalized) for cross-review dedup
created_at TEXTNOT NULL DEFAULT (datetime(now))
);
CREATEINDEXidx_findings_reviewON findings(review_id);
CREATEINDEXidx_findings_fileON findings(file);
CREATEINDEXidx_findings_statusON findings(status);
CREATEINDEXidx_findings_severityON findings(severity);
CREATEINDEXidx_findings_fpON findings(fingerprint);
-- Audit trail for status changesCREATETABLEfinding_events (
id INTEGERPRIMARY KEY AUTOINCREMENT,
finding_id TEXTNOT NULLREFERENCES findings(id) ON DELETE CASCADE,
event TEXTNOT NULLCHECK(event IN (
opened,auto_resolved,manually_resolved,
dismissed_fp,dismissed_wontfix,reopened
)),
commitTEXT,
reason TEXT,
created_at TEXTNOT NULL DEFAULT (datetime(now))
);
CREATEINDEXidx_fe_findingON finding_events(finding_id);

Design Decisions

DecisionRationale
UUID v7 for reviews.idTime-sortable, no auto-increment collision across offline runs
fingerprint fieldEnables cross-review dedup without LLM — same code pattern = same finding
Separate finding_eventsFull audit trail: who changed status, when, why, on which commit
end_line on findingsTree-sitter can provide exact node ranges (future: #358)
summary on reviewsStore LLM summary for historical context without re-running
Keep .cora/history/*.jsonBackward compat — cora debt CLI still reads these files

Dependencies

Acceptance Criteria

  • migrate_v5() runs cleanly on fresh and existing databases
  • All 3 tables created with correct constraints and indexes
  • Existing v1-v4 tables untouched
  • schema_version incremented to 5
  • Idempotent — running migration twice is safe

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions