# Phase 20 acceptance register

Release 0.20.0 supplies the following gates. Never translate a test fixture PASS into a production PASS. Fill host-specific evidence only after executing against that host; no credentials or personal data belong in this file.

| Gate                        | Execution / evidence required                                                | Current scope                                                                     |
| --------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Local quality               | `npm run check`                                                              | Unit, injected HTTP/DOM, security, lint, formatting, schema syntax and CSS builds |
| Production dependency audit | `npm audit --omit=dev --audit-level=high`                                    | Registry advisory result, time-specific; does not scan OS/images                  |
| Live MySQL/Redis            | Compose target below                                                         | Prepared; requires Docker/service access                                          |
| Runtime preflight           | `npm run release:check` with runtime production environment                  | Prepared; requires target services                                                |
| Host/proxy/TLS              | `nginx -t`, trusted IP/spoofing, HTTPS cookies, firewall, restart/shutdown   | Target host required                                                              |
| Browser/device              | Actual Telegram native/web and admin browsers below                          | Target preview and devices required                                               |
| Backup/restore              | `backup-restore.md` into empty isolated recovery host                        | Real database/media drill required                                                |
| Load and recovery           | Baseline, burst, pool saturation, Redis outage, SIGTERM, worker restart      | Liveness microcheck is not business throughput evidence                           |
| Telegram/provider           | Verified bot, secret webhook, retries, consent and duplicate delivery review | Real endpoint and explicit operator actions required                              |

## Live isolated services

```sh
docker compose -f infrastructure/docker/compose.acceptance.yml up --build --abort-on-container-exit --exit-code-from acceptance
docker compose -f infrastructure/docker/compose.acceptance.yml down --volumes
```

The acceptance Compose project has no published database/Redis ports and uses temporary MySQL storage. Its fixed credentials are disposable test values, never production defaults. The dedicated runner uses actual MySQL and Redis, requires an empty database named `tte_acceptance` (or an allowlisted suffix), checks matching explicit confirmation and never drops an existing database. It runs all 23 migrations and six seeds, verifies replay/history, concurrent duplicate ledger rewards, competing withdrawal holds, immutable-entry triggers, separate outbox row locks and atomic Redis counters. It never starts a Telegram sender. If the first migration test fails, subsequent failures are not independent evidence; repair the first cause and rerun on a fresh isolated target.

To use an existing disposable test service, supply `NODE_ENV=test`, `ACCEPTANCE_DATABASE`, identical `ACCEPTANCE_CONFIRM`, `ACCEPTANCE_MYSQL_HOST/PORT/USER/PASSWORD` and `ACCEPTANCE_REDIS_URL`, then `npm run test:live`. These runner-only keys must not be placed in the application's `.env` file. The direct runner assumes an isolated network; it does not accept production credentials. Absence of services or configuration fails the command, rather than skipping tests.

## Browser and accessibility matrix

Test Android Telegram, iOS Telegram, Telegram Desktop, Telegram Web (iframe/cookie behavior), and current Chromium/Firefox/Safari admin browsers. At 320, 390, 768 and 1440 CSS pixels, and 200% zoom, verify no horizontal clipping, visible focus, labels, keyboard navigation, dialog focus trapping/return, Escape close, contrast, reduced motion, safe-area bottom navigation and touch targets. Use a screen reader for navigation/forms/error announcements. Record device/browser/version/screenshots and actual results.

Mini App: Telegram authentication expiry/reopen, light/dark sync, offline/retry, no tasks, task timer after app backgrounding, server rejection of early/duplicate claim, referrals, withdrawal eligibility/fees, history, announcements and logout. Free-only rewards remain the defined model; no paid entry/staking is introduced.

Admin: password/TOTP and recovery-code login, invitation renewal/cancellation, authenticator app switching, one-time recovery-code clearing, role changes/session revocation, scoped timed bans, stale-version conflicts, fraud evidence, two-person wallet correction, failed-job duplicate-risk acknowledgement, upload rejection and permission-denied UI. Never use production user data for screenshots.

`node acceptance/preview-shell.js` serves public shells only for local UI inspection, without a database or authenticated business API. It cannot validate successful authenticated journeys. Close it after inspection.

## Load / fault acceptance

`npm run test:load -- --origin=http://127.0.0.1:3000 --requests=100 --concurrency=5` provides a bounded loopback GET `/health/live` profile. It reports status counts and p95; only HTTP 200/429 are accepted. This is a liveness/rate-limit smoke profile, not a capacity claim.

For the intended workload, measure authenticated read and controlled mutation latency against disposable users, database CPU/locks/connections, Redis latency/memory, event-loop delay, outbox age and system memory. Increase traffic gradually under an operator-owned staging host, set a measured concurrency/latency target and stop on errors. Check repeat claims and wallet holds for invariants after contention. Record exact workload, hardware and target before calling a result PASS.

Stop Redis: protected HTTP operations must fail closed, readiness must degrade, and restart/reconnect must recover without unbounded queue growth. Saturate MySQL connections: readiness must return within its deadline, then recover. SIGTERM during requests/worker work must drain within the configured budget; kill/restart a worker and verify lease-based recovery and at-least-once delivery handling. Verify only one scheduler lease owns each scheduled run.

## Release decision

Record release hash, host, date, operator, check outputs, remaining defects, backup/RPO/RTO evidence and rollback owner. A code-quality PASS alone does not authorize launch. This repository does not automatically deploy, register webhooks, invite administrators, send messages or move money.
