# Production Acceptance Checklist — v3

Run this after deploying to a staging domain with MySQL 8+.

## Installation

- [ ] Back up existing database.
- [ ] New install: import `backend/database/schema.sql`.
- [ ] v2 upgrade: apply `backend/database/migration_v3_randomization.sql` once.
- [ ] Configure production `.env`.
- [ ] Set `APP_ENV=production`.
- [ ] Configure real OTP email/SMS delivery.
- [ ] Run `php backend/database/seed.php` only in staging/demo or immediately remove/change demo credentials.
- [ ] Run `./qa/run_checks.sh`.
- [ ] Run `npm install` and `npm run build` successfully.

## Super Admin UI

- [ ] Login as Super Admin.
- [ ] Dashboard loads without console/API errors.
- [ ] Companies page loads.
- [ ] Platform Question Bank loads.
- [ ] Create Reasoning single-choice question and select the correct option.
- [ ] Create Quantitative numeric question and correct numeric answer.
- [ ] Create English Reading MCQ.
- [ ] Create English Writing prompt with reference answer/rubric.
- [ ] Create English Speaking prompt with rubric.
- [ ] Create Coding prompt with reference solution/rubric.
- [ ] Verify no panel, table, field or modal overlaps at 1440px, 1024px, 768px, 430px and 390px widths.

## Company Portal

- [ ] Company sees Platform questions.
- [ ] Company sees its own questions.
- [ ] Company does not see another company's private questions.
- [ ] Create fixed/manual paper section.
- [ ] Create random paper section with a pool of at least 5 questions and random count 3.
- [ ] Attempt random count larger than pool; verify validation blocks publishing.
- [ ] Create candidate.
- [ ] Assign randomized paper.

## Randomization proof

Use two fresh candidates assigned the same randomized paper.

Run SQL:

```sql
SELECT assignment_id, section_id, question_id, sort_order
FROM assignment_questions
WHERE assignment_id IN (ASSIGNMENT_A, ASSIGNMENT_B)
ORDER BY assignment_id, section_id, sort_order;
```

- [ ] Each assignment has exactly the configured `question_count` per random section.
- [ ] Assignment A question set remains unchanged after browser refresh/logout/login.
- [ ] Assignment B can receive a different sample/order.
- [ ] `results.max_score` equals only the questions in that candidate's snapshot.

## Candidate objective questions

- [ ] Single choice saves.
- [ ] Multiple choice saves.
- [ ] True/false saves.
- [ ] Numeric saves.
- [ ] Correct responses receive configured marks.
- [ ] Wrong responses receive configured negative marks if enabled.
- [ ] Candidate API payload does not contain correct answers.

## English / coding / speaking

- [ ] English Reading objective scoring works.
- [ ] English Writing response saves.
- [ ] Coding response and language save.
- [ ] Speaking browser requests microphone permission.
- [ ] Speaking audio uploads within configured size limit.
- [ ] Examiner can open evaluation dialog.
- [ ] Evaluation modal remains inside viewport on mobile and desktop.
- [ ] Examiner can save score and remarks.
- [ ] Final result recalculates after last manual score.

## Timer and section behavior

- [ ] Each section has an independent timer.
- [ ] Refresh does not reset timer.
- [ ] Browser clock manipulation does not extend server timer.
- [ ] Answer save after expiry is rejected.
- [ ] Section reaches 00:00 and auto-submits.
- [ ] Manual Submit opens custom confirmation modal.
- [ ] Next section starts after submission.

## Tenant isolation tests

Create Company A and Company B.

- [ ] Company A cannot list Company B candidates.
- [ ] Company A cannot list Company B papers.
- [ ] Company A cannot list Company B private questions.
- [ ] Company A cannot assign Company B candidate.
- [ ] Company A cannot assign Company B paper.
- [ ] Candidate A cannot open Candidate B assignment URL.
- [ ] Examiner A cannot manually score Company B answer ID.
- [ ] Company A API credential cannot retrieve Company B external assignment ID.

## ATS/API

- [ ] Company creates API credential.
- [ ] `GET /api/integrations/v1/papers` returns only that company's published papers.
- [ ] `POST /api/integrations/v1/invitations` creates candidate + assignment.
- [ ] `assignment_questions` snapshot is created at invitation time.
- [ ] Retrying same external assignment ID is idempotent.
- [ ] Candidate completes test.
- [ ] Signed webhook reaches company callback.
- [ ] Polling status/result endpoint returns correct candidate result.

## UI/UX

- [ ] No horizontal body scroll on mobile.
- [ ] Tables scroll inside `.table-wrap` instead of page overflow.
- [ ] Question pool uses internal scroll when long.
- [ ] Modal overlay appears above sticky headers/sidebars.
- [ ] Long question text wraps.
- [ ] Modal body scrolls if content exceeds viewport.
- [ ] Modal footer remains usable.
- [ ] Escape/backdrop close works where allowed.
- [ ] Busy state prevents duplicate modal action.
- [ ] Browser console has no uncaught React errors.
- [ ] Network tab has no unexpected 4xx/5xx responses.

Do not call the product production-ready until every applicable staging item above passes.
