Item 7 — public accuracy reads the CLEAN ledger; BEAT CLOSE hidden until C4

Kev's call: the 30D accuracy surfaces must read TRUTH, not a cache that can't be
filtered. My earlier degraded-row exclusion only touched getModelAggregate
(Postgres); the public buckets/badge still read outcomeService (Redis outcome
log), which counts degraded projection-0 outcomes and has no field to filter on.

- /api/accuracy (AccuracyBadge) + /api/ledger/accuracy (buckets/ModelRecord)
  now source from the clean Postgres ledger aggregate via new
  ledgerService.getAccuracyView + accuracyBucketsFromAgg (model_value > 0
  excludes degraded rows). Same response shapes → no frontend change. Redis
  outcome log is now read by nothing public; it can age out or be rebuilt.

- BEAT CLOSE is a MEASURED-WRONG ZERO: captureClosing re-records the locked line
  as the "closing" line, so clv is flat on the whole sample and beat_close reads
  0% (comparing a number to itself). Full write-up: specs/audit-data/
  clv-capture-broken.md (the fix belongs to C4). Until then, beat_close_pct +
  clv_distribution are SUPPRESSED at the source (getModelAggregate, gated by
  clvCaptureReliable() / CLV_CAPTURE_RELIABLE=1). Every public surface already
  renders BEAT CLOSE only when non-null, so they all hide it now — no wrong zero
  anywhere. HIT RATE (real) is unaffected.

Suite 271/3261 green, web build exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-17 16:08:18 -04:00
parent 36e653d695
commit 89a2977f57
7 changed files with 222 additions and 35 deletions
+2
View File
@@ -65,6 +65,7 @@ describe('getModelAggregate scoping', () => {
});
test('user scope renders percentages at n≥20 like the public record', async () => {
process.env.CLV_CAPTURE_RELIABLE = '1'; // exercise the CLV math (suppressed by default, item 7)
const rows = [
...Array.from({ length: 14 }, () => ({ outcome: 'hit', clv_result: 'beat', grade: 'A' })),
...Array.from({ length: 7 }, () => ({ outcome: 'miss', clv_result: 'faded', grade: 'B' })),
@@ -73,5 +74,6 @@ describe('getModelAggregate scoping', () => {
expect(agg.settled).toBe(21);
expect(agg.hit_pct).toBe(Math.round((14 / 21) * 100));
expect(agg.beat_close_pct).toBe(Math.round((14 / 21) * 100));
delete process.env.CLV_CAPTURE_RELIABLE;
});
});