docs: address PR #51 review — rarity naming, OPS threshold, truncation invariant

- Add rarity name cross-reference table in Background section mapping PRD
  display names (Replacement/Reserve/Starter/All-Star/MVP/Hall of Fame) to
  codebase names (Common/Bronze/Silver/Gold/Diamond/HoF) with IDs
- Fix T4-2: correct Gold OPS threshold from 0.700 to 0.900 (confirmed in
  rarity_thresholds.py); add note that 0.700 is the Bronze floor
- Fix T4-1: restate truncation invariant as a single precise assertion —
  sum(columns) == 108 - truncated_amount — instead of two independent checks
  that can both pass while the sum is wrong for unrelated reasons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-03-24 16:07:18 -05:00
parent f2c09d09e6
commit 6f67cfec9a

View File

@ -26,6 +26,21 @@ probability system: 2d6 x 3 columns x 6 rows). Each Refractor tier (T1 through T
accumulated budget across all four tiers is 4.0 chances, equal to approximately 3.7% of the accumulated budget across all four tiers is 4.0 chances, equal to approximately 3.7% of the
108-chance total (4 / 108 ≈ 0.037). 108-chance total (4 / 108 ≈ 0.037).
**Rarity naming cross-reference:** The PRD chapters (`prd-evolution/`) use the player-facing
display names. The codebase and this spec use the internal names from `rarity_thresholds.py`.
They map as follows:
| PRD / Display Name | Codebase Name | ID |
|---|---|---|
| Replacement | Common | 5 |
| Reserve | Bronze | 4 |
| Starter | Silver | 3 |
| All-Star | Gold | 2 |
| MVP | Diamond | 1 |
| Hall of Fame | HoF | 99 |
All rarity references in this spec use the codebase names.
Rarity IDs in the codebase (from `rarity_thresholds.py`): Rarity IDs in the codebase (from `rarity_thresholds.py`):
| Rarity Name | ID | | Rarity Name | ID |
@ -74,8 +89,12 @@ Verify:
**Expected Outcome:** **Expected Outcome:**
Sum remains 108 after every boost under non-truncation conditions. Under truncation conditions Sum remains 108 after every boost under non-truncation conditions. Under truncation conditions
(a column hits 0), the sum is reduced by the truncated amount — the implementation discards the (a column hits 0), the final column sum must equal exactly `108 - truncated_amount` — where
excess rather than redistributing it. No column value falls below 0. `truncated_amount` is the portion of the 1.0-chance budget that was dropped due to the 0-floor
cap. This is a single combined assertion: `sum(columns) == 108 - truncated_amount`. Checking
"sum <= 108" and "truncated amount was discarded" as two independent conditions is insufficient
— a test can pass both checks while the sum is wrong for an unrelated reason (e.g., a positive
column also lost value due to a bug). No column value falls below 0.
**Risk If Failed:** **Risk If Failed:**
@ -121,7 +140,9 @@ Worked example for validation reference:
The cumulative 4.0-chance shift produces a ~3.7% total movement from negative to positive The cumulative 4.0-chance shift produces a ~3.7% total movement from negative to positive
outcomes. No single outcome column increases by more than 2.5 chances across the full T4 outcomes. No single outcome column increases by more than 2.5 chances across the full T4
journey under any profile. The card remains recognizably Bronze — it does not cross the Gold journey under any profile. The card remains recognizably Bronze — it does not cross the Gold
OPS threshold (0.700 for 2024/2025 thresholds) unless it was already near the boundary. OPS threshold (0.900 for 2024/2025 thresholds; confirmed in `rarity_thresholds.py`
`BATTER_THRESHOLDS_2024.gold` and `BATTER_THRESHOLDS_2025.gold`) unless it was already near
the boundary. Note: 0.700 is the Bronze floor (`bronze` field), not the Gold threshold.
**Risk If Failed:** **Risk If Failed:**