# SCHEMA.md — HYPERIDOL Power Rankings v1 dataset

Version: v1 · Observed: 2026-09-19 · Units: 50 groups + 351 idols

## Files
- `groups.json` — 50 group records: identity, columns (invisible backpack), market dims, MARKET OVR, band, rank
- `idols.json` — 351 idol records: identity, columns, market dims (SOL/INF/ACC/ANC), skill stats (all PENDING at v1)
- `groups.csv` / `idols.csv` — flat exports
- `power_rankings.db` — SQLite (tables `groups`, `idols`; one JSON blob per unit_id)
- `data_gaps.json` — every unresolved gap + conflict (nothing silently resolved)
- `spend_ledger.json` — exact Perplexity usage (calls, tokens, estimated cost — labeled estimated)
- `snapshots/2026-09-19.json` — immutable v1 snapshot

## The invisible backpack (per unit)
Every stat is a column object: `{value, source, observed, confidence, note}`.
`source` is always named (outlet + date) or the collection method
("Spotify artist page, direct snapshot"). Unknown stays null. Never 0-by-default:
N/A, PENDING, and evidenced-zero are separate states.

## MARKET dimensions (groups)
| Dim | Label | Columns (log-normalized, mean of available) |
|-----|-------|----------------------------------------------|
| MUS | Music | physical_sales_window_best, bb200_entries_career, hot100_entries_career |
| STG | Stage | biggest_tour_gross, tour_gross_window, musicshow_wins_career, mama_wins_total, mma_wins_total |
| FAN | Fandom | spotify_followers, youtube_subs, instagram_followers, tiktok_followers, x_followers |
| LEG | Legacy | daesangs_career, bb200_no1_albums_career, grammy_noms, debut_seniority, bbma_wins, vma_wins |
| MOM | Momentum | spotify_monthly_listeners (direct, 50/50, 2026-09-19), physical_sales_window_best, circle_digital_2025_best (rank, inverted) |

Column score: `100 * ln(1+v) / ln(1+v_max)` over groups with data (power-law aware).
Dim score: mean of available column scores.
Dim confidence: HIGH (≥60% columns + ≥50% verified-fresh), MEDIUM (≥40% columns), else LOW.
No columns at all → PENDING.

Excluded from scoring (provenance audit, kept in backpack, never feed OVR):
Kworb-sourced columns, unknown-provenance `kpopscore_*`, `ranker_group_rank`,
and legacy v0 outputs (`rank`, `tier`, `playstyle`).

## MARKET OVR (canonical at v1)
`OVR = 0.15*(MUS+STG+FAN+MOM) + 0.40*LEG` — 60% current-window / 40% all-time.
- Any PENDING dim → OVR = "Scouting"
- Any LOW dim → OVR shown as ±6 range (clamped to [0, 97])
- Launch cap 97. Mythic (94+) is founder-gated: never auto-awarded
  (`mythic_eligible` flag set for Suga's review; display band capped at Legendary).

Bands: Mythic 94+ (<1%) · Legendary 87–93 (~4%) · Epic 79–86 (~18%) ·
Rare 68–78 (~30%) · Common <68 (~46%).

## Idol market (v1)
SOL / INF / ACC: PENDING (no idol-level solo data at v1 — Phase 2).
ANC (group anchor): derived from the group's FAN dim, labeled as derived.
market_ovr: "Scouting" for all 351 idols (gating rule). Scouting board sorts by ANC.

## SKILL (idols) — v1
VOC / DAN / RAP / STP / CON / VSA: all PENDING, `skill_ovr` = "Scouting".
CRE (songwriting) stays a separate authorship credential, never mixed into OVR.
Rule: no skill score without named evidence. Phase 2 scouts this.

### Game front-end label mapping (2026-09-19 playtest)
The live game's idol cards use six stats — STAR, VOCAL, RAP, DANCE, VISUAL,
LIVE (each X of 99). Presentation layers map our skill fields to those labels:
VOC→VOCAL, DAN→DANCE, RAP→RAP, STP→STAR, CON→LIVE, VSA→VISUAL.
Labels only — all values remain PENDING at v1. Field names in the dataset are
unchanged; the mapping lives in the presentation layer so backend ingestion
stays stable. Idol position badges derive only from stated roles:
Main Vocalist→V, Main Rapper→R, Main Dancer→D, Center→S, Leader→L
(most idols have no role recorded → no badge, never guessed).

## Group DPT
Computed only: mean of top-4 member SKILL OVRs. PENDING at v1 (skills unscouted).

## Confidence system
HIGH = point value · MEDIUM = ±3 · LOW = ±6 + scouting badge ·
PENDING = "—" / Not scouted.

## Ranks
`rank_v1`: group rank by OVR midpoint (ranges sort by midpoint). Scouting groups unranked.
