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
+85
View File
@@ -0,0 +1,85 @@
// Session 62 (A1-S1) — the feature-promise locks. Every pricing claim that
// was vapor is now real, and these tests keep it that way.
const { quarterKelly, americanToDecimal } = require('../../src/utils/kelly');
const { applyTierGating } = require('../../src/utils/tierGating');
const { getScanLimit } = require('../../src/config/tiers');
const fs = require('fs');
const path = require('path');
const WEB = path.join(__dirname, '..', '..', 'web', 'src');
const read = (rel) => fs.readFileSync(path.join(WEB, rel), 'utf8');
describe('quarter-Kelly (pricing: "Quarter-Kelly sizing recommendations")', () => {
test('computes from real probability × real odds', () => {
// p=0.55 at -110: b=0.909, f=(0.909*0.55-0.45)/0.909 ≈ 0.055 → quarter ≈ 1.4%
const k = quarterKelly(0.55, -110);
expect(k.quarter).toBeCloseTo(0.0138, 3);
expect(k.pct).toBeCloseTo(1.4, 1);
});
test('no edge → null (Kelly says: no bet, never a fabricated size)', () => {
expect(quarterKelly(0.5, -110)).toBeNull();
});
test('missing inputs → null, never defaults', () => {
expect(quarterKelly(null, -110)).toBeNull();
expect(quarterKelly(0.6, null)).toBeNull();
expect(quarterKelly(1.2, -110)).toBeNull();
});
test('american conversion both signs', () => {
expect(americanToDecimal(-110)).toBeCloseTo(1.909, 3);
expect(americanToDecimal(150)).toBeCloseTo(2.5, 3);
});
});
describe('Desk-only gate at the API (alt ladder + kelly never leave the server below Desk)', () => {
const RESULT = { grade: 'A', confidence: 70, alt_lines: [{ line: 1.5, grade: 'A' }], kelly: { pct: 1.4 }, reasoning: { summary: 'x' }, kill_conditions_triggered: [] };
test('desk keeps both', () => {
const g = applyTierGating(RESULT, 'desk');
expect(g.alt_lines).toBeTruthy();
expect(g.kelly).toBeTruthy();
});
test('analyst loses both, keeps reasoning', () => {
const g = applyTierGating(RESULT, 'analyst');
expect(g.alt_lines).toBeUndefined();
expect(g.kelly).toBeUndefined();
expect(g.reasoning.summary).toBe('x');
});
test('free loses both AND gets locked reasoning', () => {
const g = applyTierGating(RESULT, 'free');
expect(g.alt_lines).toBeUndefined();
expect(g.kelly).toBeUndefined();
expect(g.reasoning.locked).toBe(true);
});
});
describe('read limits match the pricing page', () => {
test('free 3/day · africa 10/day · analyst unlimited (Founder promise) · desk unlimited', () => {
expect(getScanLimit('free')).toBe(3);
expect(getScanLimit('africa')).toBe(10);
expect(getScanLimit('analyst')).toBe(Infinity);
expect(getScanLimit('desk')).toBe(Infinity);
});
});
describe('no unverifiable factor-count claims anywhere', () => {
it.each([
'components/Pricing.tsx', 'app/help/page.tsx', 'app/about/page.tsx',
'app/upgrade/desk/page.tsx', 'components/GradeCard.tsx',
'components/vyndr/GradeResultCard.tsx', 'components/vyndr/ProcessingGrade.tsx',
])('%s carries no "40+" claim', (rel) => {
expect(read(rel)).not.toContain('40+');
});
});
describe('free tier sees the promised LOCKED PREVIEW of kill conditions', () => {
const { mapScanToGradeResult } = require('../../web/src/lib/gradeAdapter');
test('free gets code-only teaser chips; analyst gets reasons', () => {
const input = {
player: 'X', stat: 'hits', line: 1.5, direction: 'over', grade: 'B',
kill_conditions: [{ code: 'TRAP', reason: 'Multiple trap signals firing.' }],
};
const free = mapScanToGradeResult({ ...input, tier: 'free' });
expect(free.killConditions).toEqual(['TRAP — upgrade to see details']);
const paid = mapScanToGradeResult({ ...input, tier: 'analyst' });
expect(paid.killConditions).toEqual(['Multiple trap signals firing.']);
});
});
+6 -4
View File
@@ -61,16 +61,18 @@ describe('scanLimit middleware', () => {
expect(lastHeaders['X-Scans-Limit']).toBe('3');
});
test('analyst tier gets 15 scans/day', () => {
// Session 62 (A1-S1) — analyst is UNLIMITED now (the Founder card promises
// "Unlimited reads"; the 15/day cap made the pricing page false).
test('analyst tier is unlimited (Founder promise)', () => {
const next = jest.fn();
let blockedAt = null;
for (let i = 0; i < 16; i += 1) {
for (let i = 0; i < 30; i += 1) {
const { req, res } = mockReqRes({ tier: 'analyst', userId: 'u-analyst' });
mw(req, res, next);
if (res.statusCode === 429 && blockedAt == null) blockedAt = i;
}
expect(blockedAt).toBe(15);
expect(next).toHaveBeenCalledTimes(15);
expect(blockedAt).toBeNull();
expect(next).toHaveBeenCalledTimes(30);
});
test('desk tier is unlimited — never blocks', () => {
+2 -2
View File
@@ -58,7 +58,7 @@ describe('tiers config', () => {
test('analyst opens reasoning + kill conditions + alerts', () => {
const a = TIERS.analyst;
expect(a.scans_per_day).toBe(15);
expect(a.scans_per_day).toBe(Infinity); // Session 62 — Founder 'Unlimited reads' promise
expect(a.reasoning_visible).toBe(true);
expect(a.kill_conditions_detail).toBe(true);
expect(a.alerts).toBe(true);
@@ -87,7 +87,7 @@ describe('tiers config', () => {
test('getScanLimit returns the per-tier daily cap', () => {
expect(getScanLimit('free')).toBe(3);
expect(getScanLimit('analyst')).toBe(15);
expect(getScanLimit('analyst')).toBe(Infinity);
expect(getScanLimit('desk')).toBe(Infinity);
});
+3 -1
View File
@@ -41,7 +41,9 @@ describe('Phase D — grade adapter (engine → §7 contract)', () => {
it('FREE tier teases 3 signals, hides kill conditions and the alt ladder', () => {
const out = adapter.mapScanToGradeResult({ ...base, tier: 'free' });
expect(out.signals.length).toBe(3);
expect(out.killConditions).toEqual([]);
// Session 62 (A1-S1) — free gets the pricing-promised LOCKED PREVIEW
// (code-only teaser), not silence.
expect(out.killConditions.every((k) => k.includes('upgrade to see details'))).toBe(true);
expect(out.altLadder).toEqual([]);
});