[AZ-503] [AZ-504] Cycle 5 Steps 11-15 sync

Wrap up cycle 5 verification + documentation:
- Steps 10/11 wrap-up reports (implementation_completeness +
  implementation_report) for the AZ-503-foundation + AZ-504 batch.
- Step 12 test-spec sync: AZ-503-foundation/AZ-504 ACs appended;
  AZ-505 deferred ACs recorded.
- Step 13 update-docs: architecture, data-model, glossary, module-
  layout, uav-tile-upload contract (v1.1.0), DataAccess + Services
  + Tests module docs synced; new common_uuidv5.md module doc.
- Step 14 security audit: PASS_WITH_WARNINGS; 0 new Critical/High;
  2 new Low informational (F1 flightId provenance, F2 pgcrypto
  deploy gap).
- Step 15 performance test: PASS_WITH_INFRA_WARNINGS; PT-08
  passed twice (AZ-504 fix verified); PT-01/02 failed due to
  recurring local Docker/colima DNS cold-start (not an app
  regression). Cycle-3 perf-harness leftover stays OPEN with
  replay #5 documented.
- Autodev state moved to Step 16 (Deploy).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-12 18:01:27 +03:00
parent c646aa93e2
commit 61612044fb
27 changed files with 1075 additions and 50 deletions
@@ -7,12 +7,17 @@ Database entity classes that map directly to PostgreSQL tables via Dapper. Prope
### TileEntity
Maps to `tiles` table.
- `Id` (Guid), `TileZoom` (int), `TileX` (int), `TileY` (int)
- `Id` (Guid) — AZ-503: deterministic UUIDv5 of `{tile_zoom}/{tile_x}/{tile_y}/{source}/{flight_id or '00000000-0000-0000-0000-000000000000'}` under namespace `Uuidv5.TileNamespace`. Stable across re-ingests; preserved on UPSERT conflict.
- `TileZoom` (int), `TileX` (int), `TileY` (int)
- `Latitude`, `Longitude` (double), `TileSizeMeters` (double), `TileSizePixels` (int)
- `ImageType` (string), `MapsVersion` (string?), `Version` (int) — `MapsVersion`/`Version` are vestigial post-AZ-484 (kept nullable for backward compatibility; no longer part of the unique key)
- `Source` (string) — AZ-484 producer wire value, defaults to `TileSourceConverter.GoogleMapsWireValue` (`"google_maps"`). Stored as plain string (not the `TileSource` enum) due to Dapper issue #259 — see `_docs/LESSONS.md` L-001. Convert via `SatelliteProvider.Common.Enums.TileSourceConverter.{ToWireValue,FromWireValue}`.
- `CapturedAt` (DateTime, UTC) — AZ-484 imagery acquisition timestamp; drives the most-recent-across-sources selection.
- `FilePath` (string), `CreatedAt`, `UpdatedAt` (DateTime)
- `FlightId` (Guid?) — AZ-503: optional flight identifier. `null` for Google Maps tiles; populated from `UavTileMetadata.FlightId` on UAV uploads. Part of the AZ-503 UPSERT conflict key via `COALESCE(flight_id, '00000000-0000-0000-0000-000000000000'::uuid)`, so two flights uploading the same `(z, x, y)` cell produce two separate rows.
- `LocationHash` (Guid) — AZ-503 `NOT NULL`: deterministic UUIDv5 of `{tile_zoom}/{tile_x}/{tile_y}` under `Uuidv5.TileNamespace`. Identical across flights and sources for the same cell. Backfilled in migration 014 via a `pg_temp.uuidv5` PL/pgSQL function; subsequent inserts compute it in the application layer (`TileService` + `UavTileUploadHandler`). Reserved for AZ-505's Leaflet covering index (`POST /tiles/inventory`) — not yet on a unique constraint.
- `ContentSha256` (byte[]?) — AZ-503: SHA-256 digest of the JPEG body. Application code enforces `NOT NULL` for new writes via `TileService.BuildTileEntity` (Google Maps) and `UavTileUploadHandler.PersistAsync` (UAV). The DB column is `bytea NULL` because legacy pre-migration rows could not be backfilled reliably from disk (file paths are volatile). See `batch_02_cycle5_report.md` "Low maintainability finding" for the rationale.
- `LegacyId` (Guid?) — AZ-503: pre-migration `id` value, populated by migration 014 from every existing row's `id`. Preserves random-`Guid` provenance for one cycle (per AZ-503 Risk 1 mitigation) so external references to the old id can still be diagnosed before deletion.
### RegionEntity
Maps to `regions` table.