Commit Graph

5 Commits

Author SHA1 Message Date
gamer147
5c1db83967 feat(pack): /pack/set_rotation_starter_class + selected_class_id read-back
Implements Task 9 from docs/superpowers/plans/2026-06-13-decomp-only-followups.md
— the pre-purchase commit endpoint the client fires from StarterClassSelectDialog
before opening a RotationStarterCardPack. Without this the per-class draw path
from yesterday's work is unreachable: the dialog blocks on the AJAX call and
never advances to /pack/open.

Wire surface:
- POST /pack/set_rotation_starter_class { pack_id, class_id } → EmptyData.
  Validates 1..8, rejects unknown packs (404), non-RS packs (400), and second
  commits (400 — one-shot per pack per spec).
- PackConfigDto carries selected_class_id (nullable, omitted via global
  WhenWritingNull policy when unset). Placement verified against decomp at
  PackInfoTask.cs:86 — it's on the parent PackConfig, NOT the child gacha as
  the original plan text had it.
- /pack/open cross-checks request.class_id against the persisted choice for
  RS packs; rejects 400 on missing-commit or class-mismatch so a tampered
  request can't swap pools after the choice is locked.

Storage:
- ViewerPackStarterClass owned entity; (ViewerId, PackId) unique index via the
  pattern from project_owned_collection_unique_index memory.
- Migration PackStarterClass — composite PK + FK cascade verified against
  fresh Postgres bootstrap.

Tests (9 new in PackControllerSetRotationStarterClassTests):
- Round-trip set → /pack/info shows selected_class_id.
- Field absent before any commit.
- Invalid class (0/9/-1/100) → 400.
- Already-chosen rejection.
- Non-RS pack / unknown pack rejection.
- /pack/open rejects no-prior-commit AND class-mismatch; succeeds when class
  matches the persisted choice.

Full suite: 1552 passed (1543 + 9).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-27 20:06:46 -04:00
gamer147
30b8af2d16 feat(pack): /pack/get_leader_skin_owned_status (empty per-class buckets) 2026-06-12 23:26:29 -04:00
gamer147
61ae086332 fix(gacha-points): look up by odds_gacha_id, not parent_gacha_id
The two wire fields differ for seasonal packs (verified against
traffic_prod_all_gacha_exchange.ndjson — every captured request pairs
odds_gacha_id=16xxx with parent_gacha_id=10xxx). The OLD DTO docstring
assumed they were always equal; today's controller used
ParentGachaId, which lands on the base/family pack id (often a
synthesized disabled stub with no GachaPointConfig) and returns [].

Fix:
- GetGachaPointRewards and ExchangeGachaPoint now consume OddsGachaId.
- Update both DTO docstrings to document the seasonal-pack pattern.
- Regression test seeds (16015 enabled w/ GachaPointConfig, 10015
  disabled stub w/o config) and asserts the response uses 16015's
  catalog.

Symptom: opening pack 16015 (parent_gacha_id=16015 in /pack/open)
accrued gacha points correctly, but /pack/get_gacha_point_rewards with
{odds_gacha_id:16015, parent_gacha_id:10015} returned an empty list.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 23:30:18 -04:00
gamer147
ef1af8259e feat(pack): gacha-point endpoint DTOs 2026-05-28 22:56:33 -04:00
gamer147
79209bd70b Pack opening 2026-05-24 02:03:13 -04:00