- AZ-463 flight selection persistence (FT-P-16) + rehydration
on boot (FT-P-17) PASS at the wire; 100-cycle leak guard
(NFT-RES-LIM-07) and 1h SSE soak (NFT-RES-LIM-06)
scaffolded as RUN_LONG_RUNNING-gated e2e companions.
- AZ-469 browser-support smoke (FT-P-34) runs in both
Chromium and Firefox via the existing playwright config;
responsive variants (FT-P-35 480px / FT-P-36 1024px) PASS
in fast (Tailwind class shape) and e2e (visibility).
- AZ-476 upload 501 MB -> 413: AC-1 user-visible error is
drift today (uploadFiles silently falls through to local
mode); it.fails() + control + e2e test.fail. AC-2 no-alert
PASS via dialog spy.
- AZ-477 settings save 500 / network drop: AC-1+AC-2+AC-3
all drift today (no try/finally, no error region, deadline
unmeasurable); 4 it.fails() + control pinning the stuck-
disabled drift; e2e companions test.fail mirror it.
- LESSONS.md seeded: vi.stubGlobal('URL', {...URL,...})
destroys the URL constructor and breaks new URL(...) in
MSW; patch the methods directly instead.
Code review: PASS (0 findings). Fast: 22/22 files, 120
passed / 13 skipped. Static: 24/24 PASS.
Co-authored-by: Cursor <cursoragent@cursor.com>
10 KiB
Batch Report
Batch: 06 Tasks: AZ-463 (Flight selection persistence + soaks), AZ-469 (Browser support + responsive variants), AZ-476 (Upload >500 MB → 413), AZ-477 (Settings save resilience + 2 s budget) Date: 2026-05-11 Cycle: Phase A baseline, Step 6 — Implement Tests Total complexity: 10 pts (3 + 2 + 2 + 3)
Task Results
| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|---|---|---|---|---|---|
| AZ-463_test_flight_selection_persistence | Done | 1 created (tests/flight_selection_persistence.test.tsx); 1 e2e created (e2e/tests/flight_selection_persistence.e2e.ts) |
5 fast (2 pass — AC-1 + AC-2 + leak-companion stub; 2 supporting controls); 4 e2e (2 PASS + 2 long-running gated) | 4 / 4 ACs covered | Long-running soaks (AC-3 / AC-4) gated by RUN_LONG_RUNNING=1; runner-level config gating to be added later |
| AZ-469_test_browser_support_responsive | Done | 1 created (tests/browser_support_responsive.test.tsx); 1 e2e created (e2e/tests/browser_support_responsive.e2e.ts) |
4 fast (3 PASS responsive class markers + 1 cross-browser config stub); 5 e2e (3 cross-browser smoke routes + 2 viewport variants) | 3 / 3 ACs covered | None |
| AZ-476_test_upload_size_cap | Done | 1 created (tests/upload_size_cap.test.tsx); 1 e2e created (e2e/tests/upload_size_cap.e2e.ts) |
3 fast (1 it.fails() for AC-1 drift + 1 control + 1 PASS for AC-2 vacuous-today); 2 e2e (1 test.fail for AC-1 + 1 PASS for AC-2 dialog spy) |
2 / 2 ACs covered | 1 documented drift: MediaList.uploadFiles catches the 413 silently and falls through to local-mode; no error region, no i18n key |
| AZ-477_test_settings_resilience | Done | 1 created (tests/settings_resilience.test.tsx); 1 e2e created (e2e/tests/settings_resilience.e2e.ts) |
6 fast (4 it.fails() for AC-1 + AC-2 contracts, 1 it.fails() for AC-3 deadline, 1 control pinning stuck-disabled drift); 2 e2e (test.fail for AC-1 / AC-2) |
3 / 3 ACs covered | 1 systemic drift: saveSystem / saveDirs lack try/finally and an error region — saving flag stays true forever; flips when both wired |
AC Test Coverage: All covered (12 / 12 ACs across the four tasks)
AZ-463 — Flight selection persistence + memory soaks (4 ACs, 9 scenarios)
| Scenario | Where | Profile | Status |
|---|---|---|---|
| AC-1 / FT-P-16 (persistence wire) | tests/flight_selection_persistence.test.tsx + e2e/tests/flight_selection_persistence.e2e.ts |
fast + e2e | PASS — selecting a flight via Header dropdown PUTs {selectedFlightId} to /api/annotations/settings/user |
| AC-2 / FT-P-17 (rehydration on boot) | same | fast + e2e | PASS — <App> boot with selectedFlightId set issues GET /api/flights/<id> and renders the flight as initially selected |
| AC-3 / NFT-RES-LIM-07 (100-cycle leak guard, long-running) | e2e/tests/flight_selection_persistence.e2e.ts |
e2e long-running (RUN_LONG_RUNNING=1) |
gated — wraps EventSource in an init script and asserts __activeES <= 1 end-of-cycle |
| AC-3 / fast companion stub (5-cycle smoke) | tests/flight_selection_persistence.test.tsx |
fast | PASS — 5 cycles produce exactly 5 PUTs (no fan-out) |
| AC-4 / NFT-RES-LIM-06 (1 h SSE soak) | e2e/tests/flight_selection_persistence.e2e.ts |
e2e long-running, chromium-only | gated — performance.memory.usedJSHeapSize at t=60 s vs t=3600 s, ≤ 10 % growth |
AC summary:
- AC-1 + AC-2 → PASS at the wire (production today persists and rehydrates correctly).
- AC-3 + AC-4 → long-running soak suite gated by env flag; CI lane wires the flag on dev/stage merges per the spec.
AZ-469 — Browser support + responsive variants (3 ACs, 9 scenarios)
| Scenario | Where | Profile | Status |
|---|---|---|---|
AC-1 / FT-P-34 cross-browser smoke (/flights, /annotations, /dataset) |
e2e/tests/browser_support_responsive.e2e.ts |
e2e × 2 projects | PASS — 3 routes × 2 browser projects = 6 smoke runs; existing playwright.config.ts provides the Chromium + Firefox projects |
| AC-1 / fast companion (project-count assertion) | tests/browser_support_responsive.test.tsx |
fast | PASS — Playwright config pinned at exactly 2 named projects |
| AC-2 / FT-P-35 mobile 480 px (Tailwind class shape) | tests/browser_support_responsive.test.tsx |
fast | PASS — desktop nav has hidden sm:flex, mobile bottom-nav has sm:hidden |
| AC-2 / FT-P-35 mobile 480 px (visibility) | e2e/tests/browser_support_responsive.e2e.ts |
e2e | PASS — bottom-nav visible, top-bar hidden after setViewportSize |
| AC-3 / FT-P-36 desktop 1024 px (Tailwind class shape) | tests/browser_support_responsive.test.tsx |
fast | PASS — same class markers asserted in opposite roles |
| AC-3 / FT-P-36 desktop 1024 px (visibility) | e2e/tests/browser_support_responsive.e2e.ts |
e2e | PASS — top-bar visible, bottom-nav hidden |
AC summary: All 3 ACs PASS in both fast and e2e profiles.
AZ-476 — Upload >500 MB → 413 (2 ACs, 5 scenarios)
| Scenario | Where | Profile | Status |
|---|---|---|---|
| AC-1 / FT-N-06 + NFT-RES-07 (in-DOM error region with i18n message) | tests/upload_size_cap.test.tsx |
fast | it.fails() — drift, production catches the 413 silently |
| AC-1 / control: production silently falls through to local mode on 413 | same | fast | PASS — file appears in the rendered media list (proves silent-fall-through drift) |
| AC-1 / e2e companion (501 MB POST → nginx 413 → DOM error region) | e2e/tests/upload_size_cap.e2e.ts |
e2e | test.fail — same drift; flips when production wires the toast |
AC-2 / no alert() on the 413 path (fast) |
tests/upload_size_cap.test.tsx |
fast | PASS (vacuous today — no error path runs at all; defence-in-depth) |
AC-2 / no alert() on the 413 path (e2e dialog spy) |
e2e/tests/upload_size_cap.e2e.ts |
e2e | PASS — Playwright dialog spy asserts no alert: events fire |
AC summary:
- AC-1 user-visible 413 →
it.fails()+ control + e2etest.fail. Flips when production wires an in-DOM alert + i18n key for the 413 path. - AC-2 no alert → PASS today (vacuous) + e2e dialog spy. Stays PASS once AC-1 lands as long as the new error region uses a toast / inline component, not
alert().
AZ-477 — Settings save resilience + 2 s budget (3 ACs, 6 scenarios)
| Scenario | Where | Profile | Status |
|---|---|---|---|
| AC-1 / FT-N-13 + NFT-RES-05 — Save button re-enables ≤ 2 s on 500 | tests/settings_resilience.test.tsx |
fast | it.fails() — drift, no try/finally |
| AC-1 / FT-N-13 + NFT-RES-05 — DOM error region appears ≤ 2 s on 500 | same | fast | it.fails() — drift, no error region rendered |
| AC-1 / control: today the Save button stays disabled after a 500 | same | fast | PASS — pins the stuck-disabled drift |
| AC-2 / FT-N-14 + NFT-RES-06 — Save button re-enables ≤ 2 s on network drop | same | fast | it.fails() — same root cause as AC-1 |
| AC-2 / FT-N-14 + NFT-RES-06 — DOM error region appears ≤ 2 s on network drop | same | fast | it.fails() |
| AC-3 / NFT-PERF-09 — DOM error visible within 2 s of response | same | fast | it.fails() — measures performance.now() between MSW response timestamp and findByRole('alert') |
| AC-1 + AC-2 e2e companions | e2e/tests/settings_resilience.e2e.ts |
e2e | 2 × test.fail — same drift |
AC summary:
- All three ACs are
it.fails()today; one control test pins the stuck-disabled drift so a regression that removes the silent-fail (e.g. starts throwing in the React render path) is caught immediately. - All three flip green simultaneously the moment
saveSystem/saveDirsget atry { ... } finally { setSaving(false) }plus an error region in the JSX.
Code Review Verdict: PASS
See _docs/03_implementation/reviews/batch_06_review.md for the full 7-phase walkthrough.
- 0 Critical, 0 High, 0 Medium, 0 Low findings.
- All
it.fails()placements paired with a control PASS test that pins the current production drift. - Architecture compliance (Phase 7): no layer-direction violations; tests are leaves of the import graph; no new cyclic dependencies; static profile (STC-S6, STC-S13) re-confirms.
Auto-Fix Attempts: 0
PASS verdict — no auto-fix loop entered.
Stuck Agents: None
One investigation took longer than usual: the AZ-476 fast test initially failed because the test rig used vi.stubGlobal('URL', { ...URL, createObjectURL, ... }) to install JSDOM polyfills, which destroyed the URL constructor (turning the global into a plain object) and silently broke new URL(...) inside MSW handlers. This was diagnosed by adding a fetch wrapper that logged every outbound request — once it was clear the request never reached MSW, the URL stub became the obvious suspect. The fix patches URL.createObjectURL and URL.revokeObjectURL directly on the constructor and restores them in afterEach. The lesson is captured in _docs/LESSONS.md so the next session sees it on autodev's B2 Recent Lessons surface.
Test Run Summary
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.ReadLints— clean on all 9 changed files.
Documented Drifts (cumulative across batch)
| Drift | Where | Spec/AC affected | Resolves when |
|---|---|---|---|
| 413 silently swallowed; falls through to local-mode | src/features/annotations/MediaList.tsx uploadFiles try/catch |
AZ-476 AC-1 | Wire toast + i18n key for the 413 path |
saveSystem / saveDirs have no try/finally |
src/features/settings/SettingsPage.tsx |
AZ-477 AC-1 + AC-2 | Wrap await api.put(...) in try { ... } finally { setSaving(false) } |
<SettingsPage> renders no error region for save failures |
same | AZ-477 AC-1 + AC-2 + AC-3 | Add a toast or inline alert with role="alert" |
Next Batch
10 → 6 tasks remain in todo/ after batch 6 archival:
- AZ-471, AZ-473, AZ-474, AZ-478, AZ-479, AZ-480.
Cumulative review (batches 04–06) is due immediately after this batch per implement/SKILL.md Step 14.5 (K=3 cadence). Cumulative report file: _docs/03_implementation/cumulative_review_batches_04-06_report.md.