[AZ-626] Decompose complete: 47 tasks + docs + module layout

Greenfield Steps 1-6 baseline for the autopilot rewrite from legacy
Qt/C++ to a Rust workspace.

- Remove legacy Qt/C++ tree (ai_controller, drone_controller,
  misc/camera, python_scaffold, root Dockerfile, autopilot.pro,
  legacy main.py / requirements.txt).
- Add _docs/00_problem (problem, restrictions, acceptance criteria,
  security approach, input data + fixtures).
- Add _docs/01_solution/solution_draft01.
- Add _docs/02_document (architecture, system-flows, data_model,
  glossary, decision-rationale, deployment, 13 component descriptions,
  tests/ specs, FINAL_report, module-layout).
- Add _docs/02_tasks/todo with 47 task specs (AZ-640..AZ-686, one
  bootstrap + 46 component tasks) and _dependencies_table.md.
- Add .cursor/rules/artifact-srp.mdc (single-responsibility rule for
  canonical _docs artifacts).
- Track autodev state in _docs/_autodev_state.md (Step 6 completed,
  ready for Step 7 Implement).

Jira: bootstrap AZ-626; component epics AZ-627..AZ-639; tasks
AZ-640..AZ-686. Total complexity 173 points across 12 epics.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-19 11:02:01 +03:00
parent f7d6cb4a3a
commit bc40ea7300
235 changed files with 12585 additions and 15097 deletions
@@ -0,0 +1,90 @@
# Fixture manifest
All fixtures live **inside this workspace** so the autopilot repo is self-sufficient — downstream test runners must never reach into a sibling repo at `../`. When you add or refresh a fixture, update the matching SHA-256 in this manifest AND the rows in `../expected_results/results_report.md` that consume it.
Total on-disk size: ~57 MB.
## Files
### Still-image aerial frames — `images/`
Used as Tier-1 input frames for detection-quality assertions.
| File | Size | SHA-256 | Upstream source | `results_report.md` rows |
|---|---|---|---|---|
| `images/4d6e1830d211ad50.jpg` | 152 KB | `4c396495af64aaf9aac5ecb92431bf0c75db42b0bdb8e4eec1937f9995acee42` | `../detections/data/images/` (re-copied 2026-05-19) | L1, D6 |
| `images/54f6459dbddb93d8.jpg` | 6.7 MB | `cd65c76a080ef72ce3528031f003f067fca6091c067a86d527a1ae91cd78be59` | `../detections/data/images/` (re-copied 2026-05-19) | D2 |
| `images/6dd601b7d2dc1b30.jpg` | 1.4 MB | `45edd83a357a9f852e14e5845265cd09c20b4b99b1828c160cb3298f0e160181` | `../detections/data/images/` (re-copied 2026-05-19) | D2 |
| `images/805bcf1e9f271a58.jpg` | 176 KB | `fe696899225fc04f2335e87acf6a3ad8a00cd3950c5940d5e73e5ce438f36257` | `../detections/data/images/` (re-copied 2026-05-19) | D2 |
| `images/f997d0934726b555.jpg` | 232 KB | `5d1c9c551c0680e5b3d0aab261bca71e724c78f6db3580da598c680b4f7d4d79` | `../detections/data/images/` (re-copied 2026-05-19) | D2 |
### Reconnaissance video — `videos/`
| File | Size | SHA-256 | Upstream source | `results_report.md` rows |
|---|---|---|---|---|
| `videos/94d42580bd1ad6ff.mp4` | 12 MB | `602b22a42515a754313551847caa6d6a6d7b3cde1d857cbd08ebc5543fb8cf7c` | `../detections/data/videos/` (re-copied 2026-05-19) | T3 (frame-rate floor scenario) |
### Movement-detection clips — `movement/`
Wide-area reconnaissance clips intended for movement-detection visual baselines. **Important**: these clips DO NOT have paired `gimbal.csv` / `telemetry.csv` files — ego-motion compensation assertions (M1M4) cannot run against them. They are useful for visual harness work, frame-count assertions, and as visual reference for the movement-detection scenarios.
| File | Size | SHA-256 | Upstream source | `results_report.md` rows |
|---|---|---|---|---|
| `movement/video01.mp4` | 5.3 MB | `6f37186f5e9be97109db8d0d220df96d21cac9ce5b50b576234c6f7ee369d2bb` | local; provenance pre-existing in workspace | M1 (visual reference only — no telemetry) |
| `movement/video02.mp4` | 5.9 MB | `7de7981e511e21e1e72f506d44541b44a4c27a995c9505ef8e3b48e69b416367` | local; provenance pre-existing in workspace | M2 (visual reference only — no telemetry) |
| `movement/video03.mp4` | 6.1 MB | `df441164da7f37d715968212b95e9bf53c8e37384f20ddfab61cd6d0d18b4f3a` | local; provenance pre-existing in workspace | M3 (visual reference only — no telemetry) |
| `movement/video04.mp4` | 5.8 MB | `36445bf1c86c5afa524000b5b2da7fc9cb3d39c745f9ad830b3d60f6868948e7` | local; provenance pre-existing in workspace | M4 (visual reference only — no telemetry) |
### Semantic reference frames — `semantic/`
Annotated reference examples for concealed-position semantic targets. **Not a graded eval set** — these are 4 hand-picked examples of footpath-to-concealment patterns, intended as visual reference for what the system should recognise. Detection-quality gates (D1, D3, D4, D5) need a full annotated multi-season eval set; these 4 PNGs are insufficient for those gates and serve as starter reference only.
| File | Size | SHA-256 | Description | `results_report.md` rows |
|---|---|---|---|---|
| `semantic/semantic01.png` | 3.1 MB | `339ad4d35ab36052828f05652ab7249801bcd5d7bb04522f0ab9cbf6f0ca008a` | Footpath leading to branch-pile hideout in winter forest | D3, D4, D5 (starter only — full multi-season set still required) |
| `semantic/semantic02.png` | 5.1 MB | `ffe3c49f5f1833724ce46083d212e714422e664b635cdd48b63311adefcd7b1f` | Footpath to FPV launch clearing, branch mass at forest edge | D3, D4, D5 (starter only) |
| `semantic/semantic03.png` | 1.0 MB | `ce89c139815e9a80679237008f7cfc3039bbd53f162d48017e840ff91e57b109` | Footpath to squared hideout structure | D3, D4, D5 (starter only) |
| `semantic/semantic04.png` | 1.3 MB | `b25c689b7aa543ec15858e4b5edfa32387ced4930130eb280d952c555f104e69` | Footpath terminating at tree-branch concealment | D3, D4, D5 (starter only) |
| `semantic/data_parameters.md` | 2 KB | n/a (text) | Description of the four reference examples + the new YOLO primitive classes that motivate them | reference only |
### Detection contract schemas — `schemas/`
| File | Size | SHA-256 | Upstream source | `results_report.md` rows |
|---|---|---|---|---|
| `schemas/expected_detections.json` | 1.4 KB | `ce60c105d697efe0359d2e6b1b46fc63e53d3789b067d53501f9c76aad9bd1ae` | `../e2e/fixtures/` (re-copied 2026-05-19) | D6 (sample Tier-1 response) |
| `schemas/expected_detections.schema.json` | 2.4 KB | `a7174e0b083dcbf42fa8672acd3e1807d11ea0629cc636ff958a4d77168733b9` | `../e2e/fixtures/` (re-copied 2026-05-19) | D6 (JSON-schema for the Tier-1 contract) |
### Database init script — `sql/`
| File | Size | SHA-256 | Upstream source | `results_report.md` rows |
|---|---|---|---|---|
| `sql/init.sql` | 3.7 KB | `b61e452c549f7b006db88d265f4346837e0a33d1abd4d977ebf3d48d8c943439` | `../e2e/fixtures/` (re-copied 2026-05-19) | suite-only reference; no autopilot AC row asserts against this |
## Copy vs reference
Fixtures were COPIED (not moved). The sibling repos still own the originals — keeping autopilot's copy in sync when an upstream changes is a manual chore today (the `monorepo-e2e` skill at the suite root will eventually own this drift; see `_docs/_process_leftovers/` if a sync is pending).
When an upstream fixture changes:
1. Recompute the SHA-256 in the source repo.
2. Re-copy into the matching `fixtures/` subdirectory here.
3. Update this manifest's SHA-256 column.
4. If the change invalidates an assertion in `../expected_results/results_report.md`, fix the row's expected result too — do not let assertions drift silently against new data.
## Gaps still pending fixture acquisition
The authoritative per-service acquisition catalogue lives in `../services.md`. Summary of the still-open gaps (each is also tagged on its row in `../expected_results/results_report.md` with a structured `<DEFERRED: ...>` marker, and a `_docs/_process_leftovers/` entry records the replay obligation):
| Gap | What's missing | Blocks AC rows | Acquisition status |
|---|---|---|---|
| Paired gimbal+telemetry CSVs for the 4 movement clips | `gimbal.csv` + `telemetry.csv` aligned to each video frame timestamps | M1M4, tightens L6/L7 | **Confirmed unavailable today** (user 2026-05-19) — requires re-flight or new recording with gimbal-feedback channel captured |
| Annotated eval set across all four seasons | Hundredsthousands of labelled images per season for concealed-position + footpath gates | D1, D3, D4, D5 | needs annotation campaign (1.5 months at 5 hrs/day target per `semantic/data_parameters.md`) |
| Per-zoom-band frame sequences | Same kind of clip as `movement/` but recorded at light, medium, and high zoom bands | tightens M2, L7, S2 | needs flight time + zoom-band metadata in the recorder |
| Mock `missions` HTTPS exchanges | Recorded JSON request/response pairs for mission GET/POST + mapobjects GET/POST | Mp1Mp5 | inline-authorable against the `mission-schema`; not yet authored |
| Mock Ground Station session traces | Scripted timing trace (connect / push / drop / reconnect / lost-link) | R4, O8 | inline-authorable; not yet authored |
| ArduPilot SITL traces | Recorded MAVLink streams for waypoint upload, geofence INCLUSION + EXCLUSION, RTL on lost-link, RTL on battery floor | R4, R5, R6, R7, R9 + project SITL conformance gate | needs SITL run |
| Operator-command envelopes | Valid / expired / replayed / malformed envelopes under the chosen Q9 auth scheme | O9, O10 | **blocked on Q9** (`_docs/02_document/architecture.md §8`) |
| VLM I/O pairs | Bounded ROI in → structured `VlmAssessment` out + schema-violation cases | L3, S5 | inline-authorable against the assessment schema once the local model is pinned |
| GPS / NTP drift scenarios | Scripted offset / lock-loss traces | R8 | inline-authorable |
When a fixture from this list lands, copy it under `fixtures/<category>/`, add a row to the relevant subsection above, and bind the matching `<DEFERRED>` row in `../expected_results/results_report.md` to its new local path.
Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

@@ -0,0 +1,32 @@
{
"$schema": "./expected_detections.schema.json",
"_meta": {
"fixture_version": "0.1.0-placeholder",
"video": "sample.mp4",
"video_sha256": "TBD-after-fixture-recording",
"model": {
"_comment": "Pinned model + classes that detections must run when this baseline applies. Refresh this block (and counts/bboxes below) whenever detections ships a new model.",
"name": "TBD",
"revision": "TBD",
"classes_source": "annotations/src/Database/DatabaseMigrator.cs (ids 0..18)"
},
"tolerance": {
"_comment": "Spec asserts ranges, not exact values. INT8 calibration drift can move pixel positions by a few units; absolute count can drift by ±1 across re-runs of the same engine on the same Jetson.",
"count_delta": 1,
"bbox_iou_min": 0.8,
"confidence_delta": 0.1
}
},
"expected": {
"total_annotations": 0,
"by_class": [
{
"class_id": 0,
"class_name": "ArmorVehicle",
"count": 0,
"bbox_samples": []
}
],
"_placeholder_note": "Replace this block with the real baseline once sample.mp4 is recorded. Each entry under `by_class` carries: class_id, class_name (must match detection_classes.name), count, and bbox_samples (an array of {time_sec, center_x, center_y, width, height, confidence} entries the spec uses for IoU comparison)."
}
}
@@ -0,0 +1,66 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Suite e2e expected detections baseline",
"type": "object",
"required": ["_meta", "expected"],
"properties": {
"$schema": { "type": "string" },
"_meta": {
"type": "object",
"required": ["fixture_version", "video", "video_sha256", "model", "tolerance"],
"properties": {
"fixture_version": { "type": "string" },
"video": { "type": "string" },
"video_sha256": { "type": "string" },
"model": {
"type": "object",
"required": ["name", "revision", "classes_source"],
"additionalProperties": true
},
"tolerance": {
"type": "object",
"required": ["count_delta", "bbox_iou_min", "confidence_delta"],
"properties": {
"count_delta": { "type": "integer", "minimum": 0 },
"bbox_iou_min": { "type": "number", "minimum": 0, "maximum": 1 },
"confidence_delta": { "type": "number", "minimum": 0, "maximum": 1 }
}
}
}
},
"expected": {
"type": "object",
"required": ["total_annotations", "by_class"],
"properties": {
"total_annotations": { "type": "integer", "minimum": 0 },
"by_class": {
"type": "array",
"items": {
"type": "object",
"required": ["class_id", "class_name", "count"],
"properties": {
"class_id": { "type": "integer", "minimum": 0 },
"class_name": { "type": "string" },
"count": { "type": "integer", "minimum": 0 },
"bbox_samples": {
"type": "array",
"items": {
"type": "object",
"required": ["time_sec", "center_x", "center_y", "width", "height"],
"properties": {
"time_sec": { "type": "number", "minimum": 0 },
"center_x": { "type": "number" },
"center_y": { "type": "number" },
"width": { "type": "number", "minimum": 0 },
"height": { "type": "number", "minimum": 0 },
"confidence": { "type": "number", "minimum": 0, "maximum": 1 }
}
}
}
}
}
}
}
}
}
}
@@ -0,0 +1,45 @@
# Semantic And Movement Detection Training Data
# Source
- Aerial imagery from reconnaissance winged UAVs at 6001000m altitude
- ViewPro A40 camera, 1080p resolution, various zoom levels
- Extracted from video frames and still images
- Movement detection requires frame sequences, not still images only; include camera/gimbal telemetry where available to separate target motion from UAV motion.
# Target Classes
- Footpaths / trails (linear features on snow, mud, forest floor)
- Fresh footpaths (distinct edges, undisturbed surroundings, recent track marks)
- Stale footpaths (partially covered by snow/vegetation, faded edges)
- Concealed structures: branch pile hideouts, dugout entrances, squared/circular openings
- Tree rows (potential concealment lines)
- Open clearings connected to paths (FPV launch points)
- Moving point/cluster candidates at wide or light/medium zoom
# YOLO Primitive Classes (new)
- Black entrances to hideouts (various sizes)
- Piles of tree branches
- Footpaths
- Roads
- Trees, tree blocks
# Annotation Format
- Managed by existing annotation tooling in separate repository
- Expected: bounding boxes and/or segmentation masks depending on model architecture
- Footpaths may require polyline or segmentation annotation rather than bounding boxes
# Seasonal Coverage Required
- Winter: snow-covered terrain (footpaths as dark lines on white)
- Spring: mud season (footpaths as compressed/disturbed soil)
- Summer: full vegetation (paths through grass/undergrowth)
- Autumn: mixed leaf cover, partial snow
# Volume
- Target: hundreds to thousands of annotated images/sequences
- Available effort: 1.5 months, 5 hours/day
- Potential for annotation process automation
# Reference Examples
- semantic01.png — footpath leading to branch-pile hideout in winter forest
- semantic02.png — footpath to FPV launch clearing, branch mass at forest edge
- semantic03.png — footpath to squared hideout structure
- semantic04.png — footpath terminating at tree-branch concealment
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

@@ -0,0 +1,104 @@
-- Suite e2e database seed.
--
-- Loaded by the `db-seed` service in docker-compose.suite-e2e.yml after
-- annotations has run its own DatabaseMigrator (which creates the schema +
-- inserts the canonical detection_classes 0..18). This file therefore only
-- adds rows that the e2e scenario depends on but the production runtime does
-- NOT seed automatically.
--
-- Idempotency: every statement uses ON CONFLICT / IF NOT EXISTS so re-running
-- the seed (e.g. on a `down -v` followed by `up`) lands the same final state.
--
-- Schema reference: annotations/src/Database/DatabaseMigrator.cs.
\set ON_ERROR_STOP on
-- Wait until annotations has populated its schema. The db-seed container starts
-- only after postgres-local is healthy, but annotations may still be spinning
-- up its tables. A bounded poll keeps the seed deterministic.
DO $$
DECLARE
attempt int := 0;
BEGIN
WHILE attempt < 60 LOOP
PERFORM 1
FROM information_schema.tables
WHERE table_schema = 'public' AND table_name = 'detection_classes';
IF FOUND THEN
EXIT;
END IF;
PERFORM pg_sleep(1);
attempt := attempt + 1;
END LOOP;
IF attempt >= 60 THEN
RAISE EXCEPTION 'detection_classes table not found after 60s — annotations migration did not complete';
END IF;
END $$;
-- Default system_settings row. Annotations starts without one, but several
-- spec assertions rely on `silent_detection = false` and known thumbnail dims
-- so overlay rendering is reproducible.
INSERT INTO system_settings (
id, name, military_unit,
default_camera_width, default_camera_fov,
thumbnail_width, thumbnail_height, thumbnail_border,
generate_annotated_image, silent_detection
) VALUES (
'00000000-0000-0000-0000-00000000aaaa',
'azaion-suite-e2e',
'e2e-unit',
3840, 70,
240, 135, 10,
true, false
) ON CONFLICT (id) DO NOTHING;
-- Default directory_settings row. Annotations writes media files under the
-- paths defined here; the e2e-runner doesn't read these directly but the
-- service requires the row to exist on first hit.
INSERT INTO directory_settings (
id, videos_dir, images_dir, labels_dir, results_dir,
thumbnails_dir, gps_sat_dir, gps_route_dir
) VALUES (
'00000000-0000-0000-0000-00000000bbbb',
'/data/videos', '/data/images', '/data/labels', '/data/results',
'/data/thumbnails', '/data/gps_sat', '/data/gps_route'
) ON CONFLICT (id) DO NOTHING;
-- Default camera_settings row used by detections to size bbox-to-meters.
INSERT INTO camera_settings (
id, altitude, focal_length, sensor_width
) VALUES (
'00000000-0000-0000-0000-00000000cccc',
100, 50, 36
) ON CONFLICT (id) DO NOTHING;
-- Stable e2e user. The UUID is referenced by the spec when asserting
-- annotation rows. Annotations does not own a `users` table — user identity
-- is carried in JWTs minted with JWT_SECRET; the user_id here just needs to
-- be deterministic and stable across runs.
-- Stored in user_settings so the spec can `SELECT user_id` to confirm the
-- seed ran.
INSERT INTO user_settings (
id, user_id,
annotations_left_panel_width, annotations_right_panel_width,
dataset_left_panel_width, dataset_right_panel_width
) VALUES (
'00000000-0000-0000-0000-00000000dddd',
'00000000-0000-0000-0000-0000e2e2e2e2',
300, 400, 320, 320
) ON CONFLICT (id) DO NOTHING;
-- Sanity check — fail loudly if the canonical detection_classes are missing.
-- annotations/src/Database/DatabaseMigrator.cs inserts ids 0..18 unconditionally.
DO $$
DECLARE
cnt int;
BEGIN
SELECT COUNT(*) INTO cnt FROM detection_classes WHERE id BETWEEN 0 AND 18;
IF cnt < 19 THEN
RAISE EXCEPTION 'expected canonical detection_classes 0..18 (count=19), got %', cnt;
END IF;
END $$;
\echo 'suite-e2e seed complete'