# Cumulative Code Review Report

**Batches**: 01–03 (AZ-456, AZ-457/459/465/481, AZ-458/467/468/482)
**Date**: 2026-05-11
**Cycle**: Phase A baseline, Step 6 — Implement Tests
**Mode**: cumulative (`/code-review` cumulative mode, all 7 phases)
**Trigger**: implement skill Step 14.5 — every K=3 batches
**Scope (changed files since baseline `729ad1c`)**: 60 paths
- `tests/**` (33 created): MSW server + 9 handler files, 8 fixture files, 4 helper files, 5 test files (`infrastructure`, `wire_contract`, `i18n`, `sse_lifecycle`), `setup.ts`, `i18n-allowlist.json`, `security/banned-deps.json`
- `src/**` (4 created): `api/client.test.ts`, `auth/AuthContext.test.tsx`, `auth/ProtectedRoute.test.tsx`, `components/Header.test.tsx`
- `e2e/**` (15 created): `playwright.config.ts`, `docker-compose.suite-e2e.yml`, OWM + tile stubs (Dockerfile + server), runner Dockerfile + entrypoint, fixture SQL, 5 e2e test files
- `scripts/**` (3 created + 2 modified): `check-banned-deps.mjs`, `check-i18n-coverage.mjs`, `check-ci-image-labels.mjs`; modified `run-tests.sh` and `run-performance-tests.sh`
- root config (3 created + 3 modified): `vitest.config.ts`, `tsconfig.test.json`, `tests/security/banned-deps.json` source-of-truth; modified `package.json`, `bun.lock`, `tsconfig.json`

**Verdict**: **PASS_WITH_WARNINGS**

---

## Phase 1 — Context

Inputs re-read:
- Task specs in current cycle's done/: AZ-456, AZ-457, AZ-458, AZ-459, AZ-465, AZ-467, AZ-468, AZ-481, AZ-482
- `_docs/02_document/architecture.md` + Architecture Vision (P1–P12)
- `_docs/02_document/module-layout.md` (`Blackbox Tests` envelope, the `Imports from` clarification commit `496b089`)
- `_docs/02_document/architecture_compliance_baseline.md` (F1–F9)
- `_docs/00_problem/restrictions.md`, `_docs/01_solution/solution.md`
- All three batch reports (`batch_01_report.md`, `batch_02_report.md`, `batch_03_report.md`)

## Phase 2 — Spec Compliance

Per-batch coverage already verified inline. Aggregated:
- AZ-456: 8/8 ACs
- AZ-457: 4/4 ACs (FT-P-01 / NFT-SEC-04 with `it.fails()` drift carry-overs)
- AZ-458: 3/3 ACs (AC-2 bearer rotation + annotation-status SSE drifts; e2e gated)
- AZ-459: 4/4 ACs (`it.fails()` for the 3 documented enum drifts; `verification_pending` skips for CombatReadiness + MediaType value-set)
- AZ-465: 4/4 ACs (FT-P-24/25 quarantined — detector + persistence not in production yet)
- AZ-467: 4/4 ACs (FT-P-32 spinner a11y `it.fails()`; FT-P-33 timeout + FT-N-03/05 RBAC `it.skip` quarantine)
- AZ-468: 3/3 ACs (FT-P-30/31 `it.fails()`; FT-N-09 `it.skip` quarantine)
- AZ-481: 3/3 ACs (image.title DRIFT reported; not blocking)
- AZ-482: 6/6 ACs (all PASS — deny-list checker is future-proofing)

**Total: 39/39 ACs covered**, with explicit drift / quarantine markers on every gap. No silent fail.

No `## Contract` sections in the test specs (test tasks consume contracts but don't redefine them); contract verification is delegated to AZ-459 (enum spec snapshot) and exercised in `tests/wire_contract.test.ts`.

## Phase 3 — Code Quality

Spot-checks across the new test infrastructure:
- Helper functions each carry a single responsibility — `seedBearer/clearBearer` (token state), `seedNavigateToLogin` (login redirect spy), `renderWithProviders` (composition root for tests), `createFakeEventSource/simulateSseStream` (SSE doubles), `jsonResponse/paginate/sse` (MSW response shorthand). Names match what each function does.
- No bare `catch` / `try` swallowing across new files.
- Arrange / Act / Assert pattern preserved across all `*.test.{ts,tsx}` files (verified via spot-check in `AuthContext.test.tsx`, `client.test.ts`, `wire_contract.test.ts`, `sse_lifecycle.test.tsx`, `Header.test.tsx`, `ProtectedRoute.test.tsx`).
- Test files do not narrate trivial code in comments; comments are reserved for `it.fails()` drift rationale and `it.skip` quarantine reason — both required for traceability.
- No `console.log` / `console.error` left in test bodies (only in `tests/setup.ts` for MSW logger config).
- Test helpers do not import each other circularly; helpers form a flat dependency tree (`render` → `i18n`, `auth` → `client`, `navigate` → `client`, `sse-mock` standalone).

No Phase 3 findings.

## Phase 4 — Security Quick-Scan

- No real secrets in fixtures: `tests/fixtures/seed_users.ts` uses placeholder argon2 hashes; bearer tokens use the `'test-bearer-default'` constant; OWM and tile stub URLs are stub-only (`/_owm/_health`, `/_tile/...`).
- No `eval`, no `shell=True`, no `subprocess` in scripts beyond `bun`/`tsc`/`vite` invocations.
- The static check refactoring in batch 3 (`scripts/check-banned-deps.mjs`) reads the deny-list from `tests/security/banned-deps.json` — JSON-only data input, regex applied to file paths and contents. No execution of file contents. No shell metachars passed to `child_process` (the script uses `node:fs`).
- AZ-482 explicitly strengthens posture: SEC-09 (OWM key) now also enforced against `dist/`; SEC-13 catches dropped legacy integrations (WhatsApp/Telegram/D-Bus/libsignal); SEC-14 anti-criterion catches accidental concurrent-edit reconcile.
- `tests/setup.ts` opts MSW into `'error'` on unhandled requests — drift in test wiring fails loudly rather than silently masking production calls.

No Phase 4 findings.

## Phase 5 — Performance

Wall-clock progression (host runs):

| Batch | Fast tests | Fast wall-clock | Static checks | Static wall-clock |
|-------|-----------|-----------------|---------------|-------------------|
| 01    | 11        | ~3 s            | 13            | ~26 s             |
| 02    | 38 + 4 skipped | ~3 s        | 19            | ~13 s             |
| 03    | 57 + 9 skipped | ~4.4 s      | 22            | ~12 s             |

- Per-test wall-clock budget remains well under the 5-minute target (`solution.md` perf budget).
- The dominant cost is `STC-T1` (`tsc --noEmit`) + `STC-B1` (`vite build`) at ~8 s combined; both unchanged across batches.
- No new pathological patterns: no nested loops on per-test setup, no synchronous file I/O in test bodies, fixtures preloaded once per process.
- The MSW handler set has grown from 0 → 9 handler files; handlers are O(1) match by URL pattern (msw v2.x trie), no N+1 risk introduced.

No Phase 5 findings.

## Phase 6 — Cross-Batch Consistency

Key cumulative concern: helpers / fixtures / static-check IDs / handler routes must not collide or duplicate across batches.

**Symbol audit** (across all batches):
- `tests/helpers/auth.ts` — `seedBearer`, `clearBearer` (1 producer, 4 consumers: `client.test.ts`, `AuthContext.test.tsx`, `ProtectedRoute.test.tsx`, `Header.test.tsx`)
- `tests/helpers/navigate.ts` — `seedNavigateToLogin` (1 producer, 1 consumer: `client.test.ts`)
- `tests/helpers/render.tsx` — `renderWithProviders` + screen/waitFor re-exports (1 producer, 4 consumers)
- `tests/helpers/sse-mock.ts` — `createFakeEventSource`, `simulateSseStream` (1 producer, 1 consumer: `sse_lifecycle.test.tsx`)
- `tests/msw/server.ts` — `server` (1 producer, 5 consumers)
- `tests/msw/helpers.ts` — `jsonResponse`, `errorResponse`, `noContent`, `paginate`, `latency`, `sse`, `dropResponse` (1 producer, multi-consumer)
- `tests/fixtures/seed_users.ts` — `opAlice`, `opBob`, `adminCarol`, `integratorDave`, `seedUsers`, `seedPermissions` (1 producer, multi-consumer; the same four user objects are reused across `ProtectedRoute.test.tsx` and `Header.test.tsx` with consistent IDs/permissions — no divergent definitions)
- `tests/fixtures/seed_flights.ts` — `seedFlights`, `liveGpsFlightId` — used by `Header.test.tsx` and `sse_lifecycle.test.tsx` consistently

**No duplicate symbol** across batches. **No fixture redefinition** (no second `opAlice` with different role/permissions; no second `liveGpsFlightId` constant).

**Static check IDs** (22 across `scripts/run-tests.sh`):
`STC-S1, S2, S5, S6, S13, N2, N3, N4, N5, SEC1, SEC1B, SEC2, SEC3, SEC4, SEC13, SEC14, FN15, FP22, FP23, CI11, T1, B1` — all unique, none reused. Naming convention: `STC-<topic-prefix><number>` consistently applied.

**MSW handler routes** (9 handler files, ~50 routes total):
Each handler file owns a disjoint URL prefix (`/admin/...`, `/flights/...`, `/annotations/...`, `/detect/...`, `/loader/...`, `/resource/...`, `/_owm/...`, `/tiles/...`). No overlap; no duplicate route definitions. Spot-checked `index.ts` to confirm `defaultHandlers` is the union without duplicates.

**Drift handling pattern uniformity**:
- `it.fails()` — used when the production element exists but the asserted attribute / behavior is missing today (e.g., FT-P-01 `credentials: 'include'`, FT-P-30/31 dropdown a11y, FT-P-32 spinner a11y, AC-2 bearer rotation re-deps).
- `it.skip` + `// QUARANTINE: ...` — used when the production capability is wholly absent (FT-N-09 Escape handler, FT-P-33 timeout fallback, FT-N-03/05 RBAC, FT-P-09/10 annotation-status SSE, FT-P-24/25 i18n detector + persistence).
- Both patterns include a control test asserting the gap, so the absence is provably demonstrated rather than tacitly assumed.

This pattern is uniform across batches 1–3. The `verification_pending` skip in AZ-459 is a third pattern (`it.skip` for "spec is provisional") — consistent within its task.

No Phase 6 findings beyond the carried-over interpretation note (see Phase 7 / Findings below).

## Phase 7 — Architecture Compliance

**Per-import inspection of test files** (cross-component edges):

| Test file | Cross-component imports | Verdict |
|-----------|-------------------------|---------|
| `src/api/client.test.ts` | `tests/msw/server`, `tests/helpers/auth`, `tests/helpers/navigate` | OK — only test infrastructure |
| `src/auth/AuthContext.test.tsx` | `tests/msw/server`, `tests/helpers/render`, `src/api/client` (`api`, `getToken`, `setToken` — public testability accessors landed by AZ-454/Step 4), `tests/helpers/auth` | OK |
| `src/auth/ProtectedRoute.test.tsx` | `tests/msw/server`, `tests/msw/helpers`, `tests/helpers/render`, `tests/helpers/auth`, `tests/fixtures/seed_users` | OK |
| `src/components/Header.test.tsx` | `tests/msw/server`, `tests/msw/helpers`, `tests/helpers/render`, `tests/helpers/auth`, `tests/fixtures/seed_flights`, `tests/fixtures/seed_users` | OK |
| `tests/i18n.test.tsx` | `src/i18n/i18n` (Public API of `00_foundation`) | OK |
| `tests/wire_contract.test.ts` | `tests/fixtures/enum_spec_snapshot` (test-only fixture) | OK |
| `tests/sse_lifecycle.test.tsx` | `src/api/sse` (`createSSE` — Public API), `src/api/client` (`setToken` — testability accessor) | OK |
| `tests/infrastructure.test.ts` | `tests/msw/server` | OK |

- **No imports of `*.internal.*` files**; no imports following `from '../../../<deep>'` patterns (all cross-references are exactly two levels: `src/<x>/<y>.test.tsx` → `../../tests/<helper>` is two levels, the maximum allowed by the test/source colocation pattern).
- **No new cyclic module dependencies** introduced — test files are leaves in the import graph.
- **No new duplicate symbols across components** — see Phase 6 audit. The only "duplicate-by-name" is `screen` and `waitFor` re-exported from `tests/helpers/render.tsx` to centralize the RTL surface; this is a proxy, not a rival definition.
- **No cross-cutting concern reimplemented locally** — error-envelope handling, MSW routing, fixture seeding, i18n bootstrap each have a single home; no test file open-codes them.

**Public API gap (still F4 from baseline)**: every test still imports by file-path granularity because `src/<component>/index.ts` barrels do not exist. This is the same baseline issue, neither resolved nor worsened by test work.

### Baseline Delta

Comparing current findings to `_docs/02_document/architecture_compliance_baseline.md` (`(file, category, rule)` triple):

**Carried over** — present at baseline, still present:

| # | File | Category | Rule |
|---|------|----------|------|
| F1 | `mission-planner/**` vs `src/features/flights/**` | Architecture | Convergence-pending duplication (deferred to Phase B) |
| F2 | `src/features/dataset/DatasetPage.tsx:9` | Architecture | Cross-feature same-layer edge (grandfathered) |
| F3 | `src/features/annotations/classColors.ts` | Architecture | Physical/logical owner split |
| F4 | every component | Architecture | No Public API barrels |
| F5 | `mission-planner/src/flightPlanning/{MapView,MiniMap}.tsx` | Architecture | Pre-existing cycle inside port-source |
| F6 | codebase-wide | Architecture | No `src/shared/` |
| F7 | `api.*` / `createSSE` call sites | Architecture | Hardcoded `/api/<service>/...` |
| F8 | `_docs/02_document/module-layout.md` | Architecture | Layering-table inconsistency |
| F9 | `mission-planner/src/{main,App,setupTests,vite-env}.tsx` | Architecture | Inert second Vite entry tree |

**Resolved** — present at baseline, NOT in current findings within the in-scope file set: **none**. (Test-implementation work correctly avoided touching production architecture; resolutions belong to Step 8 Refactor or Phase B feature cycles, not Step 6.)

**Newly introduced** — current findings absent at baseline: **none**. The "test helpers import production accessors" pattern was clarified out of finding status by `_docs/02_document/module-layout.md` commit `496b089` ("Clarify Blackbox Tests imports rule (helpers vs test bodies)"). It is now an established, documented exception, not a finding.

Per-category counts (current architecture findings in scope, excluding carried-over baseline): **0 Critical, 0 High, 0 Medium, 0 Low**. No verdict change.

## Findings (cumulative)

### F-CUM-1 — Drift production tasks accumulating (Low / Maintainability / carry-over from batches 2–3)

The three batches together documented **9 production drifts** that tests track via `it.fails()` or `it.skip` quarantine:

1. AZ-457 FT-P-01 — bootstrap refresh `credentials: 'include'` missing → `src/auth/AuthContext.tsx`
2. AZ-457 NFT-SEC-04 — broader `credentials: 'include'` claim narrow today → `src/api/client.ts`
3. AZ-459 — `AnnotationStatus`, `MediaStatus`, `Affiliation` enum drift vs `enum_spec_snapshot.json` → `src/types/index.ts`
4. AZ-458 NFT-PERF-03 / NFT-RES-02 — bearer rotation reconnect ≤5 s missing → `src/features/flights/FlightsPage.tsx:65-68` (deps array)
5. AZ-458 FT-P-09/10 / NFT-PERF-06 — annotation-status SSE not opened → `src/features/annotations/AnnotationsPage.tsx`
6. AZ-465 FT-P-24 — i18n detector path missing → `src/i18n/i18n.ts`
7. AZ-465 FT-P-25 — i18n persistence missing → `src/i18n/i18n.ts`
8. AZ-467 FT-P-32 — ProtectedRoute spinner a11y attrs missing → `src/auth/ProtectedRoute.tsx`
9. AZ-467 FT-P-33 / FT-N-03 / FT-N-05 — ProtectedRoute timeout + RBAC routes missing → `src/auth/ProtectedRoute.tsx`
10. AZ-468 FT-P-30 / FT-P-31 / FT-N-09 — Header dropdown a11y + Escape handler → `src/components/Header.tsx`
11. AZ-481 — `org.opencontainers.image.title` OCI label missing → `.woodpecker/build-arm.yml`

**Recommendation**: file these as Phase B feature tasks during Step 9 (New Task) once Phase A baseline closes. Each is a small, scoped fix; together they materially improve production posture. Do NOT lift them in this Step 6 window — Phase A scope ends at "tests in place"; flipping drifts is feature-cycle work.

This is a **non-blocking** finding; it's bookkeeping for the next phase. Verdict: PASS_WITH_WARNINGS contribution from this finding only.

### F-CUM-2 — Test-helper interpretation rule, now codified (informational)

Batches 1, 2, and 3 each surfaced the "test helpers import production accessors" finding as Low / Architecture / Interpretation. Commit `496b089` ("Clarify Blackbox Tests imports rule (helpers vs test bodies)") wrote the resolution into `_docs/02_document/module-layout.md`: black-box discipline applies to test bodies; setup helpers and composition-root wrappers may import production accessors.

**Status**: closed. Future cumulative reviews should NOT re-emit this finding. The Phase 7 inspection above already treats helper imports of `src/api/client` accessors as OK.

## Auto-Fix Attempts: 0
## Stuck Agents: None
## Verdict: PASS_WITH_WARNINGS

Reason: 0 Critical / 0 High; 1 Low / Maintainability (the production-drift bookkeeping in F-CUM-1). The verdict allows the implement skill to proceed to batch 4 without auto-fix gate intervention.

## Recommendation for Batch 4

Per batch-3 report: **AZ-466 (4) + AZ-475 (2) + AZ-462 (2) + AZ-460 (2) = 10 pts**. AZ-466 lands the `data-destructive` marker + `<DestructiveButton>` wrapper that other tasks (admin user delete, class delete, flight delete) rely on; landing it early is dependency-friendly for batch 5 (canvas / detection-classes / photo-mode / tile-split).

No cumulative-review-gated changes need to be applied before batch 4 starts.
