skills/mmalogic/SKILL.md
Dedicated MMALogic/OctagonAI website agent. This command carries ALL domain knowledge for mmalogic.com — betting rules, canonical paths, anti-patterns, verification checklists, ground truth validation, and learned errors. Use this for ANY task invo
npx skillsauth add nhouseholder/nicks-claude-code-superpowers mmalogicInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Dedicated MMALogic/OctagonAI website agent. This command carries ALL domain knowledge for mmalogic.com — betting rules, canonical paths, anti-patterns, verification checklists, ground truth validation, and learned errors. Use this for ANY task involving the UFC website.
Triggers: "mmalogic", "octagonai", "ufc website", "ufc site", "update the site", "deploy the site", "fix the site", any mention of mmalogic.com
Run this FIRST to unlock webapp file edits. The mmalogic-site-guard.py hook blocks ALL edits to webapp/frontend/ unless this marker exists:
touch ~/.claude/.mmalogic_agent_active
echo "MMALogic agent activated — webapp edits unlocked"
Read ALL of these files. Do NOT skip any. Do NOT "apply mentally" — actually read them:
1. ~/.claude/memory/topics/ufc_website_maintenance_rules.md — 15-item verification checklist + 29 display rules
2. ~/.claude/memory/topics/ufc_canonical_paths.md — canonical directory paths
3. ~/.claude/memory/topics/ufc_betting_model_spec.md — 4-bet model specification + 12 scoring rules
4. ~/.claude/memory/topics/ufc_ground_truth_spec.md — ground truth registry + validator spec
5. ~/.claude/skills/site-update-protocol/SKILL.md — full update protocol
6. ~/.claude/anti-patterns.md — search for "UFC" entries
7. ~/.claude/recurring-bugs.md — search for "UFC" entries
After reading, summarize in one line what you loaded: "Loaded: 15-item checklist, canonical paths (ufc-predict/webapp/frontend/), 4-bet model, 12 scoring rules, ground truth spec, N anti-patterns, N recurring bugs."
# Where am I?
echo "Working directory: $(pwd)"
# Branch check — NEVER deploy from feature branches
BRANCH=$(git branch --show-current 2>/dev/null)
echo "Branch: $BRANCH"
if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ]; then
echo "⚠️ NOT ON MAIN — you are on branch: $BRANCH. Switch to main before deploying."
fi
# Is this iCloud? (iCloud folders diverge from GitHub)
[[ "$(pwd)" == *"Mobile Documents"* ]] && echo "⚠️ iCLOUD DIRECTORY — must verify freshness against GitHub"
# Clone fresh from GitHub for comparison
cd /tmp && rm -rf mmalogic-fresh
git clone https://github.com/nhouseholder/ufc-predict.git mmalogic-fresh --depth 1 2>&1 | tail -1
# Compare version
LOCAL_VER=$(cat ufc-predict/webapp/frontend/src/version.js 2>/dev/null || echo "NOT FOUND")
REMOTE_VER=$(cat /tmp/mmalogic-fresh/webapp/frontend/src/version.js 2>/dev/null || echo "NOT FOUND")
echo "Local version: $LOCAL_VER"
echo "GitHub version: $REMOTE_VER"
# If versions differ, ALWAYS use GitHub version
if [ "$LOCAL_VER" != "$REMOTE_VER" ]; then
echo "⚠️ VERSION MISMATCH — local is stale. Work from /tmp/mmalogic-fresh/ or pull latest."
fi
RULE: If local is stale, work from the fresh GitHub clone in /tmp/. Never edit stale files. RULE: If branch is not main, switch before deploying. Feature branch deploys overwrite production.
Before making ANY changes, record what currently works. This prevents regressions.
cat webapp/frontend/src/config/version.jsgrep -c "FIREBASE\|SUPABASE\|API_KEY\|SECRET" <files-you-will-edit> 2>/dev/null
_update/baseline.md (will be cleaned up before commit)RULE: If you can't state specific values for what currently works, you can't detect regressions. Record the baseline.
Based on what the user asked, route to the appropriate workflow:
| User says | Task type | Workflow | |-----------|-----------|---------| | "update the site" / "push changes" / "deploy" | Update & Deploy | → Step 3A | | "fix [bug]" / "something's broken" / screenshot | Debug | → Step 3B | | "audit the site" / "check everything" | Audit | → Step 3C | | "redesign" / "rebuild the look" | Redesign | → Step 3D | | "add [feature]" / "change [component]" | Feature Work | → Step 3E | | "check the site" / "how does it look" | Visual Verification | → Step 3F | | "clean rebuild" / "start fresh" / "nuke and rebuild" | Clean Rebuild | → Step 3G | | "review" / "--review" / "is this ready" / "pre-merge review" | Domain-Safe Review | → Step 3H |
The Latest Event table is NEVER generated retroactively by running the algorithm on a settled event. It is built in two phases:
Phase A — Pre-event cache (fight day, after final odds rescrape): scrape final sportsbook odds → run prediction path ONCE → apply all gates (SUB→DEC, slight-fav KO skip, heavy-fav KO skip, DEC method gate, combo round promotion, parlay selection) → write the resulting {picks, bets_placed, odds, skip_flags, predicted_method, predicted_round} envelope to event_picks_cache/UFC_<slug>.json. This is THE bet slate. Frozen at event start.
Phase B — Post-event grade (Sunday auto-cron, after UFCStats posts results): read cached picks → compare to actual UFCStats outcomes → compute P/L from cached odds → write actual_* + *_correct + *_pnl into ufc_profit_registry.json → regenerate algorithm_stats.json / hero totals / Latest Event table → push to production. Grade step is READ-ONLY with respect to picks and odds — it NEVER rewrites *_placed, predicted_*, or odds.
Why: 2026-04-17 audit of UFC 327 found Ulberg (-103), Reyes (-148), Swanson (-113) were placed with method_placed=true and combo_placed=true despite skip_ko_prop=true — retroactive ingest ran a code branch that diverged from the pre-event gate. Caching locks the slate; grading is pure arithmetic.
Legacy events pre-dating the cache system carry source: "retroactive_fallback" — flagged in registry so audit can find them. New events MUST be source: "cached_prediction".
python3 validate_registry.py (see Ground Truth Validation below)ufc-predict/webapp/frontend/public/data/diff -rq ufc-predict/webapp/frontend/src/ webapp/frontend/src/grep -c "FIREBASE\|SUPABASE\|API_KEY\|SECRET\|VITE_" webapp/frontend/.env* wrangler.toml 2>/dev/null
Counts must match before and after. If count drops, you removed a credential — UNDO immediately.cd ufc-predict/webapp/frontend && npm run build (NEVER from root webapp/)version.js and any "last updated" display.rm -rf _update/ 2>/dev/null before committinggit add -A. Stage only files you intentionally changed:
git add webapp/frontend/src/config/version.js webapp/frontend/public/data/*.json [other specific files]
git commit -m "v[X.Y.Z]: [description]"
git push origin main
git revert HEAD # revert the bad commit
git push origin main # CI redeploys the reverted state
Log failure to ~/.claude/anti-patterns.md with root cause./site-audit from ufc-predict/webapp/frontend//site-redesign from ufc-predict/webapp/frontend/ufc-predict/webapp/frontend/Run the full 15-item checklist from ufc_website_maintenance_rules.md:
Use this when the data pipeline is too corrupted to patch. Starts from scratch:
cd /path/to/ufc-predict
# 1. Backup current registry
cp ufc_profit_registry.json ufc_profit_registry_backup_$(date +%Y%m%d_%H%M%S).json
# 2. Run clean backtest (all events, walk-forward, cache-only for speed)
UFC_BACKTEST_MODE=1 UFC_CACHE_ONLY=1 UFC_NUM_EVENTS=71 python3 UFC_Alg_v4_fast_2026.py 2>&1 | tee backtest_clean_$(date +%Y%m%d).log
# 3. Verify the new registry is larger than or equal to backup
python3 -c "
import json
old = json.load(open('ufc_profit_registry_backup_*.json'))
new = json.load(open('ufc_profit_registry.json'))
old_events = len(old.get('events', []))
new_events = len(new.get('events', []))
print(f'Old: {old_events} events, New: {new_events} events')
assert new_events >= old_events, f'REGRESSION: new has fewer events ({new_events} < {old_events})'
print('✓ Event count OK')
"
# Run the ground truth validator on the fresh registry
python3 validate_registry.py
# If ANY rule fails, the backtest has a bug — do NOT proceed to frontend
# Fix the backtester, re-run, re-validate until ALL 12 rules pass
# The backtester outputs the registry. Now generate the display data files:
# algorithm_stats.json, prediction_output.json, etc.
# These must be derived FROM the validated registry, not independently computed.
# Copy to webapp data directory
cp ufc_profit_registry.json webapp/frontend/public/data/
cp algorithm_stats.json webapp/frontend/public/data/
cp prediction_output.json webapp/frontend/public/data/
# ... (all data files)
If the frontend components are broken/confused:
cd ufc-predict/webapp/frontend
npm run build
# Verify build succeeds with zero warnings about missing data
# Stage specific files — NEVER git add -A (prevents accidental backup/temp file commits)
git add ufc_profit_registry.json algorithm_stats.json prediction_output.json
git add webapp/frontend/public/data/*.json
git add webapp/frontend/src/config/version.js
# Add any other intentionally changed files by name
git commit -m "Clean rebuild: fresh backtest + validated registry + rebuilt frontend"
git push origin main # Triggers GitHub CI auto-deploy
# Wait for deploy, then visual verification via Claude in Chrome
# Run FULL 15-item checklist on live site
# If live site has regressions: git revert HEAD && git push origin main
After deploy, record the known-good state:
CLEAN REBUILD BASELINE — [date]
Registry: [event count] events, [bout count] bouts
ML: [W]-[L], [pnl]u | Method: [W]-[L], [pnl]u | Round: [W]-[L], [pnl]u
Combo: [W]-[L], [pnl]u | Parlay: [W]-[L], [pnl]u
Total: [total bets] bets, [combined pnl]u, [ROI]%
Validator: ALL 12 RULES PASS
Version: v[X.Y.Z]
Commit: [SHA]
Save this baseline to ~/.claude/memory/topics/ufc_clean_rebuild_baseline.md so future sessions know the last known-good state.
Multi-agent site review with MMALogic guardrails. This is /site-review adapted to respect the betting model, scoring pipeline, and display rules. All agents are constrained to prevent the exact types of breakage we've seen 15+ times.
Before dispatching agents, run the validator:
cd ufc-predict && python3 validate_registry.py
If validator fails, fix data FIRST — don't review broken state.
Write _review/CONTEXT.md with standard project info PLUS:
DOMAIN_CONSTRAINTS:
- DO NOT suggest changes to: validate_registry.py, ufc_profit_registry.json, any scoring logic,
algorithm files (UFC_Alg_*.py), constants.json, odds cache files
- DO NOT suggest "simplifying" P/L calculations — they implement 12 verified scoring rules
- DO NOT suggest removing bet types, columns, or parlay rows from any table
- DO NOT suggest changing odds format (MUST stay American: +150, -200)
- DO NOT suggest merging/refactoring EventBetsDropdown safePnl() — it's a safety net for 6 edge cases
- The 4+1 bet model is FINAL and user-confirmed. Do not suggest alternatives.
- confidence = raw differential (0.14–3.0+), NOT a percentage. Do not "fix" this.
PROTECTED FILES (read-only during review):
- webapp/frontend/.env*
- wrangler.toml
- validate_registry.py
- UFC_Alg_*.py
- constants.json
- ufc_odds_cache.json / ufc_prop_odds_cache.json
- ufc_profit_registry.json
SAFE TO REVIEW:
- webapp/frontend/src/components/ (visual/UX only, not scoring logic)
- webapp/frontend/src/pages/ (layout, navigation, UX)
- webapp/frontend/src/styles/ (CSS, Tailwind)
- webapp/frontend/public/ (assets, not data/*.json)
- Workers (live-tracker, API routes — security and reliability only)
Dispatch prompt:
You are reviewing mmalogic.com's frontend. Read
_review/CONTEXT.md— pay special attention to DOMAIN_CONSTRAINTS. Read~/.claude/skills/senior-frontend/SKILL.mdand~/.claude/skills/frontend-design/SKILL.md.Review ONLY visual/UX quality in
webapp/frontend/src/:
- Component architecture (are components too large? good composition?)
- User experience (loading states, empty states, error states, mobile responsiveness)
- Visual design quality (typography, spacing, color consistency)
- Performance (bundle size, lazy loading, image optimization)
- Accessibility (ARIA, keyboard nav, contrast)
HARD CONSTRAINTS:
- DO NOT suggest changes to ANY table column content, P/L calculation, or bet type display
- DO NOT suggest changes to safePnl(), scoring logic, or data processing
- DO NOT suggest removing ANY existing functionality
- If you see something in a protected file that looks odd, NOTE IT but do NOT suggest changing it
Rate findings P0-P3. End with TOP 3 FRONTEND IMPROVEMENTS (visual/UX only). Write to
_review/frontend_review.md.
Dispatch prompt:
You are reviewing mmalogic.com's backend infrastructure. Read
_review/CONTEXT.md. Read~/.claude/skills/senior-backend/SKILL.md.Review ONLY reliability and security:
- Live tracker Worker: error handling, timeout handling, Firestore write safety
- API routes: input validation, auth on protected endpoints
- CORS configuration
- Secrets management (are any hardcoded?)
- Firestore rules and data protection
- GitHub Actions workflows: are they robust?
HARD CONSTRAINTS:
- DO NOT suggest changes to scoring logic or the algorithm
- DO NOT suggest changes to the registry format or validator
- DO NOT suggest removing or restructuring the live tracker's cron schedule
- Focus on: "could this break in production?" and "is this secure?"
Rate findings P0-P3. End with TOP 3 RELIABILITY IMPROVEMENTS. Write to
_review/backend_review.md.
Dispatch prompt:
You are reviewing mmalogic.com as a product. Read
_review/CONTEXT.md. Read~/.claude/skills/senior-dev-mindset/SKILL.md.Review the site as a USER would experience it:
- Does the site deliver on its promise (UFC betting predictions with transparent P/L)?
- Is the navigation intuitive? Can users find what they need?
- Are there dead pages, stub features, or confusing flows?
- Is the data presentation clear to someone who bets on UFC?
- What's the #1 thing that would make this more valuable to a bettor?
HARD CONSTRAINTS:
- DO NOT suggest changing the betting model, bet types, or scoring rules
- DO NOT suggest "simplifying" the data — bettors want the detail
- Focus on: "what would make a UFC bettor come back every week?"
Rate findings P0-P3. End with TOP 3 PRODUCT IMPROVEMENTS. Write to
_review/fullstack_review.md.
After all agents complete:
MMALOGIC REVIEW COMPLETE
=========================
Validator: [ALL 12 RULES PASS ✓]
Reviewers: Frontend + Backend + Product (all MMALogic-constrained)
Protected files: [confirmed untouched]
Findings: [N total] (P0: X, P1: Y, P2: Z, P3: W)
Constraint violations filtered: [N findings removed for touching protected areas]
[Standard site-review report follows]
The review is READ-ONLY. It produces recommendations. The user decides what to act on, and any implementation goes through the normal /mmalogic workflow with validator checks.
MUST run after EVERY deploy, fix, or feature ship. No exceptions. Skipping this step is what caused 11 bugs going undetected on 2026-03-24. Items 16-21 were added 2026-04-15 after user mandate to catch table/data-freshness bugs the original 15 missed. Items 22-23 were added 2026-04-17 after UFC 327 ingest placed KO method+combo bets on Ulberg (-103) / Reyes (-148) / Swanson (-113) despite skip_ko_prop=true — confirming the v11.15 gate was bypassed and that retroactive recomputation is unsafe.
mcp__Claude_in_Chrome__navigate or PreviewUNABLE TO VERIFY for each item and explain whyLanding Page (/)
Picks Page (/picks) 5. Confidence display — Does it show raw diff values (e.g., "1.43 diff")? NOT percentages ("143% conf"). State the value shown for 1 pick. 6. R1 KO gating — If there's a KO R1 pick, does it show a Combo bet row? If not KO R1, is there NO round/combo shown? 7. Combo bets — Do KO R1 picks show a CMB tag/row? State which fight (if any) shows one. 8. SUB gating — If SUB is predicted, does it show "DEC (SUB→DEC)" not "by SUB"? 9. Both parlays — Is HC Parlay AND High ROI Parlay shown (if algorithm generates both)?
Event Detail (History page, expand 1 event) 10. Fighter loss = -1u everywhere — Find a losing pick. Does it show -1.00u in ML, Method, and Combo (if KO R1)? 11. Fighter win = odds-based payout — Find a winning method bet. Does it show the actual payout (e.g., +2.45u), NOT "+1.00u" flat? 12. Parlay row — Does every event show a parlay row with legs listed and P/L shown? 13. Summary header — Does the event header show ALL bet types? ML, Method, Combo, O/U, Parlay. NO Round column.
Admin/Optimizer (/admin) 14. Current values populated — Does the optimizer show actual numbers for params? NOT "—" everywhere. 15. All param categories — Are multiple param categories visible (Scoring, Advanced Features, System Integration)?
Table Updates & Data Freshness (filesystem + visual)
16. Cross-Component Table Parity — Open event detail (EventBetsDropdown), admin backtest tab (AdminBacktest BoutRow), landing latest event (EventSlideshow EventCard), LastWeekPicks, HistoryPage. All 5 show same columns in same order: [ML, Method, Combo, O/U, Parlay, Combined]. ZERO Round columns anywhere.
17. Parlay row present + totals match registry — On landing, the parlay card total equals registry parlay sum. LastWeekPicks and HistoryPage show parlay rows for each event. No component shows parlay 0W-0L while registry has non-zero totals.
18. Data freshness — 3-file reconciliation — Run:
bash python3 -c " import json h = json.load(open('webapp/frontend/public/data/hero_stats.json')) a = json.load(open('webapp/frontend/public/data/algorithm_stats.json')) r = json.load(open('ufc_profit_registry.json')) algo_combined = sum(a.get(f, 0) or 0 for f in ['ml_pnl','method_pnl','round_pnl','combo_pnl','ou_pnl','parlay_pnl']) parlay_keys = ['parlay','parlay_roi','parlay_hc3','parlay_roi3','parlay_hm3','parlay_hm2','parlay_mx2','parlay_ou3_over','parlay_ou3_under'] fight_keys = ['ml','method','combo','ou'] events = r.get('events', []) reg_combined = sum(sum((e.get(k) or {}).get('pnl', 0) or 0 for e in events) for k in fight_keys) + sum(sum((e.get(pk) or {}).get('pnl', 0) or 0 for e in events) for pk in parlay_keys) print(f'hero: {h.get(\"combined_pnl\")}u') print(f'algo: {algo_combined:.2f}u') print(f'reg: {reg_combined:.2f}u') "
All three must agree to ±0.01u. (Note: algorithm_stats uses flat ml_pnl+method_pnl+... fields, NOT totals.combined.)
19. Latest event is current — TWO-PART CHECK (both required)
**Part A — Registry vs Site parity:** Registry is sorted reverse-chronologically, so newest event is `.events[0]`, NOT `.events[-1]`.
```bash
python3 -c "import json; e=json.load(open('ufc_profit_registry.json'))['events'][0]; print(f'{e[\"event_name\"]} — {e[\"date\"]}')"
```
vs. the newest event shown on site (EventSlideshow top + LastWeekPicks). These must match.
**Part B — Registry vs real-world UFC calendar (catches the "site looks right but data is stale" bug):** Part A only proves the site mirrors the registry — it does NOT prove the registry was updated after the most recent completed UFC event. The post-event pipeline (backtester + track_results.py + sync_and_deploy) may not have run.
```bash
# Print registry's newest event date and today's date
python3 -c "
import json, datetime
e = json.load(open('ufc_profit_registry.json'))['events'][0]
reg_date = datetime.date.fromisoformat(e['date'])
today = datetime.date.today()
days = (today - reg_date).days
print(f'Registry newest: {e[\"event_name\"]} on {e[\"date\"]} ({days} days ago)')
print(f'Today: {today}')
print('⚠️ STALE' if days > 10 else '✓ fresh (<=10 days)')
"
```
Then **independently verify** via WebFetch (ufcstats.com/statistics/events/completed or google "most recent UFC event") what the most recently completed UFC event was. If the real-world most-recent completed event is NOT in the registry as `.events[0]`, the post-event pipeline hasn't been run — FAIL this item regardless of Part A result.
**Fail modes this catches:**
- Registry `.events[0]` is 2+ weeks old while a UFC event fired last weekend — post-event backtest never ran.
- Site display matches registry perfectly but both are stale.
- `track_results.py` ran but failed silently; winners never got ingested into the registry.
**Remediation on FAIL:** Run the post-event pipeline: `UFC_BACKTEST_MODE=1 UFC_CACHE_ONLY=1 python3 UFC_Alg_v4_fast_2026.py` → `fix_registry_placed_flags.py` → `patch_registry_from_archive.py` → `fix_registry_placed_flags.py` → `validate_registry_cells.py --strict` → sync data to `webapp/frontend/public/data/` → deploy.
20. Null-odds cell handling — Spot-check one event with any bout where method_odds or combo_odds is null. Wins with odds show real payout (never bare —). Losses always show -1.00u (never — or blank), even when odds are null.
21. Parlay type coverage (6 files) — Current 9 canonical parlay keys must appear in each source:
bash for f in pnl_contract.py track_results.py fix_registry_placed_flags.py sync_and_deploy.py build_event_analysis.py webapp/frontend/src/lib/parlayUtils.js; do echo "=== $f ===" grep -o 'parlay_mx2\|parlay_ou3_over\|parlay_ou3_under\|parlay_hm3\|parlay_hm2' "$f" | sort -u done
Each file must list all active parlay keys (no silent skips — DUAL_PATH_DIVERGENCE #7b).
22. Slight-favorite KO skip enforced (v11.15 gate) — Every bout in the latest event with predicted_method=='KO' AND -150 < odds < -100 must have skip_ko_prop=true AND method_placed=false AND combo_placed=false. Flag alone is NOT enough — placement MUST be suppressed.
bash python3 -c " import json ev = json.load(open('ufc_profit_registry.json'))['events'][0] bad = [] for b in ev['bouts']: odds = b.get('odds') pm = b.get('predicted_method') if pm == 'KO' and odds is not None and -150 < odds < -100: if b.get('method_placed') or b.get('combo_placed'): bad.append(f\"{b.get('picked')} ({odds}) pm={pm} method_placed={b.get('method_placed')} combo_placed={b.get('combo_placed')} skip_ko_prop={b.get('skip_ko_prop')}\") print('Slight-fav KO gate violations:', len(bad)) for line in bad: print(' -', line) "
Must report 0 violations. If any appear, the v11.15 gate is broken in the placement layer — FAIL this item regardless of Part A/B of item 19.
23. Latest Event was graded from source=cached_prediction (not retroactive_fallback) — verify via jq '.events[-1].source' ufc_profit_registry.json. Confirms the Sunday grade cron graded a frozen pre-fight cache rather than re-running the algorithm at ingest time.
bash jq '.events[-1] | {event_name, source}' ufc_profit_registry.json
Must print "source": "cached_prediction". Retroactive fallback is allowed ONLY for legacy events pre-dating the cache system — never on an event whose pre-scrape day had a working cache writer. Legacy events stay flagged in the registry so audit can find them.
After checking all 21 items, output this table in your response:
21-POINT CHECKLIST — [version] — [date]
Live site: mmalogic.com
==========================================================================
# Item Result Value
──────────────────────────────────────────────────────────────────────────
1 Combined P/L PASS +1926.30u
2 Event count PASS 100 events
3 Bet type cards PASS ML +190u / Method +190u / ...
4 ROI badge PASS ~107%
5 Confidence display PASS "1.43 diff" ✓
6 R1 KO gating PASS [fight name] shows CMB
7 Combo bets PASS [fight name] CMB row ✓
8 SUB gating N/A No SUB predictions this card
9 Both parlays PASS HC + ROI both shown
10 Fighter loss = -1u PASS [event] losing picks all -1u
11 Win = real odds PASS +2.45u (not +1.00u)
12 Parlay row PASS All events show parlay
13 Summary header PASS ML/M/C/O/P — no Round ✓
14 Admin params populated PASS All params show values
15 Admin categories PASS 3 categories visible
16 Table parity (5 comps) PASS EventBetsDropdown / AdminBacktest / EventSlideshow / LastWeekPicks / HistoryPage — all match
17 Parlay totals vs registry PASS Hero parlay +1270.63u = registry sum (±0.01u)
18 hero/algo/registry agree PASS All 3 report +1926.30u
19 Latest event is current PASS [A] reg=site ✓ [B] reg newest = real-world last completed UFC (X days ago) ✓
20 Null-odds handling PASS Losses -1.00u without odds; wins show real payout
21 Parlay type coverage (6 files) PASS 9 canonical keys present in pnl_contract / track_results / fix_registry / sync_and_deploy / build_event_analysis / parlayUtils.js
22 Slight-fav KO skip enforced PASS 0 violations — all KO picks with -150<ml<-100 have method_placed=false AND combo_placed=false
23 Cache-sourced latest event PASS source="cached_picks" (not retroactive_fallback)
==========================================================================
TOTAL: 23/23 PASS (or N/23 — list failures below)
FAILURES (if any):
- Item N: [specific value seen] vs [expected value] — [root cause if known]
If ANY item FAILS: do NOT write "MMALOGIC TASK COMPLETE" — investigate and fix the failure first. Re-run Step 4 after the fix.
The UFC event table has been wrong 15+ times across multiple sessions. The root cause: no automated validation between the backtester output and what the website displays. Bugs include wrong methods (KO instead of DEC due to missing tiebreaker), missing parlays, -1u on won bets, missing bet types, and incorrect P/L calculations.
This script lives at ufc-predict/validate_registry.py. It reads ufc_profit_registry.json and checks ALL 12 scoring rules from the betting model spec against every bout in every event.
| Rule | Check | Failure = | |------|-------|-----------| | 1 | Fighter loss → ALL placed bets = -1u | Data corruption | | 2 | Wins use real odds payout, not flat +1u | Payout calculation bug | | 3 | All prop bets require ML win | Scoring logic bug | | 4 | Combo wins require ML + method + round all correct | Combo scoring bug | | 5 | No bet placed when odds are null | Phantom bet bug | | 6 | No round/combo on DEC predictions | Gating bug | | 7 | Bet count matches available odds | Bet counting bug | | 8 | Method scoring: exact method match, KO/TKO grouped | Method matching bug | | 9 | Method and Round scored independently | Independence violation | | 10 | Parlay exists per event with correct legs | Parlay data bug | | 11 | Total bets = sum of fight bets + parlays | Arithmetic bug | | 12 | W + L = total bets per category | Balance check failure |
| Check | What It Catches | |-------|----------------| | R1 KO gating | Round/combo bets only on KO R1 predictions | | SUB gating | No SUB method bets below threshold | | DEC tiebreaker | When model is close between KO and DEC, verify tiebreaker fired | | Parlay completeness | Both HC and ROI parlays present (or documented why not) | | Odds reasonableness | No odds outside -2000 to +5000 range | | P/L math | For each bout: combined_pnl = ml_pnl + method_pnl + round_pnl + combo_pnl | | Event totals | Event-level totals match sum of bout-level P/L | | Registry totals | Header totals match sum of all event totals |
ALWAYS run before:
Step 3A)Step 3B — "the data is correct" requires validator proof)Step 3C)Step 3D)Step 3E)NEVER deploy if the validator fails. Fix the data first.
abs(odds) / (abs(odds) + 100) for favorites, 100 / (odds + 100) for underdogs.parlay (HC) and parlay_roi (ROI).parlay_candidates and _hc_legs to find them._in_dead_zone) but it's effectively disabled.parlay_mega (alongside parlay for HC and parlay_roi for ROI)parlays array with label "Mega Parlay"MEGA_PARLAY_THRESHOLD = -400parlay_odds_decimal internally — convert for display: dec >= 2.0 → +((dec-1)*100), dec < 2.0 → -(100/(dec-1)).f1/f2 keys. f1 = first fighter in the name1|||name2 key, f2 = second.f1_dec vs f2_dec — the one closer to +100/+200 belongs to the favorite.prediction_archive/. If they diverge, the archive is ground truth.# Compare last event's predictions: backtester vs prediction_archive
for bout in registry_event['bouts']:
archive_method = archive_picks[bout['picked']]['predicted_method']
if bout['predicted_method'] != archive_method:
print(f"DIVERGENCE: {bout['picked']} backtester={bout['predicted_method']} archive={archive_method}")
# Patch to archive value
mmalogic-live-tracker Cloudflare Worker at https://mmalogic-live-tracker.nikhouseholdr.workers.dev*/5 * * * * — fires every 5 min, only processes during Saturday 22:00-Sunday 09:00 UTClive_events/{slug}onSnapshot — zero manual work on fight nightcurl -X POST https://mmalogic-live-tracker.nikhouseholdr.workers.dev/FIREBASE_SA_KEY configured on the Workerpredicted_method == "SUB" → change to "DEC", null the round, re-score method (DEC vs actual)predicted_method == "KO" with predicted_round > 1 → null round_correct/combo_correct/round_pnl/combo_pnl (no bet placed)method_correct == true && method_pnl == null → look up prop odds from cache, fill payoutUFC_BACKTEST_MODE=1 UFC_CACHE_ONLY=1) to fix scoring. Manual patches diverge from the backtester's logic and cause inconsistencies. The ONE exception: patching the most recent event's method prediction when the backtester's walk-forward diverges from prediction_archive (see Backtester vs Prediction Archive above).round_correct and combo_correct MUST be null (not false). false = bet placed and lost. null = no bet placed.round_pnl = -1 AND combo_pnl = -1. Not null.event.ml.pnl == sum(bout.ml_pnl) for each bet type.__NO_PROPS__: true blocks re-scraping: The prop odds cache marks fights with no props found. On subsequent runs, the scraper skips these. To force re-scraping, delete the __NO_PROPS__ entries from ufc_prop_odds_cache.json before running.track_results.py) is the source of truth for actual results. algorithm_stats.json (from the backtester) may have different ML/Method numbers due to different scoring interpretations. When they disagree, update algorithm_stats.json to match registry-computed values.run-predictions workflow → GH Actions syncs constants from Firestore (UFC_SYNC_CONSTANTS=1) → runs algorithm → ingests predictions to site + commits to GitHub.GITHUB_TOKEN + INGEST_SECRET in Cloudflare Pages env vars, GOOGLE_APPLICATION_CREDENTIALS_JSON + INGEST_SECRET in GitHub repo secrets. All verified configured.method_pred without fallbackpredicted_method field is what the FightCard component reads. If it says "SUB", the site shows "by Submission" regardless of what final_bets says.predicted_round must be set to null. DEC has no round — a fight going to decision means it went the distance.pick_ml != null AND pick_ml < 0 AND pick_ml > -180?? 0 pattern makes null pass > -180 — NEVER use (pick_ml ?? 0) for this checkgetPublicFreePick() Firestore path (line ~66)getPublicFreePick() recomputation path (via pickByConfidence, line ~80)getFreePick() Firestore path (line ~125)["Fighter A", "Fighter B"])[{fighter: "A"}, {fighter: "B"}])legs.map(l => typeof l === 'string' ? l : l.fighter || l).join(' + ').join directly): LastWeekPicks.jsx, EventSlideshow.jsx (legs are always strings from registry)parlay_odds_decimal, some components looked for combined_decimal_odds. Use (parlay.parlay_odds_decimal || parlay.combined_decimal_odds) for compatibility.parlay.legs and decimal_odds to verify consistencywrangler deploy manually. Push to ufc-predict main branch → GitHub CI auto-deploys. Manual deploys risk deploying from the wrong directory.cat webapp/frontend/src/config/version.js — if it shows an OLD version, you're in the WRONG directory. ABORT.webapp/ is ARCHIVED (archive/webapp_ROOT_STALE_v10.68/). It froze at v10.68 and deploying from it destroyed months of work. NEVER build or deploy from it.ufc-predict/webapp/frontend/ → commit → push → CI builds and deploys automatically.if BACKTEST_MODE: block, ~line 8870+): scores historical fightsif not BACKTEST_MODE: block, ~line 8790+): builds prediction cards for live events# Check that all gates/boosts appear in both paths
grep -n "HEAVY_FAV_KO_BOOST\|SKIP_KO_SLIGHT_FAV\|_bet_method.*KO.*300\|_card_bet_method.*KO.*300" UFC_Alg_v4_fast_2026.py
# Each pattern should appear at LEAST twice (once per path)
UFC_Alg_v{MAJOR}_fast_{YEAR}.pyALG_VERSION constant at top of file (e.g., ALG_VERSION = "11.17")webapp/frontend/src/config/version.js MUST match the algorithm's internal versionALG_VERSION in the algorithm fileAPP_VERSION in version.js to matchbacktest_runs/EXPERIMENT_LOG.mdUFC_Alg_v{X}_fast_{YEAR}.ARCHIVED.py if keeping for reference.test_refinements.py and backtest_baseline.py reference the algorithm by filename — if the filename changes, update these references.cp ufc_profit_registry.json webapp/frontend/public/data/
cp algorithm_stats.json webapp/frontend/public/data/
cp prediction_output.json webapp/frontend/public/data/
EventBetsDropdown.jsx has a safePnl(correct, pnl, odds) function that computes P/L when registry data is incomplete:
correct === false → return -1 (loss is always -1u regardless of odds)correct === true && odds != null → return payout at oddspnl != null → return pnl (registry value takes priority)loadRegistry() prefers Firestore over static JSON when Firestore has bout-level data. If Firestore has stale 25-event data, the website shows 25 events even though the static JSON has 71.UFC_CACHE_ONLY=1 for backtests. Cache-only mode processes 71 events in ~5 minutes. Full scraping takes 30+ minutes and crashes.SYSTEM_BET_BOOST (+diff per agreeing system), SYSTEM_FADE_PENALTY (-diff per disagreeing system), SYS_THRESH_ADJ (lower pick threshold per net signal), SYS_METHOD_BOOST (method score amplification), SYS_SCORE_WEIGHT (base weight for score modification).SYSTEM_SCORE_WEIGHT = 0.0 means systems are independent (don't modify fighter scores). Non-zero means they actively modify the scoring pipeline.constants.json = single source of truth for all algorithm parametersalgorithm_data/constantsconstants.json at startup (or syncs from Firestore via UFC_SYNC_CONSTANTS=1)ufc_odds_cache.json and ufc_prop_odds_cache.json are IRREPLACEABLE historical data. Once an event passes, BFO pages disappear.UFC_CACHE_ONLY=1 is the default in backtest mode. All 71+ events have complete ML + prop odds cached. Never re-scrape during backtest — it wastes 20+ minutes and risks overwriting good data with stale or unavailable data.git add ufc_odds_cache.json ufc_prop_odds_cache.json && git commit.ufc-predict/webapp/frontend/ (NEVER root webapp/ — it's archived)ufc-predict/webapp/frontend/public/data/ufc-predict/ufc-predict/validate_registry.pynhouseholder/ufc-predictEvery /mmalogic session MUST update knowledge before ending. This is not a suggestion — it's a hard requirement. The output format below has a "Knowledge updated" field. If it says "none" AND you fixed a bug or learned something, you failed this step.
| Trigger | Where to write | Format |
|---------|---------------|--------|
| Fixed a bug | ~/.claude/anti-patterns.md | ### [SHORT_TITLE] — [DATE] with Context, Bug, Root cause, Fix, Applies when |
| Bug occurred before | ~/.claude/recurring-bugs.md | Add recurrence count + link to anti-pattern |
| New display rule | ufc_website_maintenance_rules.md | Add numbered rule to the appropriate section |
| New validator check needed | ufc_ground_truth_spec.md + validate_registry.py | Add check function + table entry |
| Scoring rule clarification | ufc_betting_model_spec.md | Add to appropriate rule section (ONLY with user approval) |
| Path or structure changed | ufc_canonical_paths.md | Update the affected path |
| Frontend component behavior | ufc_website_maintenance_rules.md | Add to Bug History table |
| Parlay logic learned | ufc_betting_model_spec.md Parlay Rules section | Document the edge case |
| Tiebreaker behavior | ufc_betting_model_spec.md | Document the tiebreaker rule |
| Odds scraper behavior | ufc_website_maintenance_rules.md rule 22/28 | Update scraper instructions |
After writing to any memory/anti-pattern file:
# Clone superpowers repo fresh (NEVER push from iCloud)
cd /tmp && rm -rf superpowers-sync
git clone https://github.com/nhouseholder/nicks-claude-code-superpowers.git superpowers-sync 2>&1 | tail -1
# Copy updated files
cp ~/.claude/anti-patterns.md /tmp/superpowers-sync/
cp ~/.claude/recurring-bugs.md /tmp/superpowers-sync/
cp ~/.claude/memory/topics/ufc_*.md /tmp/superpowers-sync/memory/topics/
cp ~/.claude/commands/mmalogic.md /tmp/superpowers-sync/commands/
# Commit and push
cd /tmp/superpowers-sync
git add -A
git commit -m "MMALogic session learning: [brief description of what was learned]"
git push origin main
# Cleanup
rm -rf /tmp/superpowers-sync
This sync MUST happen before the session ends. Knowledge that only lives in one session's memory is worthless — it dies when the session closes.
At session end, ask yourself:
If ANY answer is "no" when it should be "yes", do it now before writing the output format.
Every task ends with:
MMALOGIC TASK COMPLETE
======================
Task: [what was done]
Directory: [confirmed canonical path]
Branch: [main ✓ / other — explain why]
Version: [before] → [after] (bumped ✓ / N/A — no deploy)
Freshness: [verified against GitHub ✓]
Baseline: [N items recorded] — [all preserved ✓ / N regressions fixed]
Validator: [ALL 12 RULES PASS / N failures — list them]
15-item checklist: [N/15 passed — MUST show full table from Step 4, specific values for each item, not a summary]
Credentials: [verified intact ✓ — counts match before/after]
Changed files: [list of specific files staged]
Knowledge updated: [list of files updated — or "none (no new learnings)" with justification]
Knowledge synced to GitHub: [yes/no — commit SHA]
Deployed: [yes/no — if yes, via GitHub CI]
Live verification: [version + date + changes confirmed on mmalogic.com ✓]
tools
Unified context management and session continuity skill. Combines total-recall, strategic-compact, /ledger, and session continuity. Runs in background to preserve critical context across compaction and sessions.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
tools
Suggest /ultraplan for complex planning tasks on Claude Code CLI (2.1.91+ only). Research preview.
tools
UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 9 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient. Integrations: shadcn/ui MCP for component search and examples.