dCLV close leg: read priced captures only (the unfixed twin)

computeDirectionalForRow selected the latest closing_captures row with no
missed_reason filter, while its sibling attachClosingProb has had one since the
CLV instrument repair. closing_captures records a refusal for every prop x book
x side on every cycle after first pitch, so a refusal always carries a later
captured_at than the last real price - latest-first returned a refusal on
26,448 of 26,448 identity groups, and computeDirectionalClv refuses on a
missedReason. That is why dclv_state has been 'unknown' on 100% of rows since
Session 64.

Measured on 600 real settled rows: 100% unknown becomes flat 45.8%, negative
23.0%, positive 22.8%, unknown 8.3%. ClvBadge will render MOVED TOWARD US 114
and MOVED AWAY 117 per 600 - near-symmetric, which is the honest shape.

Existing rows do not recompute (first-computation-wins). The re-stamp is
described in BUILD-STATE, not run.

The grade is untouched. No rows deleted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-08-12 00:14:12 -04:00
parent 7c8ef8b12e
commit 2271f46ab2
4 changed files with 222 additions and 0 deletions
+31
View File
@@ -2085,6 +2085,37 @@ phased plan in the Session-57 conversation / BUILD-STATE Next section).
on the cron and had last drained through 08-08. Accrual counts must be read off
SETTLED rows, never graded ones.
## B1 — the dCLV close leg (non-obvious)
- **`computeDirectionalForRow` was the UNFIXED TWIN of the A1 CLV bug.** Its
sibling `attachClosingProb` (`ledgerService:455`) got `.is('missed_reason',
null)` during the CLV instrument repair; the close leg at `:625` never did, and
**nothing tested it** — which is exactly why it survived a fix made a few lines
above it. Both reads now filter, and a test asserts EVERY
`from('closing_captures')` read carries the filter.
- **The mechanism is a clock artifact, not a data gap.** `buildCaptureRows`
records a refusal for every prop × book × **both sides** on every ~20-minute
cycle AFTER first pitch, so a `missed_window` row ALWAYS has a later
`captured_at` than the last real price. `latest-first` without a filter
returned a refusal on **26,448 of 26,448** identity groups; `computeDirectionalClv`
refuses on a `missedReason`; hence `dclv_state = 'unknown'` on 100% of rows
since Session 64. **Filter BEFORE the limit** — "latest of the priced rows",
never "the latest row, hopefully priced".
- **MEASURED before/after on 600 real settled rows:** 100% `unknown` →
**flat 275 (45.8%) · negative 138 (23.0%) · positive 137 (22.8%) · unknown 50
(8.3%)**. Badge render: **MOVED TOWARD US 114 · MOVED AWAY 117 · no badge 369**.
The near-symmetry (114 vs 117) is the honest shape for a market that mostly
does not move — a lopsided result would have been the warning sign.
- **`positive` ≠ badge.** 137 positive but 114 rendered: `clvBadge` suppresses a
bare label when `locked_odds`/`closing_odds` can't form the receipt. Flat and
unknown render nothing by design.
- **EXISTING ROWS DO NOT RECOMPUTE.** `settleLedger:792` is
first-computation-wins (`row.dclv_computed_at ? null : compute…`) so a badge a
user saw can never flip. The 14,083 rows already stamped `unknown` stay
`unknown`; only rows settled from here carry real values. A re-stamp is a
separate, deliberate decision — described in BUILD-STATE, not run.
- **This makes `missed_window` genuinely unread**, which is the precondition the
reclaim order stopped on. The reclaim is still downstream and still needs R2.
## Active Skills
- vyndr-voice (all user-facing output)
- prop-analysis (grading methodology)