- FindingKey: stable SHA-256 over provider+masked+source+line
- Dedup: preserves first-seen order, returns drop count
- 8 unit tests covering stability, field sensitivity, order preservation
- Replace inline jsonFinding switch with output.Get() dispatch
- Add renderScanOutput helper used by RunE and tests
- Introduce version var + versionString() for SARIF tool metadata
- Update --output help to list table, json, sarif, csv
- Change root Execute to os.Exit(2) on RunE errors per OUT-06
(exit 0=clean, 1=findings, 2=tool error)
- Add cmd/keys.go with six subcommands backed by the Plan 04 query layer
- keys list prints masked findings with id/provider/confidence/source columns
and supports --provider/--verified/--limit/--unmask filters
- keys show <id> renders a finding fully unmasked with verify metadata
- keys export --format=json|csv reuses the formatter registry, atomic
file writes when --output is set
- keys copy <id> uses atotto/clipboard for clipboard handoff
- keys delete <id> prompts via cmd.InOrStdin unless --yes is passed
- keys verify <id> gates on verify.EnsureConsent, then updates the stored
row inline via UPDATE findings SET verify_* using db.SQL()
- Remove the keysCmd stub from cmd/stubs.go (single declaration)
- All subcommands read config via openDBWithKey() mirroring scan.go
SUMMARY.md for Plan 06-04: Filters struct + ListFindingsFiltered +
GetFinding + DeleteFinding on pkg/storage. Foundation for keys command
tree in Plan 06-05.
- Fixed 9-column header: id,provider,confidence,key,source,line,detected_at,verified,verify_status
- Uses encoding/csv for automatic quoting of commas/quotes in source paths
- Honors Options.Unmask for key column
- Registers under "csv" in output registry
- SARIFFormatter emits schema-valid SARIF 2.1.0 JSON for CI ingestion
- One rule per distinct provider, deduped in first-seen order
- Confidence mapped high/medium/low to error/warning/note
- startLine floored to 1 per SARIF spec requirement
- Registered under name 'sarif' via init()
- Filters struct: Provider, Verified (*bool), Limit, Offset
- ListFindingsFiltered: optional WHERE + ORDER BY created_at DESC, id DESC
- GetFinding: single-row lookup, propagates sql.ErrNoRows on miss
- DeleteFinding: returns RowsAffected so caller can distinguish hit/miss
- Shared scan/hydrate helpers decrypt key_value via existing Decrypt
- Renders findings as 2-space indented JSON array
- Honors Options.Unmask for key field exposure
- Omits empty verify fields via json omitempty
- Registers under "json" in output registry
- TableFormatter implements Formatter interface, registered as "table"
- Writes to arbitrary io.Writer instead of hardcoded os.Stdout
- Strips ANSI colors when writer is not a TTY or NO_COLOR is set
- Uses bundled tableStyles so plain/colored paths share one renderer
- PrintFindings retained as backward-compat wrapper delegating to Format
- When any finding has Verified=true, append a VERIFY column with colored
glyphs: ✓ live / ✗ dead / ⚠ rate / ! err / ? unk
- Per-finding VerifyMetadata is rendered on an indented secondary line
with deterministic (sorted) key ordering
- Backward compatible: unverified scans produce identical output to
pre-Phase-5 runs
- VerifyAll(ctx, findings, reg, workers) returns a result channel closed
after all findings are processed or ctx is cancelled.
- Default worker count of 10 when workers <= 0.
- Missing providers yield StatusUnknown with 'provider not found' error.
- Graceful context cancellation stops dispatch while still draining inflight.