Files
satellite-provider/_docs/06_metrics/structure_2026-05-12_cycle5.md
Oleksandr Bezdieniezhnykh ea278afb37 [AZ-503] [AZ-504] Cycle 5 Step 17: retrospective + close cycle
retro_2026-05-12_cycle5.md captures the cycle-end retrospective:
- Implementation: 2 tasks (AZ-504 + AZ-503-foundation), 4 SP total,
  100% first-attempt pass rate, 1 mid-implement scope-split
  (AZ-503 → AZ-503-foundation + AZ-505, blocked-linked).
- Quality: 50/50 PASS/PASS_WITH_WARNINGS, 0 new Medium+, 1 new Low
  (defensive contentSha256 soft-NULL guard).
- Security: PASS_WITH_WARNINGS, 0 new Critical/High/Medium, 2 new
  Low informational (F1 flightId provenance, F2 pgcrypto runbook
  gap).
- Performance: PASS_WITH_INFRA_WARNINGS — first measurable PT-08
  ever (Run #1 199ms, Run #2 117ms vs 2000ms threshold); PT-01/02
  failed on recurring local Docker/colima DNS cold-start, not an
  app regression.
- Structural: +1 ProjectReference edge (IntegrationTests → Common),
  +1 minor contract bump (uav-tile-upload 1.0.0 → 1.1.0), +1 DB
  migration (014_AddTileIdentityColumns.sql), 0 NuGet bumps,
  0 csproj additions, DAG still acyclic at 9 projects.

structure_2026-05-12_cycle5.md captures the structural snapshot.

LESSONS.md updated with 3 cycle-5 entries (oldest dropped to
preserve the 15-entry ring buffer):
- [architecture] Cross-repo cryptographic invariants must live as
  code constants in both repos with reference-vector tests.
- [tooling] When perf-mode "one re-run" fires twice with the same
  DNS root cause, escalate from re-run to harness fix.
- [process] Spec contradicts live code by >=2 prerequisites →
  prefer split into foundation + follow-up (A/B/C option C).

Top 3 follow-up actions (cycle 6 candidates):
- Action 1 (1 SP): DNS pre-warm in scripts/run-performance-tests.sh
  → closes the cycle-3 perf-harness leftover.
- Action 2 (5 SP): AZ-505 — inventory endpoint + HTTP/2 + Leaflet
  covering index (blocked-linked on AZ-503-foundation, this cycle).
- Action 3 (1 SP): pgcrypto pre-install runbook step (F2-cy5 doc
  fix).

Cycle 5 closed. Autodev state advanced for cycle 6 by the next
/autodev invocation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 18:07:57 +03:00

10 KiB

Structural Snapshot — 2026-05-12 (post-cycle 5, AZ-503-foundation + AZ-504)

Cycle 5 delta against structure_2026-05-12_cycle4.md. Source of truth: _docs/02_document/module-layout.md + on-disk *.csproj graph + _docs/02_document/contracts/.

Projects

Layer csproj Cycle 5 delta
1 (Foundation) SatelliteProvider.Common +1 file: Utils/Uuidv5.cs (NEW, 80 LoC; RFC 9562 §5.5 SHA-1 UUIDv5 + pinned TileNamespace GUID). No new NuGet deps; uses framework-only System.Security.Cryptography.SHA1 + System.Buffers.Binary.BinaryPrimitives.
1 (Foundation) SatelliteProvider.DataAccess +1 migration: Migrations/014_AddTileIdentityColumns.sql (NEW, embedded resource); +4 properties on Models/TileEntity.cs (FlightId, LocationHash, ContentSha256, LegacyId); UPSERT in Repositories/TileRepository.cs rewritten for the integer-key + flight-aware contract. No new NuGet deps; pgcrypto extension enabled by the migration script (PG-server-side, not a NuGet).
1 (Foundation, shared DTO) SatelliteProvider.Common (DTO sub-folder) +1 property: DTO/UavTileMetadata.FlightId (Guid?, init-only, optional). Contract uav-tile-upload.md bumped 1.0.0 → 1.1.0 (additive).
3 (Application) SatelliteProvider.Services.TileDownloader +ImportSite: using SatelliteProvider.Common.Utils (for Uuidv5.Create) + using System.Security.Cryptography (for SHA256.HashData) in TileService.cs and UavTileUploadHandler.cs. UavTileUploadHandler.BuildUavTileFilePath gains an optional Guid? flightId parameter. No new csproj refs.
3 (Application) SatelliteProvider.Services.RegionProcessing unchanged
3 (Application) SatelliteProvider.Services.RouteManagement unchanged
4 (API / Entry) SatelliteProvider.Api unchanged (Program.cs not edited this cycle)
5 (Test-Support) SatelliteProvider.TestSupport unchanged
6 (Tests) SatelliteProvider.Tests +1 test class (Uuidv5Tests.cs); +3 facts in UavTileFilePathTests.cs; +2 facts in UavTileUploadHandlerTests.cs.
6 (Tests) SatelliteProvider.IntegrationTests +1 ProjectReference (SatelliteProvider.Common) — so raw-SQL seeds can call Uuidv5.Create directly; +2 facts in UavUploadTests.cs (AC-3 multi-flight, AC-4 float-rounding); +3 facts in MigrationTests.cs (Az503 columns / index / backfill determinism); 1 fact superseded (NewUniqueConstraintIncludesSourceColumn_AZ484_AC1Az503MigrationSupersedesAz484UniqueIndex); seed for the pre-existing MultiSourceCoexistence_AZ484_Cycle2 test repaired to populate location_hash.

Project count: 9 (unchanged from cycle 4 — AZ-503-foundation adds files to existing projects, doesn't add a new csproj).

Cross-Project Import Edges (compile-time ProjectReference)

Edge Count Cycle 5 delta
Api → {Common, DataAccess, TileDownloader, RegionProcessing, RouteManagement} 5 unchanged
TileDownloader → {Common, DataAccess} 2 unchanged
DataAccess → {Common} 1 unchanged
RegionProcessing → {Common, DataAccess} 2 unchanged
RouteManagement → {Common, DataAccess} 2 unchanged
Tests → {Api, TileDownloader, RegionProcessing, RouteManagement, Common, DataAccess, TestSupport} 7 unchanged
IntegrationTests → {TestSupport, Common (NEW)} 2 +1 edge (motivated by AZ-503 — seeds need the production Uuidv5.Create algorithm to compute location_hash for raw-SQL inserts)

Total ProjectReference edges: 21 (cycle 4: 20). Net delta: +1 edge.

Source-import sites — cycle 5 delta

Importer Imports from Cycle 5 delta
SatelliteProvider.Services.TileDownloader/TileService.cs SatelliteProvider.Common.Utils (Uuidv5), System.Security.Cryptography (SHA256) NEW (AZ-503 deterministic identity + content hash)
SatelliteProvider.Services.TileDownloader/UavTileUploadHandler.cs SatelliteProvider.Common.Utils (Uuidv5), System.Security.Cryptography (SHA256) NEW (same — UAV write path)
SatelliteProvider.IntegrationTests/UavUploadTests.cs SatelliteProvider.Common.Utils (Uuidv5) NEW (seed helper for raw-SQL inserts)
SatelliteProvider.Tests/Uuidv5Tests.cs SatelliteProvider.Common.Utils NEW (unit-test class)
All other source files unchanged

5 new source-level import lines across 4 files; all to either the new internal SatelliteProvider.Common.Utils.Uuidv5 utility or the framework System.Security.Cryptography.SHA256. Zero new third-party imports.

Graph properties

  • Cycles in project import graph: 0 (clean DAG — unchanged)
  • Average ProjectReferences per component: 21 / 9 = ~2.3 (cycle 4: ~2.2). Net delta: +0.1 (one new IntegrationTests → Common edge).
  • Max in-degree: Common (still highest — now at 7 incoming edges: Api, TileDownloader, DataAccess, RegionProcessing, RouteManagement, Tests, IntegrationTests (new)). Cycle 4 had Common at 6.
  • Max out-degree: Tests (7 — unchanged).
  • TestSupport position: leaf-of-test-subgraph; no production-layer importers (unchanged).
  • The new IntegrationTests → Common edge is justified: integration-test seeds need the same deterministic Uuidv5 algorithm the production code uses, otherwise the new NOT NULL location_hash column would force every seed to encode the SHA-1 byte order by hand. Reusing SatelliteProvider.Common.Utils.Uuidv5 keeps the algorithm in one place (which is also where the cross-repo invariant with gps-denied-onboard/components/c6_tile_cache/_uuid.py lives).

NuGet dependency hygiene (cycle 5)

Package Cycle-4 version Cycle-5 version Status
All NuGet packages across all 9 csproj files unchanged unchanged Zero NuGet bumps this cycle. AZ-503-foundation uses framework-only types (SHA1, SHA256, BinaryPrimitives); AZ-504 is a 2-line shell-script edit.
Carry-overs (still OPEN) Cycle-3 D2 (Microsoft.NET.Test.Sdk 17.8.0 transitive NuGet.Frameworks flag), cycle-4 D4 (Microsoft.IdentityModel.Tokens 7.0.3 + System.IdentityModel.Tokens.Jwt 7.0.3 in TestSupport), Serilog.AspNetCore 8.0.3 fallback unchanged All three remain explicitly out of cycle-5 scope per coderule.mdc "scope discipline".

Database schema surface (cycle 5 delta)

Object Change Source
tiles table +4 columns: flight_id uuid NULL, location_hash uuid NOT NULL (backfilled deterministically for pre-existing rows), content_sha256 bytea NULL, legacy_id uuid NULL 014_AddTileIdentityColumns.sql
idx_tiles_unique_location_source (AZ-484, float-based) DROPPED same
idx_tiles_unique_location (pre-AZ-484, defensive duplicate cleanup) DROPPED same
idx_tiles_unique_identity (integer-key + COALESCE(flight_id, zero-UUID)) CREATED (unique) same
idx_tiles_location_hash (location_hash lookups for the future AZ-505 inventory endpoint) CREATED (non-unique) same
Postgres extension pgcrypto CREATE EXTENSION IF NOT EXISTS (used during the migration's session-scoped pg_temp.uuidv5 PL/pgSQL function only) same

The migration runs in a single transaction and is idempotent under DbUp's journal. No table or column was renamed (per coderule.mdc "Do not rename any database objects without confirmation").

Architecture / contract surface (cycle 5 delta)

  • Contract bumped: _docs/02_document/contracts/api/uav-tile-upload.md v1.0.0 → v1.1.0 (additive — adds optional metadata.flightId: uuid?; adds derived tileId field in the response). Backward-compatible with cycle-4 clients.
  • No new public-API contract files. (AZ-505 will introduce inventory.md next cycle.)
  • New per-flight on-disk path layout for UAV tiles: ./tiles/uav/{flightId or 'none'}/{z}/{x}/{y}.jpg. Additive — legacy paths under ./tiles/uav/{z}/{x}/{y}.jpg are not moved. Documented in uav-tile-upload.md "File-path layout" section.
  • New cross-repo invariant: the TileNamespace GUID 5b8d0c2e-7f1a-4d3b-9c5e-1f3a8e7d2b6c in SatelliteProvider.Common/Utils/Uuidv5.cs must match the same constant in gps-denied-onboard/components/c6_tile_cache/_uuid.py (sibling workspace). Cycle-5 commit covers the satellite-provider side; the gps-denied-onboard side will be handled when AZ-505 / the consumer-side work runs.

Net Architecture delta vs cycle 4

  • Resolved (closed by this cycle): 0 architecture-level findings closed (the cycle-3 perf-harness leftover is half-closed — AZ-504 script fix is verified working, but the exit-0 deletion criterion is blocked by recurring local DNS noise; replay #5 documented).
  • Newly introduced (informational only):
    • 2 Low informational findings in the security audit (F1-cy5 metadata.flightId not authenticated provenance; F2-cy5 pgcrypto deployment runbook gap on managed Postgres). Both are long-term recommendations, not code defects.
    • 1 Low Maintainability finding in the AZ-503 code review (the contentSha256 soft-NULL guard in TileService.BuildTileEntity when File.Exists is false — practically unreachable, defensively kept).
    • 0 new Medium, 0 new High, 0 new Critical.
  • +1 cross-project edge (IntegrationTests → Common) — justified (cross-repo invariant deduplication).
  • Contract delta: 1 minor version bump on uav-tile-upload.md (1.0.0 → 1.1.0, additive).
  • Schema delta: +1 migration (014), +4 columns, +2 indexes, -2 indexes, +1 Postgres extension.
  • Net Architecture delta: 0 net architecture-level findings (the 3 new Lows are informational only, and the +1 edge + minor contract bump are net-neutral structural additions, not violations).

What this snapshot says about cycle 5's shape

Cycle 5 is the project's first schema-changing cycle since cycle 1's AZ-484 — a database migration with backfill, a new internal cross-component algorithm (Uuidv5), a contract minor bump, and a new on-disk layout for one tile source. Quantitatively it's small (4 SP delivered = 3 SP foundation + 1 SP script fix), but it lays foundational identity infrastructure that next cycle's AZ-505 (5 SP — inventory endpoint, HTTP/2, Leaflet covering index) is blocked on. The structural delta is bounded (+1 import edge, +1 minor contract version, +1 migration, +4 columns, +2 indexes, -2 indexes, 0 new csproj, 0 new NuGet bumps), and the DAG remains acyclic with the same 9 projects.