S1 (a1): feature-promise audit — no claim survives unverified

PROMISE-AUDIT.md: every /pricing claim → verified/built/reworded.
BUILT (was vapor): alt line ladder + edge ranking (same-features regrade
at shifted lines, Desk-gated at the API), quarter-Kelly (engine quantile
P(win) x real captured odds — either missing → no sizing), free-tier
kill-condition locked previews. FIXED (was false): analyst 15/day cap vs
the Founder 'Unlimited reads' promise → analyst unlimited; every '40+
factors' claim (real count: 22 named features) reworded truthfully in 7
files. VERIFIED: cascade alerts (real, wired), phi correlation,
leg history, cross-book comparison, WC soccer, real-time feed.
Locked by tests/unit/promiseAudit.test.js. Jest now ignores
.claude/worktrees (parallel agents' suites no longer leak into runs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-11 14:24:41 -04:00
parent e4d2e79f95
commit d242b11b4b
20 changed files with 296 additions and 30 deletions
+10 -2
View File
@@ -64,8 +64,14 @@ function mapScanToGradeResult(input = {}) {
// richer blurred-paywall treatment returns in Phase G (Session 38).
const allSignals = toSignals(input.factors);
const signals = tier === 'free' ? allSignals.slice(0, 3) : allSignals;
// Session 62 (A1-S1) — the pricing page promises free users a LOCKED
// PREVIEW of kill conditions (the count + codes exist server-side via
// tierGating), not their absence. Free sees "⚠ TRAP — upgrade to see
// details"; paid sees the reasons.
const killConditions = tier === 'free'
? []
? (input.kill_conditions || [])
.map((k) => (k && k.code ? `${k.code} — upgrade to see details` : ''))
.filter(Boolean)
: (input.kill_conditions || []).map((k) => (typeof k === 'string' ? k : (k && k.reason) || '')).filter(Boolean);
return {
@@ -89,8 +95,10 @@ function mapScanToGradeResult(input = {}) {
killConditions,
books: Array.isArray(input.books) ? input.books : [],
altLadder: includeAlt && Array.isArray(input.alt_lines)
? input.alt_lines.map((a) => ({ line: a.line, grade: a.grade }))
? input.alt_lines.map((a) => ({ line: a.line, grade: a.grade, edge: a.edge_pct, base: a.base }))
: [],
// Session 62 (A1-S1) — quarter-Kelly (Desk; API already strips below).
kelly: includeAlt && input.kelly && input.kelly.pct != null ? input.kelly : undefined,
// Session 42 — Player Intelligence additions. All OPTIONAL + self-hiding:
// they only render once the engine supplies them (Session 43 data pipeline).
...buildIntelFields(input),