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>
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_AC1 → Az503MigrationSupersedesAz484UniqueIndex); 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
Uuidv5algorithm the production code uses, otherwise the new NOT NULLlocation_hashcolumn would force every seed to encode the SHA-1 byte order by hand. ReusingSatelliteProvider.Common.Utils.Uuidv5keeps the algorithm in one place (which is also where the cross-repo invariant withgps-denied-onboard/components/c6_tile_cache/_uuid.pylives).
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.mdv1.0.0 → v1.1.0 (additive — adds optionalmetadata.flightId: uuid?; adds derivedtileIdfield in the response). Backward-compatible with cycle-4 clients. - No new public-API contract files. (AZ-505 will introduce
inventory.mdnext 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}.jpgare not moved. Documented inuav-tile-upload.md"File-path layout" section. - New cross-repo invariant: the
TileNamespaceGUID5b8d0c2e-7f1a-4d3b-9c5e-1f3a8e7d2b6cinSatelliteProvider.Common/Utils/Uuidv5.csmust match the same constant ingps-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.flightIdnot authenticated provenance; F2-cy5pgcryptodeployment runbook gap on managed Postgres). Both are long-term recommendations, not code defects. - 1 Low Maintainability finding in the AZ-503 code review (the
contentSha256soft-NULL guard inTileService.BuildTileEntitywhenFile.Existsis false — practically unreachable, defensively kept). - 0 new Medium, 0 new High, 0 new Critical.
- 2 Low informational findings in the security audit (F1-cy5
- +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.