# Structural Snapshot — 2026-05-23 (post-cycle 8, strict-validation sweep) Cycle 8 delta against `structure_2026-05-12_cycle5.md` (the most recent snapshot — cycles 6 and 7 did not write snapshots; **process gap** carried forward as a cycle-8 follow-up). Source of truth: `_docs/02_document/module-layout.md` + on-disk `*.csproj` graph + `_docs/02_document/contracts/`. ## Projects | Layer | csproj | Cycle 6+7+8 delta vs cycle-5 snapshot | |-------|--------|---------------------------------------| | 1 (Foundation) | `SatelliteProvider.Common` | **+5 DTO files modified** (cycle 8 only): `DTO/RequestRegionRequest.cs` (`Lat`/`Lon` rename + `[JsonRequired]`), `DTO/CreateRouteRequest.cs` + `DTO/RoutePoint.cs` + `DTO/GeofencePolygon.cs` + `DTO/GeoPoint.cs` (`[JsonRequired]`), `DTO/UavTileMetadata.cs` (`[JsonRequired]` + `Items` on the batch envelope). Plus cycle-6: AZ-505 inventory DTOs. Plus cycle-7: AZ-794 `tileZoom/tileX/tileY → z/x/y` rename. | | 1 (Foundation) | `SatelliteProvider.DataAccess` | **+1 migration in cycle 6** (`015_*.sql` — AZ-505 leaflet covering index); **0 migrations in cycle 7 + cycle 8.** Cycle 8 has zero schema touch — first 2-cycle stretch without schema change since cycle 3-4 (cycle 7 was the first migration-free cycle). | | 1 (Foundation, shared DTO) | `SatelliteProvider.Common` (DTO sub-folder) | **No new DTO files this cycle**; cycle 8 added `[JsonRequired]` annotations to 5 existing files. | | 3 (Application) | `SatelliteProvider.Services.{TileDownloader, RegionProcessing, RouteManagement}` | unchanged (zero service-layer changes in cycle 8 — the entire cycle landed in `SatelliteProvider.Api/Validators/` + `Common/DTO`) | | 4 (API / Entry) | `SatelliteProvider.Api` | **+9 validator files in cycle 8** (`Validators/CreateRouteRequestValidator.cs`, `Validators/GeofencePolygonValidator.cs`, `Validators/GetTileByLatLonQueryValidator.cs`, `Validators/RegionRequestValidator.cs`, `Validators/RejectUnknownQueryParamsEndpointFilter.cs`, `Validators/RoutePointValidator.cs`, `Validators/UavTileBatchMetadataPayloadValidator.cs`, `Validators/UavTileMetadataValidator.cs`, `Validators/UavUploadValidationFilter.cs`); **+1 DTO file** (`DTOs/GetTileByLatLonQuery.cs`, nullable record). Plus cycle-6 + cycle-7 adds: `Validators/ValidationEndpointFilter.cs`, `Validators/ValidationEndpointFilterExtensions.cs`, `Validators/InventoryRequestValidator.cs`, `Validators/GlobalValidatorConfig.cs` (cycle 7 foundations). `Program.cs` extended by every batch (`.WithValidation()`, endpoint filters, `.Accepts<>` / `.Produces<>` / `.ProducesProblem(400)` chains, transient registrations). | | 5 (Test-Support) | `SatelliteProvider.TestSupport` | unchanged | | 6 (Tests) | `SatelliteProvider.Tests` | **+8 test files in cycle 8** under `Tests/Validators/` (RegionRequestValidatorTests + GetTileByLatLonQueryValidatorTests + RejectUnknownQueryParamsEndpointFilterTests + CreateRouteRequestValidatorTests + RoutePointValidatorTests + GeofencePolygonValidatorTests + UavTileBatchMetadataPayloadValidatorTests + UavTileMetadataValidatorTests); **+63 unit-test methods** total. | | 6 (Tests) | `SatelliteProvider.IntegrationTests` | **+5 test files in cycle 8** (RegionFieldRenameTests + RegionRequestValidationTests + GetTileByLatLonValidationTests + CreateRouteValidationTests + UavUploadValidationTests); **+52 integration-test methods** total. `Program.cs` updated by every batch to wire the new entry points into both `RunSmokeSuite` and `RunFullSuite`. `ProblemDetailsAssertions.cs` extended with `AssertErrorsContainsMention` shared helper. | **Project count**: **9** (unchanged from cycle 5 — cycle 8 adds files to existing projects, doesn't add a new csproj). ## Cross-Project Import Edges (compile-time `ProjectReference`) | Edge | Count | Cycle-6+7+8 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} | 2 | unchanged (the cycle-5 +1 edge has held) | **Total ProjectReference edges**: **21** (cycle 5: 21). Net delta across cycles 6 + 7 + 8: **0** — three consecutive cycles with zero new compile-time edges. This is the longest stretch of edge stability in the project's history. ## Source-import sites — cycle 8 delta | Importer | Imports from | Cycle 8 delta | |----------|--------------|---------------| | `SatelliteProvider.Api/Validators/{Create,Get,Region,Route,Uav}*Validator.cs` (9 new files) | `FluentValidation` (12.0.0, cycle-7 dep) + `SatelliteProvider.Common.DTO` | NEW (every cycle-8 validator imports from Common.DTO; no new third-party imports). | | `SatelliteProvider.Api/Validators/RejectUnknownQueryParamsEndpointFilter.cs` (NEW) | `Microsoft.AspNetCore.Http` (framework) | NEW (reusable query-param allow-listing filter). | | `SatelliteProvider.Api/Validators/UavUploadValidationFilter.cs` (NEW) | `FluentValidation` + `Microsoft.AspNetCore.Http` + `System.Text.Json` + `SatelliteProvider.Common.DTO` | NEW (bespoke `IEndpointFilter` for the multipart endpoint). | | `SatelliteProvider.Tests/Validators/*` (8 new files) | `FluentValidation.TestHelper` + `SatelliteProvider.Common.DTO` + `Xunit` | NEW (each test file scoped to one validator). | | `SatelliteProvider.IntegrationTests/*ValidationTests.cs` (5 new files) | existing `IntegrationTests` infrastructure (`ProblemDetailsAssertions`, `TestRunMode`, `JwtTokenFactory`) + `SatelliteProvider.Common.DTO` | NEW (each file scoped to one endpoint's validation surface). | | All other source files | unchanged | — | **~25 new source-level import lines** across the new files; **all internal or framework**. **Zero new third-party imports.** FluentValidation 12.0.0 was added by cycle 7 (it's the foundation that AZ-795 epic builds on); cycle 8 expands its consumer set within the Api project but doesn't introduce a new dependency line. ## Graph properties - **Cycles in project import graph**: **0** (clean DAG — unchanged for 5 consecutive cycles). - **Average ProjectReferences per component**: 21 / 9 = **~2.3** (unchanged). - **Max in-degree**: Common (still highest — **7** incoming edges: Api, TileDownloader, DataAccess, RegionProcessing, RouteManagement, Tests, IntegrationTests). Unchanged. - **Max out-degree**: Tests (**7** — unchanged). - **TestSupport position**: leaf-of-test-subgraph; no production-layer importers (unchanged). ## NuGet dependency hygiene (cycle 8) | Package | Cycle-7 version | Cycle-8 version | Status | |---------|-----------------|-----------------|--------| | FluentValidation | 12.0.0 (cycle 7 add) | unchanged | The cycle-7 retro Action 1 recommended a 12.0.0 → 12.1.1 bump (`D-AZ795-1`). Cycle 8 did NOT take the bump — the security audit re-confirmed Low / Hardening severity, and the cycle-8 scope (per-endpoint child tasks) explicitly excluded NuGet hygiene per `coderule.mdc` scope discipline. **Still OPEN, carried forward.** | | All other NuGet packages across all 9 csproj files | unchanged | **unchanged** | **Zero NuGet bumps this cycle.** Three consecutive zero-bump cycles (cycle 6 closed AZ-505's only NuGet bump; cycle 7 added FluentValidation 12.0.0 + extensions; cycle 8 used FluentValidation entirely against cycle-7's foundation). | | Carry-overs (still OPEN) | Cycle-3 D2-cy4 (`Microsoft.NET.Test.Sdk 17.8.0` transitive `NuGet.Frameworks` flag — **Medium**); cycle-4 D4 (`Microsoft.IdentityModel.Tokens` / `System.IdentityModel.Tokens.Jwt` 7.0.3 NU1902 — Low); `Serilog.AspNetCore` 8.0.3 fallback (Low) | unchanged | All three remain explicitly out of cycle-8 scope (scope discipline). Re-listed in `_docs/05_security/dependency_scan_cycle8.md` carry-over table. | ## Database schema surface (cycle 8 delta) | Object | Change | Source | |--------|--------|--------| | (no schema changes this cycle) | — | — | **Zero schema changes in cycle 8** — second consecutive migration-free cycle (cycle 7 was the first). Cycle 8's strict-validation work lives entirely above the persistence layer (deserializer + FluentValidation + endpoint filters); no field validation requires a column change because all rules are *application-level enforced* (the database still accepts any well-formed value the validator lets through). ## Architecture / contract surface (cycle 8 delta) - **3 NEW contracts published**: - `_docs/02_document/contracts/api/region-request.md` v1.0.0 (AZ-808 — published with post-AZ-812 `lat`/`lon` wire format directly per the coordination clause). - `_docs/02_document/contracts/api/route-creation.md` v1.0.0 → **v1.0.1** (AZ-809 v1.0.0 + an in-cycle PATCH bump for the F-AZ809-1 50-polygon cap added during the Step-14 security-audit follow-up). - `_docs/02_document/contracts/api/tile-latlon.md` v1.0.0 (AZ-811). - **1 contract MINOR bump**: `uav-tile-upload.md` v1.1.0 → **v1.2.0** (AZ-810 — additive: new "Metadata validation" section). - **No contract MAJOR bumps this cycle** (cycle 7 shipped the only MAJOR bump in project history — `tile-inventory.md` 1.0.0 → 2.0.0; cycle 8 produced only new contracts + minor + patch). - **Public-API contract coverage**: now **5 contracts** across **5 documented endpoints** (`region-request`, `route-creation`, `tile-latlon`, `tile-inventory`, `uav-tile-upload`) + the shared `error-shape.md`. Endpoints without a dedicated contract: 2 read-only `GET /api/satellite/{region,route}/{id}` (path-Guid only — no strict-validation surface, per cycle-8 implementation report). - **Contract-per-endpoint ratio**: **5 / 7** = **71%** (cycle 7: 2 / 7 = 29%, cycle 6: 1 / 7 = 14%). Cycle 8 more than doubled the documented-endpoint coverage in a single cycle. - **Architecture findings carry-over from cycle 7**: 0 new architecture-layer findings; 0 architecture-layer carry-overs resolved (cycle 7's open recommendations on implement-skill ↔ downstream-skill artifact contract were partially addressed — cycle 8 produced both the per-batch reports AND the consolidated `implementation_report_strict_validation_cycle8.md` per cycle-7 Action 1 recommendation; see `retro_2026-05-23_cycle8.md` § Pattern 1). ## Net Architecture delta vs cycle 7 - **Resolved (closed by this cycle)**: **1** — cycle-7 Pattern 1 (missing implementation report) is closed by `implementation_report_strict_validation_cycle8.md` + `implementation_completeness_cycle8_report.md` + `cumulative_review_batches_01-04_cycle8_report.md` (three artifact types where cycle 7 had none). - **Newly introduced**: - 1 Medium **resolved in-cycle**: F-AZ809-1 (unbounded `geofences.polygons` DoS) — found in Step 14 security audit, fixed in commit `8fca6e0` with a `MaxPolygons = 50` cap + unit + integration test + contract v1.0.1 + traceability row AZ-809 AC-1b before Step 14 closure. **Net contribution: 0 (resolved before retrospective).** - 2 Low (F-AZ810-1 `JsonException.Message` echo in `UavUploadValidationFilter`; F-AZ810-2 `DateTime` vs `DateTimeOffset` for `CapturedAt` — UTC-deployment-only impact). Open; carry to cycle 9. - 0 new Medium, 0 new High, 0 new Critical (after F-AZ809-1 resolution). - **0 cross-project edges added**, **0 import-graph cycles introduced**, **0 NuGet additions**, **0 migrations**. - **Net Architecture delta**: **-1** (Pattern 1 closed; only Low informational findings introduced, both carried forward). **First negative net architecture delta since cycle 4.** ## What this snapshot says about cycle 8's shape Cycle 8 is the project's **largest single-cycle code volume** to date (5 tasks shipped in 4 batches; 17 SP delivered vs cycle 7's 8 SP; ~115 new test methods; 9 new validator files; 3 new contracts + 1 MINOR + 1 PATCH bump). It is also the **lowest structural-impact cycle of that size**: zero new csproj, zero new NuGet, zero new cross-project edges, zero schema changes, zero new architecture violations. The entire cycle stays inside the `SatelliteProvider.Api/Validators/` namespace + `Common/DTO` annotations + `Tests/Validators/` test directory + 4 probe scripts + 4 new docs — a clean layered addition on top of the cycle-7 foundation (`UnmappedMemberHandling.Disallow`, `GlobalExceptionHandler`, `error-shape.md` v1.0.0, FluentValidation 12.0.0). Cycle 8 also closes Pattern 1 from cycle 7: the implement skill produced both per-batch reports AND a consolidated implementation report AND a completeness report AND a cumulative cross-batch review — four artifact types where cycle 7 had two (per-batch reports + per-batch reviews only). This is the directly visible execution of cycle 7's Action 1 ("formalise the implement-skill ↔ downstream-skill artifact contract") — done implicitly by the implement skill itself this cycle, rather than via the rule-file change cycle 7 recommended. The DAG remains acyclic with the same 9 projects; max-in-degree is still `Common` at 7; the project's architectural shape has held stable across cycles 6 + 7 + 8 despite the steady delivery cadence.