Azaion UI — Retrospective Solution
Output of /document Step 5. Synthesis of the implemented architecture
and per-component choices, derived from the verified technical docs:
_docs/02_document/architecture.md (Step 3a), system-flows.md (Step 3b),
data_model.md (Step 3c), deployment/*.md (Step 3d),
components/*/description.md (Step 2), 04_verification_log.md (Step 4),
glossary.md and architecture.md § Architecture Vision (Step 4.5).
This is retrospective — it describes the solution as it is, with
observed limitations called out per component. Future work (testability
fixes, async-detect wiring, mission-planner convergence) is referenced
by source and not re-stated as a plan here.
Status: synthesised-from-verified-docs (Step 5 — /document)
Date: 2026-05-10
Project: Azaion UI (operator-facing browser SPA)
Product Solution Description
Azaion UI is a single-page React 19 application, statically built and served
by nginx inside an ARM64 container, that operates the browser-facing half of
the Azaion UAV operations suite. It lets an operator plan flights, browse and
annotate captured media, run AI object detection (synchronous on images;
asynchronous video detect is target-only — not wired today, see
04_verification_log.md F7), curate datasets, manage detection classes /
users / aircraft, and operate the GPS-Denied positioning workflow including a
planned Test Mode driven by .tlog + video pairs through SITL.
The solution communicates with the parent suite's microservices over REST
and Server-Sent Events only — no WebSocket, no GraphQL, no in-browser
persistence beyond a single bearer token in memory and a Secure HttpOnly
refresh cookie. State management is two React Contexts (AuthContext and
FlightContext); everything else is page-local.
A second React 18 + MUI 5 tree (mission-planner/) lives at the repo root as
the port-source for 05_flights — it is NOT deployed, NOT
compiled by the production Vite build, and is on a multi-cycle path to
deletion as features migrate into src/features/flights/ (Phase B feature
cycles per the convergence plan in architecture.md § Architecture Vision).
Component interaction diagram
Detailed per-flow sequences (F1–F14): _docs/02_document/system-flows.md.
Architecture
The solution is organised as 11 components under a strict layering
(_docs/02_document/module-layout.md):
- L0 (Foundation):
00_foundation, 11_class-colors
- L1 (Transport):
01_api-transport
- L2 (Auth + Shared UI):
02_auth, 03_shared-ui
- L3 (Feature pages):
04_login, 05_flights, 06_annotations,
07_dataset, 08_admin, 09_settings
- L4 (App shell):
10_app-shell
Component dependency graph: _docs/02_document/diagrams/components.md.
Cross-cutting principles (binding constraints — architecture.md § Architecture Vision)
P1 REST + SSE only · P2 Static bundle + nginx · P3 Bearer in memory + refresh
in HttpOnly cookie · P4 Two-context state (Auth + Flight) · P5 ARM-first edge
deployment · P6 Bilingual (en + ua) · P7 Lift cross-cutting at 2+ touches ·
P8 WPF parity is a goal not a constraint · P9 Spec is source of truth for
numeric enums · P10 No hardcoded credentials in source · P11 Persist what you
type · P12 Admin can edit existing detection classes.
Component: 00_foundation
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
| Shared types + hooks + i18n bundles, no domain logic |
typescript@5.7 strict, i18next + react-i18next, custom hooks (useDebounce, useResizablePanel) |
Single source of truth for the suite's typed REST contract; zero runtime cost (types erased); all bilingual strings live here |
useResizablePanel reads UserSettings.panelWidths but never writes back — violates principle P11 (04_verification_log.md finding #11). i18next lng:'en' is hardcoded — no detector / no persistence. Inline numeric-enum comments are required by P9 (already added 2026-05-10) |
TypeScript strict mode; bilingual coverage; numeric-enum drift between src/types/index.ts and the suite spec must be resolved |
None — shared types only |
Negligible (transitive only) |
Selected — current solution. Layer-0 placement keeps every other component dependency-free w.r.t. types/hooks. |
Component: 01_api-transport
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
Native fetch wrapper + native EventSource wrapper |
src/api/client.ts (fetch + 401-retry refresh), src/api/sse.ts (createSSE helper) |
Zero added dependencies (no axios / TanStack / SWR); 401-retry is centralised — every authenticated request gets refresh-token rotation for free |
Bearer for SSE goes in the query string (?token=...); EventSource cannot send headers (ADR-008). EventSource holds the bearer captured at create time — refresh-rotation breaks long-running subscriptions; reconnect logic is missing today (Step 8 hardening) |
All authenticated fetch requests must include credentials:'include' for the HttpOnly refresh cookie to flow; SSE endpoints must accept the bearer in the URL |
401-retry path is secure (POST + cookie); bootstrap GET refresh in AuthContext.tsx:24 is broken (no credentials:'include') — Step 4 fix |
Negligible |
Selected — current solution. Fits P1 (REST + SSE only) and P3 (no localStorage). |
Component: 02_auth
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
AuthContext + ProtectedRoute + login/logout/bootstrap-refresh |
react-router-dom@7, React Context, 01_api-transport |
XSS-resistant (bearer in memory, never in storage); login UX matches WPF era; refresh-token rotation is server-driven |
Two refresh paths in code (F2): bootstrap GET (AuthContext.tsx:24 — broken, missing credentials:'include') vs. 401-retry POST (api/client.ts:44 — correct). Bootstrap path will fail on cross-origin and force a re-login on cold load — Step 4 fix priority. ProtectedRoute spinner has no role='status' / no timeout |
RBAC is server-enforced; UI must NOT trust AuthUser.role for security — only for showing/hiding nav |
Bearer never written to storage (P3); refresh cookie is Secure HttpOnly SameSite=Strict (issued server-side); WPF-era encrypted-creds command-line handoff intentionally NOT ported (P8) |
Negligible |
Selected — current solution. Refresh-path consolidation (single POST with credentials:'include') is the planned fix; structurally sound. |
Component: 03_shared-ui
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
Header + flight dropdown + FlightContext + ConfirmDialog + HelpModal + DetectionClasses strip |
React Context (FlightContext), Tailwind, 01_api-transport |
One place for cross-page chrome; FlightContext is the only flight-selection store (P4); ConfirmDialog reused by 4 components |
FlightContext ceiling: GET /api/flights?pageSize=1000 is a hardcoded magic number (finding B3). selectFlight is fire-and-forget PUT — no error path. Header dropdown lacks role=combobox / aria-expanded / Esc-to-close / focus-trap. ConfirmDialog lacks aria-modal / role=dialog. HelpModal does NOT close on Esc (inconsistent with ConfirmDialog); GUIDELINES are hardcoded English instead of i18n |
Selected flight persists as a UserSettings field via PUT /api/annotations/settings/user (NOT /api/flights/select — 04_verification_log.md F3) |
None additional |
Negligible |
Selected — current solution. Flight-dropdown a11y + 1000-row pagination ceiling are Step 4 / Step 8 candidates. |
Component: 04_login
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
Public /login route with username + password, calls 02_auth login |
React, Tailwind |
Single dedicated public surface; clean separation from ProtectedRoute |
runUnlockSequence 4×600 ms theatrical animation is decorative; document only (finding B4) |
Receives bearer from server; cookie set server-side |
Login form does NOT autocomplete sensitive values; only public route in the SPA |
Negligible |
Selected — current solution. No structural concerns. |
Component: 05_flights
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
Flight CRUD + waypoints + altitude profile + GPS-Denied (Operations + planned Test Mode) — currently being ported from mission-planner/ (React 18 + MUI 5) into src/features/flights/ (React 19 + Tailwind 4) |
leaflet@1.9.4 + react-leaflet@5 + leaflet-draw + leaflet-polylinedecorator; chart.js@4 + react-chartjs-2; @hello-pangea/dnd@18; native fetch; EventSource for F13 live-GPS SSE; OpenWeatherMap (direct HTTPS) |
Replaces WPF MapMatcher with browser-native cartography; live-GPS telemetry is real (F13); altitude charts work; mission-planner port gives a high-fidelity reference UX |
Component spans two physical trees (one component, two trees — ADR-009). mission-planner/src/utils/flightPlanUtils.ts:60 carries a hardcoded OpenWeatherMap API key (P10 violation — Step 4 fix). Wind errors are silently swallowed; sequential per-segment await is a perf trap; battery-capacity unit ambiguous (Wh vs Ws); km vs m altitude mixing. mapIcons.ts defaultIcon CDN URL pinned to leaflet@1.7.1 (drift). Waypoint POST shape mismatches the suite spec — UI sends {name, latitude, longitude, order}; spec wants {Geopoint:{Lat,Lon,MGRS}, Source, Objective, OrderNum, Height} (finding #20 — likely 400s on a strict server). Edit-cycle is delete-then-recreate today (finding #19). FlightsPage save is N+M round-trips (delete + recreate per waypoint). MiniMap licence/responsive concerns; AltitudeDialog / JsonEditorDialog modal a11y; WaypointList drag/touch a11y; AltitudeChart bundle bloat. Test Mode (F12) is target-only — .tlog + video upload, IMU sync, SITL feed — none wired today |
Wind data fetch + map tile fetch require browser internet; field deployments need an offline tile cache (not implemented); .tlog parser must be available client-side or server-side once Test Mode lands |
OpenWeatherMap key must move to .env per P10 (Step 4 testability fix); satellite tile URL is env-driven via VITE_SATELLITE_TILE_URL in mission-planner only (target: src/ once port lands) |
Direct browser → external HTTP costs for OWM + tiles; otherwise compute is client-side |
Selected — current solution; under active convergence. Per architecture.md § Architecture Vision, the mission-planner tree is on a multi-cycle path to deletion (mission-planner/ → src/features/flights/); convergence happens in Phase B feature cycles, not in Step 8. |
Component: 06_annotations
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
Bounding-box editor (CanvasEditor), VideoPlayer, AI Detect (sync only today), AnnotationsSidebar, MediaList browser scoped to selected flight, AnnotationsPage orchestrator |
HTML5 <canvas> + HTML5 <video> (replaces WPF LibVLC — ADR-003); native fetch + SSE; react-dropzone for upload; Tailwind |
Frame-accurate-ish video review without LibVLC; doubly-prefixed POST /api/annotations/annotations save path verified at Step 4 (F5); F14 annotation-status SSE (admin-wide, client-side filtered) is real |
Async video detect (F7) is NOT wired — no /api/detect/video/{id}, no /api/detect/stream/{jobId} calls anywhere; sync POST /api/detect/${id} is used for both images and videos today (silent UX hazard for long videos). VideoPlayer hardcoded fps=30 (ADR-003 consequence). CanvasEditor missing pan; wrong time-window (symmetric ±200 ms instead of asymmetric [-50, +150] ms — finding #6); missing affiliation icons; missing CombatReadiness indicator; dead AFFILIATION_COLORS. AnnotationsSidebar AI-detect doesn't stream progress; silent catches. AnnotationsPage no panel-width persistence (P11 violation); handleDownload tainted-canvas risk; handleSave fallback hides save loss; annotation save body shape mismatches spec — must add Source, WaypointId, rename time→videoTime (finding #32). MediaList uses alert(); blob: locals ignore filter. Missing keyboard shortcuts (R, V, PageUp/Down). Missing Camera config side panel (GSD computation — finding #17). Missing Tile zoom for splitTile. Hardcoded English strings in help / sidebar |
Detect must be wired to detect/ async pipeline once F7 ships (Phase B); X-Refresh-Token header required for long-running video detect (per _docs/10_auth.md); annotation overlay window must align to spec asymmetric [-50, +150] ms |
XSS-safe canvas rendering; uploads filtered by react-dropzone MIME; server is authoritative for virus scan; tainted-canvas risk on download (finding) |
Detect-pipeline cost is server-side; UI compute is client-side |
Selected — current solution. Async-detect wiring + canvas-editor parity + a11y are Phase B targets; structurally sound. |
Component: 07_dataset
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
| Dataset Explorer with three tabs (annotations / editor / class-distribution), bulk-validate, status filters |
React, Tailwind, chart.js@4 for class-distribution chart (DatasetPage.tsx:151), reuses CanvasEditor from 06_annotations (cross-feature edge — finding) |
Validate button is wired (Step 4 correction — 04_verification_log.md F9); class-distribution chart is implemented (Step 4 correction); "objectsOnly" checkbox is implemented; objectsOnly filter works |
[V] keyboard shortcut missing (only the button works); Refresh thumbnails button missing; status-bar StatusText slots missing; IsSeed highlight missing (legacy 8 px IndianRed border — open question); editor tab does not save (finding #4); magic mediaType=1 literal (finding #5); dead ConfirmDialog import; silent catches; status filter conflates None with All; classNum=0 sentinel collides with real class 0 (finding #9); no virtualisation; no keyboard shortcuts at all |
AnnotationStatus numeric drift (UI 0/1/2 vs spec 0/10/20/30) surfaces as wrong status filter values on the wire — Step 4 fix per P9; DatasetItem.isSplit not in spec response — parent-suite doc fix recorded in leftovers and applied |
None additional |
Class-distribution endpoint server-side cost; otherwise client-side |
Selected — current solution. Bulk-validate + chart parity confirm the React port is closer to WPF parity than the original draft suggested; remaining gaps are surface-level + a11y. |
Component: 08_admin
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
| Class CRUD (add + delete; edit to be re-introduced per P12), user management, AI/GPS settings forms, aircraft default-toggle |
React forms with useState; native fetch; reuses ConfirmDialog (only on user-deactivation) |
Single page consolidates 4 admin surfaces; aircraft cross-service mutation works (PATCH /api/flights/aircrafts/${id}) |
AI Settings & GPS Settings forms render with defaultValue only — no state, no submit handler, the Save button does nothing (PRIORITY — Step 6 problem-extraction surface). Hardcoded GPS device default '192.168.1.100' / port '5535' shipped in production bundle. handleDeleteClass has no ConfirmDialog despite being destructive (finding B4). Detection-class read uses /api/annotations/classes but write uses /api/admin/classes (cross-service split — accepted but documented). handleToggleDefault duplicated in 09_settings (aircraft default lives in two pages — surface intent at Step 6). Many hardcoded English strings (P6 violation — Step 4 fix). Admin cannot edit existing classes today (P12 violation; PATCH /api/admin/classes/{id} to introduce — Phase B task) |
RBAC is server-enforced (UI MUST NOT trust AuthUser.role for security); /admin route lacks role-gate — security PRIORITY |
RBAC server-enforced; class-delete bypasses ConfirmDialog (Step 4 fix) |
Compute is server-side |
Selected — current solution; multiple Step 4 / Step 6 / Phase B fixes queued. Structurally sound; the broken Save buttons are a P0 product-correctness defect. |
Component: 09_settings
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
| System / Directory / Camera / User settings forms; aircraft default-toggle |
React forms, useState, native fetch |
Settings persist via annotations/ service (/api/annotations/settings/system + /api/annotations/settings/directories) — verified Step 4 (F11); aircraft default-toggle goes to flights/ (cross-service — accepted) |
saveSystem / saveDirs lack try/finally — PUT failure leaves saving:true permanently (finding B4). Numeric inputs use `parseInt(v) |
|
0— clearing a field silently writes 0 (finding B4). No optimistic concurrency (Step 6 surface).UserSettings.panelWidthsis typed butuseResizablePaneldoesn't write back (P11 violation — Step 4 fix)./settingsroute role-gate is more nuanced than/admin` (server-enforced via 403; no SETTINGS permission code in spec) |
/settings role-gate per _docs/02_document/architecture.md § Security; aircraft default is a global config today (finding) |
RBAC server-enforced |
Component: 10_app-shell
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
App.tsx + main.tsx + routing tree + global CSS |
react-router-dom@7, Tailwind 4 + az-* design tokens (src/index.css), React 19 root |
Single composition root; clear AuthProvider → ProtectedRoute → FlightProvider → Header + Routes chain; /flights is the default authenticated route |
/admin and /settings lack role-based route guards (PRIORITY — security finding; complements server-side RBAC). No ErrorBoundary. No lazy code-splitting / no chunked routes (bundle bloat finding). index.html body class hardcodes hex literals (bg-[#1e1e1e] text-[#adb5bd]) instead of az-* tokens (cosmetic — ADR-005) |
Routing tree is the security surface for client-side navigation; server-side RBAC is authoritative |
UI role-gate is convenience, not security; server-enforced 403 is the actual gate |
Negligible |
Selected — current solution. Adding ErrorBoundary + lazy routes + role-gate are Step 4 / Step 8 candidates. |
Component: 11_class-colors
| Solution |
Tools |
Advantages |
Limitations |
Requirements |
Security |
Cost |
Fit |
Class → color + text mapping; getPhotoModeSuffix helper; consumed by 03_shared-ui/DetectionClasses, 06_annotations, 07_dataset |
Pure TypeScript module; no external deps |
Lifted out of 06_annotations at Step 2 (P7); single source of truth for class color tokens; yoloId = classId + photoModeOffset mapping is centralised |
Physical file still lives at src/features/annotations/classColors.ts — the layout-doc-only mapping pending a Step 4 file move (module-layout.md Verification Needed #1, #8). getPhotoModeSuffix may duplicate the typed DetectionClass.photoMode field — likely redundant; possible deletion after typed propagation |
None |
None |
Negligible |
Selected — current solution. Cleanest L0 component; the file-move is purely cosmetic / layering hygiene. |
Testing Strategy
Current state — verified at Step 4
- Zero test coverage in
src/ — src/**/*.test.* returns zero matches (Grep verified at Step 4, 04_verification_log.md §1).
- No test framework configured in
package.json (Vitest / Jest / Playwright are all absent).
- No test step in CI —
.woodpecker/build-arm.yml runs bun install + bun run build only.
- The legacy WPF stack had an
Azaion.Test xUnit project that tested utilities only; that test surface did NOT migrate.
Target test pyramid (to be defined at autodev Step 5 — Decompose Tests)
The test-runner choice is deferred to autodev Step 3 (Test Spec) → Step 5 (Decompose Tests) per the Step 4.5 decision (architecture.md § Architecture Vision, item 7 of Open Questions). This document does not pre-empt that decision; it lists the categories the existing code already implies.
Unit / module-level
- Foundation hooks:
useDebounce, useResizablePanel — pure timing + state behavior; testable with React Testing Library + fake timers.
- Foundation utilities:
flightPlanUtils.ts (battery / distance / wind compute) — pure functions; testable in isolation once the OpenWeatherMap key is moved to .env (P10 / Step 4).
- Class-colors module:
getPhotoModeSuffix, yoloId mapping — pure functions.
- i18n bundles: assert that every English key has a Ukrainian counterpart and vice versa (P6 mandatory check).
- Numeric-enum coverage: assert that
AnnotationStatus, MediaStatus, Affiliation, CombatReadiness numeric values match the suite spec (P9 enforcement — directly catches the enum drift that motivated Step 4 corrections).
Component / integration
AuthContext — login, bootstrap-refresh (currently broken — fix first), 401-retry refresh, logout. Mock 01_api-transport to assert request shape (credentials:'include' is the regression to lock down).
FlightContext — list pagination ceiling, selectFlight round-trip, hydration of selected flight from UserSettings.
CanvasEditor — bbox draw / 8-handle resize / Ctrl-multi-select; affiliation overlay; CombatReadiness indicator (once dead AFFILIATION_COLORS is wired).
MediaList — pagination, filter, drag-drop upload, delete with ConfirmDialog.
DatasetPage — bulk-validate flow (POST + UI state); class-distribution chart load; status filter (specifically that None and All are NOT conflated — finding fix).
AdminPage — class add / delete (currently no edit; will gain edit per P12). User add / deactivate. AI Settings / GPS Settings forms (currently broken — the Save button must POST something — Step 6 product-correctness defect).
SettingsPage — system / directory / camera saves; aircraft default toggle (cross-service flights/ mutation).
End-to-end (browser)
- Login → /flights default route → flight selection → MediaList → annotation save → bulk-validate — the operator's primary loop. Already exercised manually at every release.
- GPS-Denied Test Mode (
F12) — once implemented (Phase B target). Inputs: .tlog + video; assertion: SITL feed reaches the onboard service and produces a positioning trace.
- Async video detect (
F7) — once implemented (Phase B target). Inputs: long video; assertion: SSE progress visible; final detections persisted.
Non-functional
- Bundle size budget:
vite build artifact ≤ ~2 MB gzipped (currently no enforcement — CI gate to add).
- Auth refresh transparency: 401 → POST refresh → retry, no UI re-render past
<ProtectedRoute> (regression test — locks the bootstrap-refresh fix).
- Route guards: unauth user →
/admin → redirected to /login (locks the missing role-gate fix).
- i18n coverage: every visible string in both
en.json and ua.json (P6 mandatory).
- a11y smoke:
ConfirmDialog has role=dialog + aria-modal; Header dropdown has role=combobox + aria-expanded + Esc-to-close; spinner has role=status.
- Endpoint contract: every
api.*() call URL matches the suite OpenAPI; specifically that the doubly-prefixed /api/annotations/annotations save path stays correct after any refactor.
- Performance: long video detect — UI stays responsive; progress visible (currently NOT met —
04_verification_log.md F7 / finding #21).
Test environment expectations
- Test DB / services: tests run against the suite docker-compose stack or a service-mock layer. The UI is HTTP-only — there is no UI-side database to bootstrap.
- CI integration: a test step must be added to
.woodpecker/build-arm.yml (currently missing — architecture.md § Deployment Model "Missing from the pipeline today").
- Coverage target: TBD at Step 3 (Test Spec) — this document does not commit to a percentage.
References
Source docs (verified inputs)
_docs/02_document/00_discovery.md — Step 0 codebase discovery, dep graph, topo order
_docs/02_document/architecture.md — Step 3a system architecture (with Step 4.5 § Architecture Vision)
_docs/02_document/system-flows.md — Step 3b 14 sequence flows F1–F14 (with Step 4 corrections)
_docs/02_document/data_model.md — Step 3c entity-relationship + enum-drift map
_docs/02_document/deployment/containerization.md — multi-stage Dockerfile, ARM64, nginx static serve
_docs/02_document/deployment/ci_cd_pipeline.md — Woodpecker .woodpecker/build-arm.yml
_docs/02_document/deployment/environment_strategy.md — dev / stage / production
_docs/02_document/deployment/observability.md — current state (no centralized client telemetry — Step 6 surface)
_docs/02_document/04_verification_log.md — Step 4 Verification Pass corrections
_docs/02_document/01_legacy_coverage_gaps.md — WPF parity rollup
_docs/02_document/glossary.md — Step 4.5 confirmed terminology
_docs/02_document/module-layout.md — Step 2.5 file-ownership map
_docs/02_document/components/00_foundation/description.md — through 11_class-colors/description.md
_docs/02_document/modules/*.md — 22 module docs covering all 77 modules
_docs/legacy/wpf-era.md — legacy reference
Configuration evidence
package.json — React 19, Vite 6, TypeScript 5.7 strict, Bun 1.3.11
vite.config.ts — dev /api → http://localhost:8080 proxy
Dockerfile — multi-stage oven/bun:1.3.11-alpine → nginx:alpine
nginx.conf — 9 /api/<service>/ reverse-proxy routes, client_max_body_size 500M
.woodpecker/build-arm.yml — ARM64-only build pipeline, no test step today
src/index.css — az-* Tailwind 4 design tokens
src/i18n/en.json, src/i18n/ua.json — bilingual bundles
src/types/index.ts — typed REST contract (with Step 4.5 inline numeric-enum comments per P9)
External integrations
- OpenWeatherMap:
api.openweathermap.org/data/2.5/onecall (hardcoded key in source — P10 violation, Step 4 fix)
- OpenStreetMap: tile servers via
react-leaflet TileLayer defaults
- Suite services (via nginx):
admin/, flights/, annotations/, detect/, loader/, gps-denied-{desktop,onboard}/, autopilot/, resource/
Related artifacts (downstream)
_docs/00_problem/ — Step 6 retrospective problem extraction (problem.md, restrictions.md, acceptance_criteria.md, input_data/, security_approach.md) — to be produced next
_docs/02_document/FINAL_report.md — Step 7 final integrated report — to be produced after Step 6