[AZ-447] autodev Steps 1-4 baseline: docs, tests, refactor specs

Captures the full output of autodev existing-code Phase A through
Step 4 (Code Testability Revision) for the Azaion UI workspace:

- Step 1 Document: _docs/02_document/ (FINAL_report, architecture,
  glossary, components/, modules/, diagrams/, system-flows,
  module-layout) plus _docs/00_problem/ + _docs/01_solution/ +
  _docs/legacy/ + _docs/how_to_test + README.
- Step 2 Architecture Baseline: architecture_compliance_baseline.md.
- Step 3 Test Spec: _docs/02_document/tests/ (environment,
  test-data, blackbox/performance/resilience/security/
  resource-limit tests, traceability-matrix), enum_spec_snapshot,
  expected_results/results_report.md (98 rows), plus the
  run-tests.sh + run-performance-tests.sh runners.
- Step 4 Code Testability Revision: 01-testability-refactoring/
  run dir (list-of-changes C01-C07, deferred_to_refactor,
  analysis/research_findings + refactoring_roadmap) and the 7
  child task specs AZ-448..AZ-454 under _docs/02_tasks/todo/
  plus _dependencies_table.md.
- _docs/_autodev_state.md pins the cursor at Step 4 / refactor
  Phase 4 entry so /autodev resumes cleanly.

Epic AZ-447 (UI testability gates) tracks the 7 child tasks that
will land in subsequent commits.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-11 00:38:49 +03:00
parent da0a5aa187
commit 510df68bcf
84 changed files with 13065 additions and 0 deletions
@@ -0,0 +1,166 @@
# Data Parameters — Azaion UI
> Output of `/document` Step 6d. The Azaion UI is a **thin client over a typed
> REST + SSE contract**; it carries no database. "Input data" therefore means
> the data shapes the SPA consumes (REST response payloads, SSE event
> payloads, env config). All claims trace to `_docs/02_document/data_model.md`,
> `architecture.md` § 45, and per-component descriptions.
**Status**: synthesised-from-verified-docs (Step 6d — `/document`)
**Date**: 2026-05-10
---
## Categories of input data
The SPA consumes four categories:
1. **Typed REST entities** — see `_docs/02_document/data_model.md` for the
full ER map; key shapes summarised below.
2. **SSE event payloads**`live-gps`, `annotation-status`, planned
`detect-stream`.
3. **Configuration / environment variables** — runtime config injected at
build time or via env.
4. **Static assets** — translation bundles, icons, design tokens (compiled
into the bundle).
---
## 1. Typed REST entities (defined in `src/types/index.ts`)
> Every entity below mirrors the suite's REST contract. Values listed here
> match the **suite spec**, which is the source of truth per principle P9.
> Where the UI's current TypeScript enum drifts from the spec, the row notes
> the drift and the Step 4 fix.
### Auth
| Entity | Fields | Source |
|--------|--------|--------|
| `AuthUser` | `id`, `email`, `name`, `role`, `permissions: string[]`, `aircraftId?` | `02_auth`; `admin/` service |
| `LoginRequest` | `{ email, password }` | `POST /api/admin/auth/login` body |
| `LoginResponse` | `{ bearer, user: AuthUser }` (refresh cookie set server-side) | `POST /api/admin/auth/login` 200 |
### Flights
| Entity | Fields | Source |
|--------|--------|--------|
| `Flight` | `id`, `name`, `aircraftId`, `startDate?`, `endDate?`, `description?` | `flights/` service |
| `Waypoint` | **Spec**: `{ Geopoint: { Lat, Lon, MGRS }, Source, Objective, OrderNum, Height }`. **UI today**: `{ name, latitude, longitude, order }` — drift, finding #20 / Step 4 fix | `05_flights`; `flights/` service |
| `Aircraft` | `id`, `name`, `model`, `isDefault`, `serialNumber?` | `flights/` (read+write); `08_admin` mutation |
| `LiveGpsEvent` (SSE) | `{ flightId, lat, lon, alt, heading, speed, ts }` | `createSSE('/api/flights/${id}/live-gps')`; F13 |
### Annotations + Media
| Entity | Fields | Source |
|--------|--------|--------|
| `Media` | `id`, `flightId`, `mediaType: MediaType`, `mediaStatus: MediaStatus`, `filename`, `waypointId?`, `videoTime?`, `thumbnail?` | `annotations/` service |
| `MediaType` enum | **Spec**: `None=0`, `Image=1`, `Video=2`. **UI**: same. | `00_foundation`; P9 |
| `MediaStatus` enum | **Spec**: must include `None`, `Confirmed`, `Error` plus the existing `New`, `AiProcessing`, `AiProcessed`, `ManualCreated`. **UI today**: only `New=0` / `AiProcessing=1` / `AiProcessed=2` / `ManualCreated=3` — drift, Step 4 fix | `00_foundation`; finding |
| `AnnotationListItem` | `id`, `mediaId`, `videoTime`, `status: AnnotationStatus`, `source: AnnotationSource`, `detections: Detection[]`, `isSeed?: boolean` | `annotations/` |
| `AnnotationStatus` enum | **Spec**: `None=0`, `Created=10`, `Edited=20`, `Validated=30`, `Deleted=40`. **UI today**: `Created=0`, `Edited=1`, `Validated=2` — drift, Step 4 fix per P9 | `00_foundation`; `04_verification_log.md` |
| `AnnotationSource` enum | `AI=0`, `Manual=1` (matches spec) | `00_foundation` |
| `Detection` | `{ classNum: number, x, y, w, h: number, affiliation: Affiliation, combatReadiness: CombatReadiness, confidence?: number }` (normalised pixel coords) | `06_annotations` |
| `Affiliation` enum | **Spec**: must include `None` plus `Unknown`, `Friendly`, `Hostile`. **UI today**: `Unknown=0`, `Friendly=1`, `Hostile=2` — drift, Step 4 fix | finding |
| `CombatReadiness` enum | **Spec**: must include `Unknown` plus `NotReady`, `Ready`. **UI today**: `NotReady=0`, `Ready=1` — drift, Step 4 fix | finding |
| `DetectionClass` | `{ id, name, color, photoMode, maxSizeM }` | `08_admin` (write) + `annotations/` (read) |
| Annotation save body | **Required** (per finding #32): `Source`, `WaypointId`, `videoTime`, plus `mediaId`, `detections`, `status`. **UI today**: missing `Source` and `WaypointId`; uses `time` instead of `videoTime` — Step 4 fix | `06_annotations/AnnotationsPage.tsx` |
| `AnnotationStatusEvent` (SSE) | `{ annotationId, mediaId, oldStatus, newStatus, ts }` | `createSSE('/api/annotations/annotations/events')`; F14 |
### Dataset
| Entity | Fields | Source |
|--------|--------|--------|
| `DatasetItem` | `id`, `mediaId`, `classNum`, `status: AnnotationStatus`, `thumbnail`, `isSeed?: boolean`, `isSplit?: boolean` (parent-suite-doc fix applied for `isSplit`) | `07_dataset`; `annotations/` |
| `ClassDistributionItem` | `{ classNum, label, color, count }` | `annotations/` |
| Bulk-validate body | `{ ids: number[], targetStatus: AnnotationStatus.Validated }` | `POST /api/annotations/dataset/bulk-status` |
### Settings + Admin
| Entity | Fields | Source |
|--------|--------|--------|
| `SystemSettings` | as defined per `09_settings/SettingsPage.tsx` (settings keys per the suite spec) | `annotations/` (`/api/annotations/settings/system`) |
| `DirectorySettings` | per `SettingsPage` directory tab | `annotations/` (`/api/annotations/settings/directories`) |
| `CameraSettings` | per `SettingsPage` camera tab | `annotations/` |
| `UserSettings` | `selectedFlightId?: number`, `panelWidths?: { ... }`, plus other per-user UI state | `annotations/` (`/api/annotations/settings/user`) |
| `User` | `id`, `email`, `role`, `isActive`, `createdAt?` | `admin/` |
### Pagination
| Entity | Shape | Source |
|--------|-------|--------|
| `PaginatedResponse<T>` | `{ items: T[], totalCount: number, page: number, pageSize: number }` | shared envelope used by every list endpoint |
---
## 2. SSE event payloads
| Stream | URL | Payload shape | Where consumed |
|--------|-----|---------------|----------------|
| Live-GPS per flight (F13) | `GET /api/flights/${flightId}/live-gps?token=${bearer}` | `LiveGpsEvent` (see above) | `src/features/flights/FlightsPage.tsx:67` |
| Annotation-status events (F14) | `GET /api/annotations/annotations/events?token=${bearer}` | `AnnotationStatusEvent` | `src/features/annotations/AnnotationsSidebar.tsx:25` |
| Async detect progress (F7) | `GET /api/detect/stream/${jobId}?token=${bearer}`**target-only, NOT wired today** | `{ jobId, progress: 0..1, detections?: Detection[], status, ts }` (anticipated) | not consumed today; planned per `04_verification_log.md` F7 |
Bearer goes in the **query string** (`?token=...`) per `ADR-008``EventSource`
cannot send headers. Refresh-rotation breaks live SSE; reconnect is missing
today (Step 8 hardening per `architecture.md` § Architecture Vision).
---
## 3. Configuration / environment variables
| Variable | Where read | Type | Default | Source |
|----------|-----------|------|---------|--------|
| `VITE_OPENWEATHERMAP_API_KEY` | (target — Step 4 fix) `mission-planner/src/utils/flightPlanUtils.ts` | string (secret) | currently hardcoded `'335799082893fad97fa36118b131f919'` (must rotate) | P10 violation, Step 4 fix |
| `VITE_SATELLITE_TILE_URL` | mission-planner Leaflet `TileLayer` | URL | none (unset breaks satellite imagery) | mission-planner only today |
| `AZAION_REVISION` | stamped into the production image at build time | string (commit SHA) | `$CI_COMMIT_SHA` from CI | `Dockerfile`; `.woodpecker/build-arm.yml` |
| `REGISTRY_HOST` | CI registry push | string | per pipeline secret | `.woodpecker/build-arm.yml` |
| `i18next.lng` | `src/i18n/i18n.ts` | language code | hardcoded `'en'` (Step 4 fix — should resolve from detector) | `i18n.ts`; AC-13 |
| nginx upstream hosts | `nginx.conf` | hostnames per service | docker-compose service names | `nginx.conf` |
The SPA bundle MUST NOT carry secrets at build time — except OpenWeatherMap
once it is moved to `.env` (per P10 the proper long-term answer is to proxy
the OWM call through `flights/` so no key reaches the browser; the `.env`
move is the interim Step 4 testability fix).
---
## 4. Static assets
| Asset | Location | Notes |
|-------|----------|-------|
| Translation bundles | `src/i18n/en.json`, `src/i18n/ua.json` | English + Ukrainian; key parity is mandatory (AC-12) |
| Design tokens | `src/index.css` (`az-bg`, `az-text`, `az-orange`, `az-success`, `az-danger`, `az-primary`, ...) | Tailwind 4; `ADR-005` |
| Map icons | `src/features/flights/mapIcons.ts` | defaultIcon CDN URL pinned to `leaflet@1.7.1` (drift — finding) |
| Aircraft / waypoint icons | bundled SVG / PNG under `src/features/flights/icons/*` (mission-planner port-source still has the larger set) | `05_flights` |
| Detection-class colors | `src/features/annotations/classColors.ts` (logically owned by `11_class-colors`) | file-move pending (P11 / module-layout Verification Needed) |
---
## 5. Data flow summary
1. **Plan flight** — UI fetches `aircrafts` from `flights/`; submits flight +
waypoints; receives the persisted flight (today: delete-then-recreate
waypoint cycle, finding #19; lossy POST shape, finding #20).
2. **Capture media** — out-of-band via the loader / annotations services;
the UI surfaces uploaded items via `MediaList` polling.
3. **Annotate** — operator edits → `POST /api/annotations/annotations`;
`F14` SSE pushes other-user status changes (admin-wide stream,
client-side filtered).
4. **AI Detect (sync image)**`POST /api/detect/${id}` returns inline
detections. **Used for both image and video today** (silent UX hazard
for long videos — `F7` to ship in Phase B).
5. **AI Detect (async video — target)**`POST /api/detect/video/${id}`
returns a job ID → SSE on `/api/detect/stream/${jobId}` streams progress.
Long videos require `X-Refresh-Token` header per `_docs/10_auth.md`.
6. **Curate dataset** — UI queries `annotations/` with status filters;
bulk-validate transitions to `AnnotationStatus.Validated`; class-distribution
chart loads from `/api/annotations/dataset/class-distribution`.
7. **Settings** — system / directory / camera saves go to `annotations/`;
aircraft default-toggle goes to `flights/` (cross-service mutation,
accepted).
8. **GPS-Denied Test Mode (target — F12)**`.tlog` + video upload to
`gps-denied-desktop/`; SITL drives `gps-denied-onboard/`; results render
back through `flights/` GPS-Denied tab.
Full sequence diagrams: `_docs/02_document/system-flows.md`.