# Code Review Report

**Batch**: 6 — AZ-463, AZ-469, AZ-476, AZ-477
**Date**: 2026-05-11
**Verdict**: PASS
**Mode**: Full (per-batch invocation by `/implement`)

## Inputs

- Task specs:
  - `_docs/02_tasks/todo/AZ-463_test_flight_selection_persistence.md` (4 ACs, 3 pts)
  - `_docs/02_tasks/todo/AZ-469_test_browser_support_responsive.md` (3 ACs, 2 pts)
  - `_docs/02_tasks/todo/AZ-476_test_upload_size_cap.md` (2 ACs, 2 pts)
  - `_docs/02_tasks/todo/AZ-477_test_settings_resilience.md` (3 ACs, 3 pts)
- Changed files (9 total, all under Blackbox Tests OWNED scope, plus one docs file):
  - `tests/flight_selection_persistence.test.tsx`
  - `tests/browser_support_responsive.test.tsx`
  - `tests/upload_size_cap.test.tsx`
  - `tests/settings_resilience.test.tsx`
  - `e2e/tests/flight_selection_persistence.e2e.ts`
  - `e2e/tests/browser_support_responsive.e2e.ts`
  - `e2e/tests/upload_size_cap.e2e.ts`
  - `e2e/tests/settings_resilience.e2e.ts`
  - `_docs/LESSONS.md` (new — autodev `B2` surface; one entry capturing the `vi.stubGlobal('URL', ...)` anti-pattern uncovered while debugging AZ-476)

## Findings

| # | Severity | Category | File:Line | Title |
|---|----------|----------|-----------|-------|
| — | — | — | — | None |

No Critical, High, Medium, or Low findings.

## Phase Walkthrough

### Phase 1 — Context Loading

All 4 task specs read; ACs catalogued; `module-layout.md` consulted for OWNED / READ-ONLY / FORBIDDEN envelopes. Every changed source file lives under `tests/**` or `e2e/**` — both `Owns` globs of the `Blackbox Tests` cross-cutting component (epic AZ-455). The single docs file (`_docs/LESSONS.md`) lives in `_docs/**`, owned by the orchestrator surface — not by any feature component. No file outside the envelope was modified.

### Phase 2 — Spec Compliance

| Task | AC | Test | Today | Drift documented |
|------|----|------|-------|------------------|
| AZ-463 | AC-1 (FT-P-16 PUT /settings/user persistence) | `tests/flight_selection_persistence.test.tsx` + e2e | PASS | — |
| AZ-463 | AC-2 (FT-P-17 rehydration on boot) | same | PASS | — |
| AZ-463 | AC-3 (NFT-RES-LIM-07 100-cycle listener leak guard) | `e2e/tests/flight_selection_persistence.e2e.ts` (long-running) + fast companion stub | gated by `RUN_LONG_RUNNING=1` per spec | runner-level config gating is a follow-up; per spec the AC lives in e2e (long-running) only |
| AZ-463 | AC-4 (NFT-RES-LIM-06 1 h SSE soak) | same | gated by `RUN_LONG_RUNNING=1` + `chromium` only (`performance.memory`) | same |
| AZ-469 | AC-1 (FT-P-34 cross-browser smoke) | `e2e/tests/browser_support_responsive.e2e.ts` | runs against both Chromium + Firefox projects in `playwright.config.ts` | — |
| AZ-469 | AC-2 (FT-P-35 mobile 480 px) | `tests/browser_support_responsive.test.tsx` + e2e | PASS — fast asserts Tailwind class shape, e2e asserts visibility | — |
| AZ-469 | AC-3 (FT-P-36 desktop 1024 px) | same | PASS | — |
| AZ-476 | AC-1 (FT-N-06 / NFT-RES-07 user-visible 413 error) | `tests/upload_size_cap.test.tsx` + e2e | `it.fails()` + control + `test.fail` (e2e) | drift — `MediaList.uploadFiles` swallows the failure silently and falls through to local mode; flips when toast + i18n key wired |
| AZ-476 | AC-2 (no `alert()` on the 413 path) | same | PASS (vacuous today — no error path runs at all) + e2e dialog spy | — |
| AZ-477 | AC-1 (FT-N-13 / NFT-RES-05 500 → button enabled + alert ≤ 2 s) | `tests/settings_resilience.test.tsx` + e2e | 2 × `it.fails()` (button + alert) + 1 control pinning the stuck-disabled drift | drift — `saveSystem` / `saveDirs` lack try/finally and an error region |
| AZ-477 | AC-2 (FT-N-14 / NFT-RES-06 network drop ≤ 2 s) | same | 2 × `it.fails()` (button + alert) | — |
| AZ-477 | AC-3 (NFT-PERF-09 deadline ≤ 2 s) | same | `it.fails()` measuring `performance.now()` between PUT response and alert visibility | — |

Every AC has at least one assertion; every documented drift is paired with either a control PASS test (pinning the current behavior) or a `test.fail` annotation, so each failure mode is observable today and the contract test flips green automatically once production lands the fix.

### Phase 3 — Test Coverage Hygiene

- 5 fast files / 4 e2e files / 0 production-source files modified.
- Total fast tests added: 19 (4 + 5 + 3 + 6 — control / `it.fails()` placement matches spec direction).
  - AZ-463: 5 (2 pass + 1 listener-leak companion + 2 controls).
  - AZ-469: 4 (3 pass + 1 cross-browser-stub).
  - AZ-476: 3 (1 `it.fails()` + 1 control + 1 PASS).
  - AZ-477: 6 (4 `it.fails()` + 1 control + 1 deadline `it.fails()`).
- Total e2e tests added: 9 (2 + 5 + 2 — long-running soaks gated; cross-browser smoke runs in both projects).
- All `it.fails()` placements paired with a control test that pins the current production drift (so the drift is asserted today and the contract test flips on production change). No `it.skip` was used to hide failures.

### Phase 4 — Hygiene & Drift

- 0 files added to `src/` (production code untouched — pure blackbox test batch).
- 1 file added under `_docs/` — `LESSONS.md`. The entry is bounded (≤ 6 lines body) per the rule contract. Surfaces the `vi.stubGlobal('URL', { ...URL, ... })` anti-pattern that destroyed the URL constructor and silently hid the 413 round-trip in AZ-476 during early debugging.
- The `tests/setup.ts` MSW boundary (`onUnhandledRequest: 'error'`) is preserved — every new test seeds its own handlers explicitly.
- AZ-477 installs a scoped `process.on('unhandledRejection')` handler that swallows ONLY the expected drift signature (`500: upstream failure` and network-error patterns). Any other rejection still throws — this is the same posture the production code will take once try/finally lands, just enforced at the test boundary in the meantime.

### Phase 5 — Static + Lint

- `bun run test:fast` — 22 files / 120 passed / 13 skipped / 46.52 s.
- `./scripts/run-tests.sh --static-only` — 24 / 24 static checks PASS / 39.72 s. No new banned-deps hits; no alert() leaks; no ML / signature / persistence / WS / SSR libs introduced.
- `ReadLints` clean on all 9 new files.
- `tsc --noEmit -p tsconfig.test.json` succeeded as part of STC-T1.

### Phase 6 — Self-Review

- Test rigs re-read end-to-end for naming clarity, AAA shape, and proper teardown of every globally mutated handle (`URL.createObjectURL`, `process.on('unhandledRejection')`, MSW handler resets via `afterEach`).
- `FlightSeed` helper in AZ-476 is intentionally local and tightly scoped — it sidesteps the async user-settings → `/api/flights/<id>` rehydration chain that AZ-463 covers separately, reducing flake without duplicating the rehydration assertion.
- Long comments in the test bodies explain *why* each `it.fails()` exists and what condition will flip it green; future readers can therefore tell intentional-drift from regression at a glance.

### Phase 7 — Architecture Compliance

- No layer-direction violations. Tests are leaves of the import graph; they import production sources but no production source imports them.
- No new cyclic dependencies (verified via `bunx tsc --noEmit` and `bun run build` in the static profile).
- `src/api/client.ts` is exercised but not modified — the contract for `api.put` / `api.upload` failure modes (rejected promises) is observed by the tests, not changed.
- `STC-S6` (no WS/GraphQL/gRPC/SSR deps) and `STC-S13` (no client-side persistence libs) re-confirm.

## Summary

PASS — the batch lands four blackbox-test tasks (12 ACs total) with zero production-code edits, every drift paired with a runnable control test, and full static + fast suite green.
