# CBB Moneyline — Complete Diagnosis

> This bug has been raised multiple times. This document captures the FULL
> root cause chain so it never needs to be re-investigated.

---

## The Chain of Failure

Three failures compound to produce the symptom "today's CBB games missing/broken."

### 1. Date Filter (FIXED — commit 4fe04f5)

`discover.ts` used UTC date (`toISOString()`) to compute "today." After 4 PM PT,
UTC rolls to the next day, excluding today's games. **Fixed** by switching to
local date components.

**Status:** Fixed. Not the current issue.

### 2. Team Code Parser — 4+3 Split Bug (UNFIXED)

**File:** `tickerParser.ts:117-135`

CBB team codes are 2-5 characters (e.g., BYU=3, UTAH=4, SDSU=4, USC=3, UK=2).
The parser handles 7-character matchups by always assuming 3+4:

```typescript
if (len === 7) {
  // Could be 3+4 or 4+3 - try 3+4 first (more common)
  return { awayCode: upper.slice(0, 3), homeCode: upper.slice(3) };
}
```

**Failures:**

- `UTAHBYU` → `UTA` + `HBYU` (should be `UTAH` + `BYU`)
- `SDSUBYU` → `SDS` + `UBYU` (should be `SDSU` + `BYU`)
- `UCLASC` → `UCL` + `ASC` (should be `UCLA` + `SC`... but this is 6 chars, handled differently)

Any 4+3 combination fails. CBB has many 4-char codes (UTAH, UCLA, SDSU, UNLV, etc.),
so this affects a significant percentage of games.

**Impact:** Wrong team codes propagate downstream:

- `groupMarketsByEvent` can't match `outcomeCode` to `awayCode`/`homeCode`
- `awayName`/`homeName` don't get set from market metadata
- `buildMoneylineGame` first lookup (by team code) fails

### 3. Fallback Name Matching — Depends on Event Fetch (FRAGILE)

**File:** `stream.ts:122-137`

The fallback in `buildMoneylineGame` matches by normalized participant display name.
This WORKS when:

- `event.awayName` and `event.homeName` are set (from event title like "Utah at BYU")
- `dm.outcomeName` is set (from `market.yes_sub_title` like "Utah")

**Fails when:**

- Event API fetch fails (rate limiting, network error) → no title → no names
- `yes_sub_title` is empty on the Kalshi market → no outcome name to match
- Names don't match after normalization (unlikely but possible with special characters)

**Additional fragility:** With potentially 100+ CBB events, the event fetch fires
100+ sequential API calls. Any failure silently drops the event's names.

### The Compound Effect

For games where the ticker parse is wrong AND the event fetch succeeds:
→ Fallback works → odds appear ✓

For games where the ticker parse is wrong AND the event fetch fails:
→ No fallback → both books null → all dashes ✗

For games where the ticker parse is correct (6-char or 8-char matchups):
→ Direct lookup works → odds appear ✓

**Result:** A subset of CBB games show with no data. How many depends on the
4+3 frequency AND event fetch success rate. On a bad day with rate limiting,
most games could be broken.

---

## The Fix

### Primary: Make name-based matching the default (not fallback)

In `buildMoneylineGame`, try name matching FIRST, then fall back to code matching.
This inverts the current priority:

```typescript
// CURRENT: code first, name fallback
let awayBook = marketBooks.get(event.awayCode) ?? null;
let homeBook = marketBooks.get(event.homeCode) ?? null;
if ((!awayBook || !homeBook) && event.awayName && event.homeName) {
  // name matching fallback...
}

// PROPOSED: name first, code fallback
// 1. Try name-based matching (always works if names are available)
// 2. Fall back to code-based matching (works for non-ambiguous sports)
```

### Secondary: Fix the parser for common 4-char CBB codes

Add a known-4-char-codes set for CBB (like `NFL_2CHAR_CODES` but for CBB 4-char):

```typescript
const CBB_4CHAR_CODES = new Set([
  'UTAH', 'UCLA', 'SDSU', 'UNLV', 'UCSB', 'ETSU', 'UMBC', 'UMKC',
  'UTEP', 'UTSA', 'MVSU', 'SIUE', 'FGCU', 'NJIT', 'IUPUI', ...
]);
```

Try 4+3 first if the first 4 chars are in the set, otherwise 3+4.

### Tertiary: Make event fetch more resilient

- Batch event fetches with rate limiting (avoid hammering API)
- Cache event data across refreshes (event titles don't change)
- If event fetch fails, still populate names from `yes_sub_title` in a second pass

---

## Why This Keeps Coming Back

1. First investigation found the date filter bug → fixed it
2. Date filter fix exposed the Polymarket CORS cascade → fixed it
3. Ticker parser bug was **documented** (section 7 of `cbb_moneyline_report.md`) but **not fixed**
4. Fallback name matching masks the parser bug sometimes but not always
5. Each session, the parser bug manifests differently depending on which games are 4+3 and whether event fetches succeed

**The fix is: don't guess team codes from tickers. Use the names Kalshi gives you.**

---

_Written 2026-02-17. This diagnosis is final._
