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>
9.3 KiB
Azaion UI — Data Model
Synthesis output of
/documentStep 3c. Consolidated fromsrc/types/index.ts, per-component data sections, and the parent suite spec at_docs/legacy/wpf-era.md§8 + the suite-level docs (cross-referenced).The UI does NOT own a database. The entities below are the contract shapes the UI consumes from suite REST + SSE endpoints. The authoritative schema is server-side; this document captures the client-side type expectations and the mismatches between those expectations and the suite spec (enum drift, shape drift) for resolution at autodev Step 4.
1. Entities by component
| Entity | Component | Backing service | Wire shape |
|---|---|---|---|
AuthUser |
02_auth |
admin/ |
{id, email, name, role, permissions[]} |
User |
08_admin |
admin/ |
{id, name, email, role, isActive} |
Aircraft |
08_admin / 09_settings |
admin/ |
{id, model, type:'Plane'|'Copter', isDefault} |
Flight |
05_flights |
flights/ |
{id, name, createdDate, aircraftId|null} |
Waypoint |
05_flights |
flights/ |
UI sends: {id, flightId, name, latitude, longitude, order}. Spec wants: {Geopoint:{Lat,Lon,MGRS}, Source, Objective, OrderNum, Height} — drift, finding #20 |
Media |
06_annotations |
annotations/ |
{id, name, path, mediaType, mediaStatus, duration, annotationCount, waypointId, userId} |
AnnotationListItem |
06_annotations |
annotations/ |
{id, mediaId, time, createdDate, userId, source, status, isSplit, splitTile, detections[]} |
Detection |
06_annotations |
annotations/ (storage) + detect/ (production) |
{id, classNum, label, confidence, affiliation, combatReadiness, centerX, centerY, width, height} |
DetectionClass |
08_admin (write) + 06_annotations (read) |
admin/ (write) + annotations/ (read) |
{id, name, shortName, color, maxSizeM, photoMode} |
DatasetItem |
07_dataset |
annotations/ |
{annotationId, imageName, thumbnailPath, status, createdDate, createdEmail, flightName, source, isSeed, isSplit} |
ClassDistributionItem |
(currently unused — backs missing chart) | annotations/ |
{classNum, label, color, count} |
SystemSettings |
09_settings |
admin/ |
{id, name, militaryUnit, defaultCameraWidth, defaultCameraFoV, thumbnailWidth, thumbnailHeight, thumbnailBorder, generateAnnotatedImage, silentDetection} |
DirectorySettings |
09_settings |
admin/ |
{id, videosDir, imagesDir, labelsDir, resultsDir, thumbnailsDir, gpsSatDir, gpsRouteDir} |
CameraSettings |
09_settings |
admin/ |
{id, altitude, focalLength, sensorWidth} |
UserSettings |
09_settings |
admin/ |
{id, userId, selectedFlightId, annotationsLeftPanelWidth, annotationsRightPanelWidth, datasetLeftPanelWidth, datasetRightPanelWidth} |
PaginatedResponse<T> |
shared (00_foundation) |
every list endpoint | {items[], totalCount, page, pageSize} |
2. Enums (numeric wire format)
The suite uses numeric wire values for every enum. The UI types in
src/types/index.tsmatch in shape but several have wrong values vs. spec. The state-of-the-world is captured instate.json::notes[](2026-05-10 02:13Z entries) — Step 4 will fix the UI side.
| Enum | UI values today | Spec values | Status |
|---|---|---|---|
MediaType |
None=0, Image=1, Video=2 |
matches | ✓ |
MediaStatus |
New=0, AiProcessing=1, AiProcessed=2, ManualCreated=3 |
also has None, Confirmed, Error |
drift — UI cannot render error state. Step 4 fix. |
AnnotationSource |
AI=0, Manual=1 |
matches numerically; spec doc shows strings (cross-repo doc fix replayed 2026-05-10) | ✓ (after parent-doc fix) |
AnnotationStatus |
Created=0, Edited=1, Validated=2 |
spec is None=0, Created=10, Edited=20, Validated=30, Deleted=40 |
drift, severe — wire payloads will be wrong. Step 4 PRIORITY. |
Affiliation |
Unknown=0, Friendly=1, Hostile=2 |
spec also has None |
drift — Step 4. |
CombatReadiness |
NotReady=0, Ready=1 |
spec also has Unknown |
drift — Step 4. |
3. Entity-relationship diagram
erDiagram
User ||--o{ Flight : "creates"
Flight ||--o{ Waypoint : "has"
Flight ||--o{ Media : "captures"
Media ||--o{ AnnotationListItem : "annotated by"
AnnotationListItem ||--o{ Detection : "contains"
DetectionClass ||--o{ Detection : "classifies (by classNum)"
Aircraft }o--o| Flight : "default for"
User ||--|| AuthUser : "session view of"
User ||--|| UserSettings : "preferences"
User ||--o{ DatasetItem : "validated by"
SystemSettings ||--|| Aircraft : "may default to"
Detection {
string id
int classNum "raw int including PhotoMode offset"
string label
float confidence
Affiliation affiliation
CombatReadiness combatReadiness
float centerX "normalized 0..1"
float centerY "normalized 0..1"
float width "normalized 0..1"
float height "normalized 0..1"
}
DetectionClass {
int id
string name
string shortName
string color "hex"
float maxSizeM "GSD constraint"
int photoMode "0=Regular, 1=Winter (+20), 2=Night (+40)"
}
Waypoint {
string id
string flightId
string name
float latitude "wire shape: drift — see finding 20"
float longitude
int order
}
AnnotationListItem {
string id
string mediaId
string time "video timestamp HH:MM:SS or null"
string createdDate "ISO 8601"
string userId
AnnotationSource source
AnnotationStatus status
bool isSplit
string splitTile "YOLO label string or null"
}
Media {
string id
string name
string path
MediaType mediaType
MediaStatus mediaStatus
string duration "HH:MM:SS or null"
int annotationCount
string waypointId
string userId
}
DatasetItem {
string annotationId
string imageName
string thumbnailPath
AnnotationStatus status
string createdDate
string createdEmail
string flightName
AnnotationSource source
bool isSeed
bool isSplit
}
4. Migration / schema-evolution strategy
The UI does NOT own a database, so there is no client-side migration to run. However:
- Enum drift above is effectively a "client-side schema migration" — every drifted enum must be aligned with the suite spec in Step 4 (or in the case of
AnnotationSource, the parent-suite doc is what's wrong and was already fixed via cross-repo doc patch on 2026-05-10). - Backwards compatibility is the suite's responsibility. The UI assumes the latest contract; if the suite needs to roll out a breaking change, it must coordinate with the UI version (typically by gating the change behind a feature flag in the admin service, then deploying both at once).
UserSettings.{annotationsLeftPanelWidth, ...}— the type exists; the wire endpoint exists; the UI does not write these today (useResizablePanelfinding #11). The fix is purely client-side wiring.
5. Seed data observations
The UI has no seed data of its own. Two sources of "default" data are observable:
FALLBACK_CLASS_NAMES(11_class-colors) — 12 generic English labels (Car, Person, Truck, …) shown only when admin classes failed to load. These are NOT a seed for the admin service; they are a defensive UI fallback only.Constants.DefaultAnnotationClassesin the legacy WPF — referenced in_docs/legacy/wpf-era.md §10. The React UI does NOT carry an equivalent (admin classes are always fetched). Acceptable.
6. Validation rules (client-side)
| Field | Rule | Source | Defect |
|---|---|---|---|
| Login email | non-empty | LoginPage |
none observed |
| Login password | non-empty | LoginPage |
none observed |
| Numeric settings | parseInt(v) || 0 |
SettingsPage |
clearing field silently writes 0 — finding B4 |
| Waypoint lat/lng | implicit (Leaflet bounds) | FlightsPage |
no explicit clamp — Step 4 |
| Detection bbox normalized 0..1 | implicit (CanvasEditor scaling) | CanvasEditor |
normalized-coordinate clamping mentioned in _docs/legacy/wpf-era.md §10 — verify in Step 4 |
| Class 1–9 keyboard pick | classes[idx + photoMode] |
DetectionClasses |
ordering vs admin contract unverified — Step 4 |
| File upload size | server cap 500 MB (nginx) | nginx.conf |
client does not pre-validate — Step 4 cosmetic |
7. Wire-format gotchas
timeas a string inAnnotationListItem(e.g.,"00:01:23.456") is parsed/formatted client-side; precision is millisecond. Used for video annotation overlay window math (50 ms before / 150 ms after — currently wrong, finding #6).splitTileas a YOLO label string is the raw<class> <cx> <cy> <w> <h>text, parsed client-side inCanvasEditor.tsx. Format mismatch is a Step 4 verification candidate.PaginatedResponse<T>ceiling —pageSize=1000is hardcoded for flights (finding B3). Other lists vary; no canonical default.- Date strings — assumed ISO 8601; no timezone handling beyond what the browser's
Dateconstructor implies. Step 4 verification when timezone bugs surface.