# Phase 13 — Free-Only Mystery Box System Report

## Result

Phase 13 is complete. The web runtime now supports server-derived Mystery Box status, task-qualified free claims, fixed or cryptographically weighted rewards, inventory controls, immutable evidence, balanced ledger settlement, and permission-scoped Admin management.

The feature is deliberately free-only. Both the original schema and Phase 13 runtime prevent payment, purchase, deposit, stake, or user-supplied reward selection.

## Delivered

- Migration `018_harden_mystery_box_claims.sql` adds box/reward optimistic versions, one-active-box uniqueness, Admin ownership fields, claim eligibility/version/period snapshots, claim immutability triggers, a claim lookup index, and separate publish/claim-view permissions.
- Public `GET /api/v1/mystery-box/status` and `POST /api/v1/mystery-box/claim` routes.
- Server-only eligibility from task completions, UTC time, Admin timezone, account state, active bans, schedule, frequency, cooldown, and inventory.
- Fixed selection and bounded weighted selection using `node:crypto.randomInt`.
- HMAC idempotency and selection evidence; no raw idempotency key is stored.
- User, box, last-claim, and reward locking with a unique opportunity constraint for race protection.
- Inventory reservation, ledger posting, wallet projection, and immutable claim insertion inside one transaction.
- Admin CRUD for draft/paused boxes and rewards, versioned updates, lifecycle transitions, claim evidence, audit context, and four distinct permissions.
- Dashboard preview now treats an exhausted reward pool as unavailable.
- Unit, integration, security, schema, and regression coverage.

## Financial and safety invariants

- `access_cost_minor` is always `0` and `requires_purchase` is always false.
- The claim body must be empty; the server chooses the current box and reward.
- Monetary values remain decimal strings backed by `BIGINT` minor units.
- Every successful claim creates one balanced `mystery_reward` ledger transaction.
- A failed claim cannot leave inventory, ledger, balance projection, or claim data partially committed.
- Duplicate opportunity and idempotency constraints remain effective under concurrent requests.
- Claims are append-only evidence; database triggers block update and deletion.
- Client time, task totals, reward amounts, weights, inventory, and selection tickets are never trusted.

## Verification

The Phase 13 test additions cover timezone periods, cooldown anchoring, task thresholds, reward exhaustion, fixed/weighted selection boundaries, free-only payload rejection, successful atomic settlement, rollback paths, replay behavior, earning restrictions, stale Admin writes, publish validation, inventory protection, public authentication, CSRF, empty-body enforcement, idempotency requirements, permission separation, SQL parameterization, migration invariants, and secure selection source.

The final project quality gate passed with **232 of 232 tests**. It also passed ESLint with zero warnings, Prettier verification, environment-template validation, both production CSS builds, project-layout verification, and database-schema verification. The schema verifier confirmed 18 migrations, 6 seeds, 72 tables, 173 named constraints, 113 up statements, 107 down statements, and 19 seed statements.

## Operational note

Construction used offline schema dry runs and mocked service/HTTP tests. No deployment database, Telegram bot, user account, or real balance was accessed or changed. Operators must review `.env`, create a backup, apply migration `018`, configure rewards, and enable the feature flag before production use.

## Status

**Phase 13 status: COMPLETE — 232 of 232 tests and the full quality gate passed.**
