# Structural Snapshot — 2026-05-12 (post-cycle 3, AZ-491 / AZ-492 / AZ-493 / AZ-494 / AZ-495 / AZ-496) Cycle 3 delta against `structure_2026-05-11_cycle2.md`. Source of truth: `_docs/02_document/module-layout.md` + on-disk `*.csproj` graph + `_docs/02_document/contracts/`. ## Projects | Layer | csproj | Cycle 3 delta | |-------|--------|---------------| | 1 (Foundation) | `SatelliteProvider.Common` | unchanged | | 1 (Foundation) | `SatelliteProvider.DataAccess` | unchanged | | 3 (Application) | `SatelliteProvider.Services.TileDownloader` | unchanged | | 3 (Application) | `SatelliteProvider.Services.RegionProcessing` | unchanged | | 3 (Application) | `SatelliteProvider.Services.RouteManagement` | unchanged | | 4 (API / Entry) | `SatelliteProvider.Api` | +`Authentication/AuthenticationServiceCollectionExtensions` extended with `iss`/`aud` resolution + fail-fast (AZ-494); `Microsoft.AspNetCore.OpenApi` 8.0.21 → 8.0.25 and `Microsoft.AspNetCore.Authentication.JwtBearer` 8.0.21 → 8.0.25 (AZ-496) | | **5 (Test-Support, NEW)** | **`SatelliteProvider.TestSupport`** | **NEW project** (AZ-491). `IsPackable=false`. Public surface: `JwtTokenFactory` (the canonical HS256 mint helper, with iss/aud parameters added by AZ-494) + `IntegrationTestResetGuard` (the two-guard model added by AZ-493). NuGet refs: `Microsoft.IdentityModel.Tokens 7.0.3` + `System.IdentityModel.Tokens.Jwt 7.0.3`. | | 6 (Tests) | `SatelliteProvider.Tests` | +`ProjectReference` to `SatelliteProvider.TestSupport` (AZ-491); removed local `TestUtilities/JwtTokenFactory.cs` | | 6 (Tests) | `SatelliteProvider.IntegrationTests` | +`ProjectReference` to `SatelliteProvider.TestSupport` (AZ-491); +`PerfBootstrap.cs` (AZ-492); +`IntegrationTestDatabaseReset.cs` (AZ-493); `JwtTestHelpers.cs` extended with `MintAuthenticated`/`MintExpired`/`ResolveIssuerOrThrow`/`ResolveAudienceOrThrow` (AZ-491 + AZ-494); removed direct `Microsoft.IdentityModel.JsonWebTokens` PackageReference | **Project count**: 9 (+1 from cycle 2; new `SatelliteProvider.TestSupport`). ## Cross-Project Import Edges (compile-time `ProjectReference`) | Edge | Count | |------|-------| | 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} | 6 (unchanged; cycle-2 baseline) | | **Tests → TestSupport** | **+1 (AZ-491)** | | **IntegrationTests → TestSupport** | **+1 (AZ-491)** | **Total ProjectReference edges**: 20 (+2 vs cycle 2). Both new edges go INTO TestSupport; no production-layer dependency on TestSupport (the IsPackable=false guarantee blocks accidental shipping). ## Source-import sites — cycle 3 delta | Importer | Imports from | Cycle 3 delta | |----------|--------------|---------------| | WebApi `Authentication` | `Microsoft.IdentityModel.Tokens` | +1 site (`ResolveRequiredOrThrow` extension; `ValidateIssuer`/`ValidateAudience` flags on `TokenValidationParameters`) | | IntegrationTests | `SatelliteProvider.TestSupport` | +N sites (`JwtTokenFactory`, `IntegrationTestResetGuard`) — replaces removed local helpers | | Tests | `SatelliteProvider.TestSupport` | +N sites (`JwtTokenFactory` in 5 test files; `IntegrationTestResetGuard` in unit `IntegrationTestResetGuardTests`) | | IntegrationTests | `Npgsql` | +1 site (`IntegrationTestDatabaseReset` uses `NpgsqlConnection` + `NpgsqlCommand`) | | IntegrationTests | `SixLabors.ImageSharp` (already present) | +1 site (`PerfBootstrap.CreateValidJpeg`) | ## Graph properties - **Cycles in project import graph**: 0 (clean DAG — unchanged) - **Average ProjectReferences per component**: 20 / 9 = ~2.2 (cycle 2 = 12 / 8 = 1.5; growth is concentrated in test surface, not production) - **Max in-degree**: Common (still highest at 6 — Api, TileDownloader, DataAccess, RegionProcessing, RouteManagement, Tests). TestSupport in-degree = 2 (Tests + IntegrationTests). - **Max out-degree**: Tests (7 — adds the new TestSupport edge to its existing 6). - **TestSupport position**: leaf-of-test-subgraph; no production-layer importers. Out-degree = 0 from a project-graph standpoint (it does not depend on any other satellite-provider project). NuGet-only dependencies. ## NuGet dependency hygiene (cycle 3) | Package | Version | Status | |---------|---------|--------| | `Microsoft.AspNetCore.OpenApi` | 8.0.21 → **8.0.25** | RESOLVED D1 (AZ-496) | | `Microsoft.AspNetCore.Authentication.JwtBearer` | 8.0.21 → **8.0.25** | RESOLVED D3 (AZ-496) | | `Microsoft.IdentityModel.Tokens` (TestSupport) | 7.0.3 (NEW) | NEW D4 — CVE-2024-21319, test-only, bump to 7.1.2 tracked as future PBI | | `System.IdentityModel.Tokens.Jwt` (TestSupport) | 7.0.3 (NEW) | NEW D4 — same advisory; same disposition | | `SixLabors.ImageSharp` | 3.1.11 (unchanged) | clean | | `Npgsql` | 9.0.2 (unchanged) | clean (now consumed by AZ-493's reset hook) | | `Microsoft.Extensions.*` | 9.0.10 (unchanged across DataAccess, TileDownloader, Tests, RegionProcessing, RouteManagement) | consistent across all 5 sites | ## Architecture / contract surface (cycle 3 delta) - No new public-API contracts under `_docs/02_document/contracts/` this cycle. AZ-494 strictly tightens token acceptance (rejects with 401 on iss/aud mismatch) without changing the public HTTP surface. - The `Microsoft.AspNetCore.Authentication.JwtBearer` middleware now validates four claim-shape invariants instead of two (signing key + lifetime + **issuer** + **audience**). - Cycle-2 contract `_docs/02_document/contracts/api/uav-tile-upload.md` v1.0.0 unchanged. ## Net Architecture delta vs cycle 2 - **Resolved**: F-AUTH-2 (iss/aud unvalidated), D1 (OpenApi 8.0.21 patch line), D3 (JwtBearer 8.0.21 patch line), PT-07/PT-08 cycle-2 leftover. **Total: 4 resolved.** - **Newly introduced**: D4 (Low, test-only NU1902 pin), F-AUTH-3 (Informational, test-runner log), F-AUTH-4 (Informational, by-design DEV-ONLY placeholders), F-DBR-2 (Low, test-only TRUNCATE guard), F-PERF-1 (Low, operator-CLI token-on-stdout). F-DBR-1 explicit false-positive. **Total: 3 new Low + 2 Informational + 1 FP.** - **Net Architecture delta**: -1 finding overall, -3 Medium-or-above (the 3 cycle-3 introductions are all Low or Informational; the 3 prior resolutions were 2 Medium + 1 Low). First cycle with a net-negative architecture delta. Pattern: prior-retro Action items + targeted dependency bumps + spec hardening combined to retire more debt than they introduced.