Problem Statement
ChainProof can diff findings between two commits (see the closed "structured diff engine" work), but every scan is otherwise stateless — nothing is retained between runs. Teams adopting ChainProof in CI have no way to answer "is our security posture improving over time," "which findings have been open the longest," or track trends across releases, without manually archiving reports. This is a reasonable expectation for a tool positioning itself as a continuous audit companion, as ChainProof's own README describes it.
Proposed Solution
Add a chainproof history command family backed by a local SQLite database (default .chainproof/history.db, git-ignored by default) that durably records every scan:
- Every
chainproof scan run (opt-in via --record-history, or always-on once .chainproof/history.db already exists) persists: commit SHA, branch, timestamp, and the full finding set (id, rule, file, line, severity, first-seen commit).
chainproof history summary [--since <date>] [--branch <name>] — reports finding counts by severity over time, and highlights newly introduced vs. resolved findings between the two most recent recorded scans.
chainproof history trend --format json|markdown — emits a time series suitable for plotting (date → severity counts) so teams can render it in their own dashboards or paste it into a markdown report.
chainproof history age — lists currently open findings sorted by how many recorded scans (or how much wall-clock time) they've persisted, surfacing long-lived unresolved risk.
- A finding is matched across scans using the same stable identity logic already used by the diff engine (rule + normalized file + line-range overlap), so a finding that moves a few lines due to unrelated edits is still tracked as the same finding rather than counted as both new and resolved.
Technical Scope
- New
packages/core/src/history/store.ts: SQLite schema and an insert/query layer.
- New
packages/core/src/history/trends.ts: aggregation logic for the summary/trend/age views, reusing the existing finding-identity matching from diff.ts.
- New
packages/cli/src/commands/history.ts wiring the summary, trend, and age subcommands into the existing CLI.
- Database file path configurable via
.chainproofrc.json; writes must be safe for concurrent CI runs (e.g., a busy-timeout/retry) without corrupting existing history.
- Tests: identity matching across a simulated 5-scan history with findings introduced, resolved, and reintroduced; trend aggregation correctness; concurrent-write safety.
Acceptance Criteria
- Running five successive scans against a fixture repo with deliberately evolving contracts produces a
history summary that correctly reports introduced/resolved counts at each step.
history age correctly ranks findings by first-seen date across the recorded history.
- History recording adds no more than a small, documented constant overhead to scan time.
- Documentation includes a recommended
.gitignore entry and a CI wiring example (e.g., persisting .chainproof/history.db as a CI cache/artifact between runs).
Estimated Scope
Approximately 700 lines of new TypeScript across the store, trend engine, CLI commands, and tests.
Problem Statement
ChainProof can diff findings between two commits (see the closed "structured diff engine" work), but every scan is otherwise stateless — nothing is retained between runs. Teams adopting ChainProof in CI have no way to answer "is our security posture improving over time," "which findings have been open the longest," or track trends across releases, without manually archiving reports. This is a reasonable expectation for a tool positioning itself as a continuous audit companion, as ChainProof's own README describes it.
Proposed Solution
Add a
chainproof historycommand family backed by a local SQLite database (default.chainproof/history.db, git-ignored by default) that durably records every scan:chainproof scanrun (opt-in via--record-history, or always-on once.chainproof/history.dbalready exists) persists: commit SHA, branch, timestamp, and the full finding set (id, rule, file, line, severity, first-seen commit).chainproof history summary [--since <date>] [--branch <name>]— reports finding counts by severity over time, and highlights newly introduced vs. resolved findings between the two most recent recorded scans.chainproof history trend --format json|markdown— emits a time series suitable for plotting (date → severity counts) so teams can render it in their own dashboards or paste it into a markdown report.chainproof history age— lists currently open findings sorted by how many recorded scans (or how much wall-clock time) they've persisted, surfacing long-lived unresolved risk.Technical Scope
packages/core/src/history/store.ts: SQLite schema and an insert/query layer.packages/core/src/history/trends.ts: aggregation logic for the summary/trend/age views, reusing the existing finding-identity matching fromdiff.ts.packages/cli/src/commands/history.tswiring thesummary,trend, andagesubcommands into the existing CLI..chainproofrc.json; writes must be safe for concurrent CI runs (e.g., a busy-timeout/retry) without corrupting existing history.Acceptance Criteria
history summarythat correctly reports introduced/resolved counts at each step.history agecorrectly ranks findings by first-seen date across the recorded history..gitignoreentry and a CI wiring example (e.g., persisting.chainproof/history.dbas a CI cache/artifact between runs).Estimated Scope
Approximately 700 lines of new TypeScript across the store, trend engine, CLI commands, and tests.