Wire BookComparison to the prop card (display-only, honest states)

BookComparison.tsx was built but UNROUTED (dead). Route it to the GradeResultCard
via a new self-fetching BookComparisonPanel that reads the live /api/books feed
(source:'bookprices' — the snapshot-locked, fenced, byte-identical store).

Contract fix (Review Zero 0.1): books frequently sit at DIFFERENT lines (WNBA DK
21.5 / FD 18.5; MLB 2/3), so BookComparison now renders EACH book's own line
per-row — never one shared header line implying a false same-number comparison.

Honest states: single-book (the common case for MLB) → one book, "One book
posting this prop.", NO crown/second row; multi-book → all books' own line+price,
NONE crowned (BOOK_CROWN_ENABLED=false — no best-price claim, verified live
crowned:false); no books → renders NULL (panel self-hides), never a placeholder.

No regression: only the always-empty inline d.books section was replaced; grade,
projection, PropLine line, and PriceTriplet price are untouched (wiring test
asserts them). Freshness (0.4): bookprices is written in the SAME snapshot that
locks the grade (intraday refresh touches neither gradedAt.line nor bookprices) —
same fresh, no stale-label needed. Web-only → grade byte-identical trivially.
Full suite 3851 green, web build exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VsztNChZ7vEvSR61AuMhD1
This commit is contained in:
Kev
2026-07-29 04:09:34 -04:00
parent 3a05447f77
commit 2ab2eeaa7d
5 changed files with 179 additions and 30 deletions
+9 -4
View File
@@ -78,9 +78,12 @@ describe('Phase D — GradeResultCard (the core moment)', () => {
expect(src).toContain('intel-surface');
expect(src).toContain('grade-reveal');
});
it('highlights the best book with green tint + green left border', () => {
expect(src).toContain('rgba(0,212,160,.13)');
expect(src).toContain("borderLeft: b.best ? '2px solid var(--g-a)'");
it('book comparison is the live BookComparisonPanel — no best-book highlight while the crown is gated OFF', () => {
// The old inline best-book green tint/border is gone: the crown is
// BOOK_CROWN_ENABLED=false, so the card makes NO best-price claim. The grid
// is now the wired BookComparisonPanel (honest breadth, no crown).
expect(src).toContain('<BookComparisonPanel');
expect(src).not.toContain("b.best ? 'rgba(0,212,160,.13)'");
});
it('renders kill conditions with amber border, gated on non-empty', () => {
expect(src).toContain('hasKill');
@@ -88,7 +91,9 @@ describe('Phase D — GradeResultCard (the core moment)', () => {
expect(src).toContain('KILL CONDITIONS');
});
it('self-hides books / alt ladder when empty', () => {
expect(src).toContain('hasBooks');
// Book comparison is now the live BookComparisonPanel (fed by /api/books);
// it self-hides (returns null) on honest-absent. Alt ladder still gates on hasAlt.
expect(src).toContain('BookComparisonPanel');
expect(src).toContain('hasAlt');
});
});