`grade` is a band label over `p_win` and nothing else. `servedGrade.gradeFor`
reads exactly { p_win, refused, refusal_reason, insufficient_data,
factor_adjustment } -- no line, no odds, no edge, no side -- and
`gradeFreeze.TOLERANCES.MAX_LETTER_MISMATCH` is 0, so the served letter MUST
equal the band of the served probability.
The intraday refresh re-graded an adversely-moved prop AT THE CURRENT LINE and
then copied only `res.grade` onto the row for the LOCKED line, discarding the
p_win that produced it. Two defects in one write: the letter stopped matching
the probability beside it, and the letter answered a different proposition than
the claim it was stamped on.
MEASURED on ledger_entries in the current model era
(engine1@2026-08-07-fullwindow): 30,047 unrevised rows carry ZERO letter/p_win
mismatches; all 9 intraday-revised rows are mismatched. The revision was the
sole producer of incoherent authoritative rows. The pre-cutover era is
uninterpretable here (its `grade` was the engine index, not a p_win band, and
mismatches 49.4% of the time WITHOUT any revision) -- which is exactly why the
control was run before quoting a rate.
Simulated over the 57 historical revisions: in the current era, keeping the
published grade restores coherence on 9 of 9.
So the adverse branch now records the move and stops there. No grader is
invoked, no ledger revision is applied, and the grade-rank comparator is
deleted rather than parked -- a dead comparator beside the code is how the
mutation gets re-wired. A production coherence gate compares every output row
to its input on every BELIEF field and, on any divergence, publishes the
untouched slate instead.
History is untouched: the 57 existing `revised_from_grade` rows keep their
values, and the UI that renders them is unchanged. Forward-only.
Lineage is not touched and stays OFF.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CQJeAG8vcDoL5zkiaJyVb8
- specs/ROW-GRAMMAR.md: the row grammar law (slot order, color law, mark
law, mobile stacking, no-truncation), locked by tests/unit/rowGrammar.
StatStrip violations fixed: MovementChip before the grade (market
context before model output); ViabilityChips after the archetype
(identity is one contiguous run).
- Line-movement sparklines: intradayRefreshService.trackHistory captures
real {t,line} points per grade (seeded with the lock, deduped when
flat, capped 24) inside the snapshot write-back; StatStrip.LineSparkline
renders at >=3 points (green toward / amber against / dim flat).
- Last-10 dot strips: services/last10Dots (streaksService accessors) ->
/api/snapshot/:sport attaches last10_dots from rosterlogs:{sport};
StatStrip.DotStrip renders vs the LOCKED line, newest first.
- CLV distribution: getModelAggregate emits clv_distribution (7 signed
buckets, outliers clamped) only past the centralized n>=20 gate;
ledger MODEL header renders the bar strip.
- Global search: SearchModal (cmd-K via GlobalHosts + window.__search),
players via /api/players/search per sport + static lib/teams.js
(soccer deliberately absent); Nav search icon + Search first in the
mobile More sheet. Explore tab untouched.
- Landing LCP: fade-up floored at opacity .6 (hero h1 contentful on
first frame, S33 visible-floor rule) + IBM Plex Mono preload:false
(4 decorative font files off the slow-4G critical path).
2654 -> 2698 tests (226 suites) green; web build exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Odds-only refresh every 20 min during slate hours (noon–midnight ET),
in-process (INTRADAY_REFRESH=0 kill switch; skips full-snapshot slots):
- signed delta RELATIVE TO THE GRADED SIDE; direction is the signal.
- WITH the grade → STEAM badge, never a re-grade.
- AGAINST >=1.0 → re-grade THAT PROP ONLY at the real current line:
holds/refuses → VALUE (better entry, same read); drops → PUBLIC revision
(grade updates, revised_from_grade preserves the ORIGINAL forever, UI
strikethrough on slate strips + ledger cards).
- Every run recaptures closing_line/odds → the close is now refresh-
fidelity; SYNC goes live by dropping SNAPSHOT_EXPECTED_INTERVAL to 1200.
- Ticker MOVE events feed from the refresh (>=1.0 moves).
- QUOTA MATH: 36 runs/day/sport x 4 sports <= 144 PropLine calls/day vs
9,000/day free capacity (3 keys x 3,000). Re-grades are internal compute.
- POST /api/internal/refresh/all for manual runs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>