diff --git a/_docs/02_document/architecture.md b/_docs/02_document/architecture.md index 0850c93..0fe387c 100644 --- a/_docs/02_document/architecture.md +++ b/_docs/02_document/architecture.md @@ -24,6 +24,7 @@ The three Layer-3 service components are compile-time siblings: each only refere - Cross-repo deterministic tile identity (AZ-503) — the `TileNamespace` UUID `5b8d0c2e-7f1a-4d3b-9c5e-1f3a8e7d2b6c` and the canonical name format are shared with the sibling workspace `gps-denied-onboard` (`components/c6_tile_cache/_uuid.py:TILE_NAMESPACE`). Both sides MUST produce byte-identical UUIDv5 output so an onboard-cached tile and a server-cached tile for the same `(z, x, y, source, flight_id)` are recognized as the same artifact without a round-trip. Changing the namespace constant on either side is a coordinated cross-repo break. (`inferred-from: Uuidv5.cs, AZ-503 task spec § Constraints`) - Fire-and-forget async processing with status polling (`inferred-from: queue + background service + status endpoint`) - JWT-validated callers only — every HTTP endpoint requires a valid HS256-signed Bearer token, validated locally against a shared `JWT_SECRET` per the suite-level auth contract (`suite/_docs/10_auth.md`). Issuer/audience are intentionally not validated yet; signature + lifetime + ≥32-byte key are. Per-endpoint permission claims (e.g. `permissions: ["GPS"]` on the UAV upload) layer on top of this baseline. +- Strict wire-format validation at the API edge (AZ-795 epic, completed across cycles 7-8) — every public-facing endpoint runs every incoming payload through two collaborating layers BEFORE the handler sees it: (a) `JsonSerializerOptions.UnmappedMemberHandling.Disallow` + `[JsonRequired]` on every non-optional DTO axis at the System.Text.Json deserializer; (b) per-endpoint FluentValidation `IValidator` wired via `WithValidation()` (JSON bodies) or `UavUploadValidationFilter` (multipart) or `RejectUnknownQueryParamsEndpointFilter` + `GetTileByLatLonQueryValidator` (query params). Both layers produce identically-shaped RFC 7807 `ValidationProblemDetails` per `error-shape.md` v1.0.0, so callers see one error contract regardless of which layer fired. The principle is: **no payload reaches a handler unless every field is present, every type matches, every range is honored, and no unknown field was silently dropped.** This closes the silent-coercion footgun class (e.g. missing `id` → zero-Guid → untracked region/route; typo `?latitude=` → `lat=0`; misnamed `{"Latitude":...}` → `lat=0`) that pre-cycle-7 produced misleading 200-OK responses. Adding a new public endpoint requires either a `WithValidation()` chain (JSON), a `UavUploadValidationFilter`-style multipart filter, or an `RejectUnknownQueryParamsEndpointFilter` + query validator (query string) — there is no other approved path. **Authentication & Authorization** (AZ-487): - Validation library: `Microsoft.AspNetCore.Authentication.JwtBearer` 10.0.7 (matches `Microsoft.AspNetCore.OpenApi` 10.0.7; AZ-496 bumped both packages from 8.0.21 → 8.0.25 in cycle 3 to close the cycle-1 D1 + cycle-2 D3 supply-chain findings, then AZ-500 bumped both 8.0.25 → 10.0.7 in cycle 4 as part of the .NET 8 → .NET 10 migration). The `TokenValidationParameters` shape is unchanged across the JwtBearer 8 → 10 jump — AZ-487/AZ-494 integration tests are the gate and all pass on .NET 10. @@ -39,7 +40,7 @@ The three Layer-3 service components are compile-time siblings: each only refere - *Google Maps* — `TileService.DownloadAndStoreTilesAsync` / `DownloadAndStoreSingleTileAsync` stamp `source='google_maps'`, `flight_id=NULL`, and a deterministic UUIDv5 `id` on every persisted row; tile JPEGs live under `{StorageConfig.TilesDirectory}/{zoom}/...` per the legacy grandfathered layout. `content_sha256` is computed from the on-disk JPEG body. - *UAV* — `POST /api/satellite/upload` (AZ-488; per-flight key extended by AZ-503) accepts a multipart batch of UAV-captured tiles, runs each item through a 5-rule quality gate (`UavTileQualityGate`), and persists accepted items via `ITileRepository.InsertAsync` with `source='uav'`, `flight_id = metadata.flightId` (or NULL for anonymous uploads), and a deterministic UUIDv5 `id`. UAV JPEGs live under `{StorageConfig.TilesDirectory}/uav/{flight_id or 'none'}/{zoom}/{x}/{y}.jpg`, so `rm -rf ./tiles/uav/{flight_id}/` removes one flight's evidence without touching other flights at overlapping cells. Requires the `GPS` permission claim on top of the JWT baseline. -The N-source storage contract is authoritative in `_docs/02_document/contracts/data-access/tile-storage.md` (v2.0.0 — bumped jointly by AZ-503-foundation and AZ-505 in cycle 6 to capture the identity columns, `tiles_leaflet_path` covering index, and `location_hash`-keyed leaflet read rule). The UAV upload contract is authoritative in `_docs/02_document/contracts/api/uav-tile-upload.md` (v1.1.0; AZ-503 added an optional `flightId` field to per-item metadata — backward-compatible). The bulk tile-inventory contract is authoritative in `_docs/02_document/contracts/api/tile-inventory.md` (v1.0.0; AZ-505). Anything that reads or writes `tiles` MUST follow those contracts rather than re-deriving the rules from prose here. +The N-source storage contract is authoritative in `_docs/02_document/contracts/data-access/tile-storage.md` (v2.0.0 — bumped jointly by AZ-503-foundation and AZ-505 in cycle 6 to capture the identity columns, `tiles_leaflet_path` covering index, and `location_hash`-keyed leaflet read rule). The UAV upload contract is authoritative in `_docs/02_document/contracts/api/uav-tile-upload.md` (v1.2.0; AZ-503 added an optional `flightId` field to per-item metadata in v1.1.0, AZ-810 cycle 8 added the strict metadata-validation section in v1.2.0). The bulk tile-inventory contract is authoritative in `_docs/02_document/contracts/api/tile-inventory.md` (v2.0.0; AZ-505 v1.0.0, AZ-794+AZ-796 cycle 7 bumped to v2.0.0 with the OSM `z/x/y` rename + strict validation rules). The four wire-format contracts added in cycle 8 are authoritative for their respective endpoints: `_docs/02_document/contracts/api/region-request.md` v1.0.0 (`POST /api/satellite/request`, AZ-808+AZ-812), `_docs/02_document/contracts/api/route-creation.md` v1.0.0 (`POST /api/satellite/route`, AZ-809), `_docs/02_document/contracts/api/tile-latlon.md` v1.0.0 (`GET /api/satellite/tiles/latlon`, AZ-811), and `_docs/02_document/contracts/api/error-shape.md` v1.0.0 (the cross-endpoint RFC 7807 `ValidationProblemDetails` envelope shared by every validating endpoint, AZ-795 cycle 7). Anything that reads or writes `tiles` MUST follow those contracts rather than re-deriving the rules from prose here. **Drift signals**: - `geofence_polygons` mentioned in AGENTS.md as a routes table column but does not exist in schema or entity — documentation drift diff --git a/_docs/02_document/ripple_log_cycle8.md b/_docs/02_document/ripple_log_cycle8.md new file mode 100644 index 0000000..620f886 --- /dev/null +++ b/_docs/02_document/ripple_log_cycle8.md @@ -0,0 +1,105 @@ +# Cycle 8 — Documentation Ripple Log + +**Cycle**: 8 (AZ-808 region-request validation + AZ-809 route-creation validation + AZ-810 UAV upload metadata validation + AZ-811 GET tiles/latlon validation + AZ-812 Region OSM rename) +**Generated by**: `/document` skill (task mode) during autodev Step 13 +**Resolution method**: `Grep --type cs` against every new or changed symbol introduced by the five tasks. C# `using`-based import analysis on `RequestRegionRequest` (renamed `Lat`/`Lon`), `UavTileMetadata` + `UavTileBatchMetadataPayload` (`[JsonRequired]`), `CreateRouteRequest` + `RoutePoint` + `GeofencePolygon` + `Geofences` + `GeoPoint` (`[JsonRequired]`), the four new validator classes, the two new envelope filters (`UavUploadValidationFilter`, `RejectUnknownQueryParamsEndpointFilter`), and the new query DTO `GetTileByLatLonQuery`. No static-analyzer used — the new surface lives almost entirely behind `Program.cs` + the four per-endpoint test files, so the literal usage scan is exhaustive. + +## Directly-changed source files (cycle 8) + +- `SatelliteProvider.Common/DTO/RequestRegionRequest.cs` (AZ-812, modified) — C# properties renamed `Latitude/Longitude` → `Lat/Lon` with `[JsonPropertyName("lat")]` / `[JsonPropertyName("lon")]`. AZ-808 added `[JsonRequired]` to `Id` + the two coordinate axes + `SizeMeters` + `ZoomLevel` + `StitchTiles` so partial bodies are rejected at the deserializer layer. +- `SatelliteProvider.Common/DTO/UavTileMetadata.cs` (AZ-810, modified) — `[JsonRequired]` added to `Latitude`, `Longitude`, `TileZoom`, `TileSizeMeters`, `CapturedAt`. `FlightId` intentionally NOT marked required (AZ-503 anonymous-flight semantics require `null` to be valid). `UavTileBatchMetadataPayload.Items` also marked `[JsonRequired]`. +- `SatelliteProvider.Common/DTO/RoutePoint.cs` (AZ-809, modified) — `[JsonRequired]` on `Latitude` + `Longitude`; `[JsonPropertyName("lat")]` / `[JsonPropertyName("lon")]` confirm the wire shape pre-rename (`RoutePoint` was already using `lat`/`lon`, only `RequestRegionRequest` was the OSM-rename target). +- `SatelliteProvider.Common/DTO/CreateRouteRequest.cs` (AZ-809, modified) — `[JsonRequired]` on every non-optional field (`Id`, `Name`, `RegionSizeMeters`, `ZoomLevel`, `Points`, `RequestMaps`, `CreateTilesZip`); `Description` and `Geofences` left optional. +- `SatelliteProvider.Common/DTO/GeofencePolygon.cs` (AZ-809, modified) — `[JsonRequired]` on `NorthWest` + `SouthEast` so partial polygons fail at the deserializer. +- `SatelliteProvider.Common/DTO/Geofences.cs` (AZ-809, modified) — `[JsonRequired]` on `Polygons` so `geofences: {}` is rejected. +- `SatelliteProvider.Common/DTO/GeoPoint.cs` (AZ-809, modified) — `[JsonRequired]` on both axes for the geofence-corner case. +- `SatelliteProvider.Api/Program.cs` (AZ-808/809/810/811/812, modified) — + - `.WithValidation()` on `MapPost("/api/satellite/request", …)` (AZ-808). + - `.WithValidation()` on `MapPost("/api/satellite/route", …)` (AZ-809). + - `.AddEndpointFilter()` on `MapPost("/api/satellite/upload", …)` (AZ-810) — bespoke multipart filter, not the generic `WithValidation()`. + - `.AddEndpointFilter(new RejectUnknownQueryParamsEndpointFilter("lat", "lon", "zoom"))` + `.WithValidation()` on `MapGet("/api/satellite/tiles/latlon", …)` (AZ-811) — two-filter chain so unknown-key rejection precedes range checks. + - `AddTransient()` registration (AZ-810 — the filter has injected dependencies). + - The `ParameterDescriptionFilter` Swagger op filter trimmed the obsolete `Latitude`/`Longitude`/`ZoomLevel` query-param annotations (AZ-811 cleanup; lat/lon/zoom remain). +- `SatelliteProvider.Api/Validators/RegionRequestValidator.cs` (AZ-808, **new**) — `AbstractValidator` with rules for non-zero `id`, `lat`/`lon` ranges, `sizeMeters` ∈ \[100, 10000\], `zoomLevel` ∈ \[0, 22\]. +- `SatelliteProvider.Api/Validators/CreateRouteRequestValidator.cs` (AZ-809, **new**) — root validator chaining `RuleForEach(req => req.Points).SetValidator(new RoutePointValidator())` for per-point checks, `RuleForEach(req => req.Geofences!.Polygons).SetValidator(new GeofencePolygonValidator()).OverridePropertyName("geofences.polygons")` for per-polygon checks, plus `Must(req => !(req.CreateTilesZip && !req.RequestMaps)).WithName("createTilesZip")` for the cross-field rule. Note the `OverridePropertyName` is required because FluentValidation's default name policy drops the parent on deep `req.Geofences!.Polygons` expressions. +- `SatelliteProvider.Api/Validators/RoutePointValidator.cs` (AZ-809, **new**) — per-point lat/lon range rules with `OverridePropertyName("lat"/"lon")` so error keys match the wire format. +- `SatelliteProvider.Api/Validators/GeofencePolygonValidator.cs` (AZ-809, **new**) — cross-field invariants `NW.Lat > SE.Lat` AND `NW.Lon < SE.Lon` for axis-aligned bounding boxes. +- `SatelliteProvider.Api/Validators/UavTileBatchMetadataPayloadValidator.cs` (AZ-810, **new**) — `items` count rules (non-null, non-empty, ≤ `UavQualityConfig.MaxBatchSize`) + `RuleForEach(p => p.Items).SetValidator(new UavTileMetadataValidator(...))`. +- `SatelliteProvider.Api/Validators/UavTileMetadataValidator.cs` (AZ-810, **new**) — per-item rules: `latitude` ∈ \[-90, 90\], `longitude` ∈ \[-180, 180\], `tileZoom` ∈ \[0, 22\], `tileSizeMeters` > 0, `capturedAt` within `\[now - MaxAgeDays, now + CapturedAtFutureSkewSeconds\]`. Uses an injectable `TimeProvider` (defaults to `TimeProvider.System`). +- `SatelliteProvider.Api/Validators/UavUploadValidationFilter.cs` (AZ-810, **new**) — `IEndpointFilter` for `POST /api/satellite/upload`. Reads the `metadata` form field, deserializes via the strict global `JsonSerializerOptions`, runs `IValidator` from DI, and enforces the envelope cross-field rule `items.Count == files.Count`. Error-map keys prefixed with `metadata.` so paths surface to the caller as `errors["metadata.items[0].latitude"]`. +- `SatelliteProvider.Api/Validators/GetTileByLatLonQueryValidator.cs` (AZ-811, **new**) — `AbstractValidator` with `lat`/`lon`/`zoom` rules using `Cascade(CascadeMode.Stop) → NotNull → InclusiveBetween` so missing-param surfaces only as `"\`\` is required."`. +- `SatelliteProvider.Api/Validators/RejectUnknownQueryParamsEndpointFilter.cs` (AZ-811, **new**) — generic envelope filter parameterized by an allowed-keys set; rejects unknown query-string parameters with RFC 7807 `ValidationProblemDetails`. **New shared infrastructure** designed for reuse by any future query-param endpoint (AZ-811 AC-9). +- `SatelliteProvider.Api/DTOs/GetTileByLatLonQuery.cs` (AZ-811, **new**) — `record GetTileByLatLonQuery(double? Lat, double? Lon, int? Zoom)` with `[FromQuery(Name="lat"|"lon"|"zoom")]` on each property. Bound via `[AsParameters]` on the handler. Nullable on purpose — see api_program.md commentary. +- `_docs/02_document/contracts/api/region-request.md` (AZ-808 + AZ-812, **new**) — v1.0.0 wire-format contract for `POST /api/satellite/request`. Published directly with `lat`/`lon` per AZ-812 AC-6 coordination (no v2.0.0 bump needed since AZ-808 + AZ-812 shipped same-cycle). +- `_docs/02_document/contracts/api/route-creation.md` (AZ-809, **new**) — v1.0.0 wire-format contract for `POST /api/satellite/route` covering all 14 validation rules, the nested per-point / per-polygon structure, and the `createTilesZip ⇒ requestMaps` cross-field invariant. Carries advisory notes for AZ-809 AC-9 (`sizeMeters` vs `regionSizeMeters` naming inconsistency) and AC-10 (input/output point-shape asymmetry). +- `_docs/02_document/contracts/api/tile-latlon.md` (AZ-811, **new**) — v1.0.0 wire-format contract for `GET /api/satellite/tiles/latlon` covering the 5 validation rules + the novel unknown-query-param rejection. Documents the rename `?Latitude=&Longitude=&ZoomLevel=` → `?lat=&lon=&zoom=`. +- `_docs/02_document/contracts/api/uav-tile-upload.md` (AZ-810, modified) — bumped to v1.2.0; new "Validation Rules" section covering the three-layer enforcement (deserializer, FluentValidation, envelope cross-field) and the `errors["metadata.…"]` key convention. Change Log entry names AZ-810. +- `_docs/02_document/modules/api_program.md` (AZ-808/809/810/811/812, modified) — endpoint table entries for the 4 endpoints bumped to credit cycle 8 and reference the new contract docs. New `Api/Validators` section row for each cycle-8 validator. New `Api/DTOs` section for `GetTileByLatLonQuery`. DI Registration item 14 lists every cycle-8 validator. New item documenting the `UavUploadValidationFilter` AddTransient + AddEndpointFilter wiring. +- `_docs/02_document/modules/common_dtos.md` (AZ-808/809/810/812, modified) — `RequestRegionRequest` renamed + JsonPropertyName documented; `RoutePoint`, `CreateRouteRequest`, `GeofencePolygon`, `Geofences`, `GeoPoint`, `UavTileMetadata`, `UavTileBatchMetadataPayload` all carry `[JsonRequired]` annotations explained; input/output `lat`/`latitude` asymmetry on the route endpoint surfaced. +- `_docs/02_document/system-flows.md` (AZ-808/809/811, modified) — F1 (single-tile download) updated to reference `tile-latlon.md` + the unknown-query-param filter. F2 (region request) updated to reference `region-request.md` + the validator. F4 (route creation) updated to reference `route-creation.md` + the cross-field rules. +- `_docs/02_document/architecture.md` (cycle 8, modified by Step 13) — (a) the contracts inventory line bumped to mention `uav-tile-upload.md` v1.2.0 + the four new cycle-8 contracts (`region-request.md`, `route-creation.md`, `tile-latlon.md`, `error-shape.md`); (b) new architectural principle "Strict wire-format validation at the API edge (AZ-795 epic, completed across cycles 7-8)" describing the two-layer enforcement and the no-handler-without-validation rule. +- `SatelliteProvider.Tests/Validators/RegionRequestValidatorTests.cs` (AZ-808, **new**) — unit tests against each `RuleFor` chain. +- `SatelliteProvider.Tests/Validators/CreateRouteRequestValidatorTests.cs` + `RoutePointValidatorTests.cs` + `GeofencePolygonValidatorTests.cs` (AZ-809, **new**) — ≥ 13 unit-test methods across the three validators. +- `SatelliteProvider.Tests/Validators/UavTileMetadataValidatorTests.cs` + `UavTileBatchMetadataPayloadValidatorTests.cs` (AZ-810, **new**) — ≥ 11 unit-test methods covering each rule (incl. `TimeProvider` injection for freshness). +- `SatelliteProvider.Tests/Validators/GetTileByLatLonQueryValidatorTests.cs` (AZ-811, **new**) — ≥ 3 unit-test methods. +- `SatelliteProvider.IntegrationTests/RegionRequestValidationTests.cs` + `CreateRouteValidationTests.cs` + `UavUploadValidationTests.cs` + `GetTileByLatLonValidationTests.cs` (AZ-808/809/810/811, **new**) — ≥ 45 failure methods + 4 happy paths total; every test uses `ProblemDetailsAssertions` from AZ-795. +- `SatelliteProvider.IntegrationTests/UavUploadTests.cs` (AZ-810 fallout, modified) — `NextTestCoordinate()` clamped to lat ∈ \[50, 70), lon ∈ \[10, 40) via modulo arithmetic. Pre-AZ-810 the seed `(Ticks/TicksPerSecond) % 1_000_000` produced lat > 90° which was silently accepted by the lenient pre-cycle-8 deserializer; the new AZ-810 validator (correctly) rejects it. This is the test-data bug that exposed the AC-9 false-PASS (see `_docs/LESSONS.md` 2026-05-23 entry). +- `SatelliteProvider.IntegrationTests/UavUploadValidationTests.cs` (AZ-810, modified — same fix) — `NextTestCoordinate()` clamped to lat ∈ \[-70, -50), lon ∈ \[-40, -10) (non-overlapping with `UavUploadTests` to avoid per-source UNIQUE-index collisions when both suites run against the same DB). +- `scripts/probe_region_validation.sh` + `probe_route_validation.sh` + `probe_upload_validation.sh` + `probe_latlon_validation.sh` (AZ-808/809/810/811, **new**) — manual probe scripts modelled on `probe_inventory_validation.sh`. + +## Importer scan results + +| Symbol | Importer count | Importer files | Component touched | +|--------|----------------|----------------|-------------------| +| `RequestRegionRequest.Lat` / `.Lon` (renamed properties; wire names unchanged at `lat`/`lon`) | 4 | `Program.cs` (request mapping), `RegionService.cs` (handler — uses `.Lat`/`.Lon` to build the queue message), `RegionRequestTests.cs`, `RegionRequestValidationTests.cs` | WebApi, RegionProcessing service, Tests | +| `[JsonRequired]` on every cycle-8 DTO axis | n/a | enforced at runtime by `System.Text.Json` + caught by `GlobalExceptionHandler` (no compile-time consumer beyond the deserializer) | WebApi (deserializer + handler) | +| `RegionRequestValidator` | 3 | `Program.cs` (assembly-scan registration), `RegionRequestValidatorTests.cs`, `RegionRequestValidationTests.cs` (indirect via running API) | WebApi (production), Tests (unit + integration) | +| `CreateRouteRequestValidator` + `RoutePointValidator` + `GeofencePolygonValidator` | 4 | `Program.cs`, 3 unit-test files, `CreateRouteValidationTests.cs` (indirect) | WebApi (production), Tests (unit + integration) | +| `UavTileBatchMetadataPayloadValidator` + `UavTileMetadataValidator` | 3 | `Program.cs`, 2 unit-test files, `UavUploadValidationTests.cs` (indirect) | WebApi (production), Tests (unit + integration) | +| `GetTileByLatLonQueryValidator` | 3 | `Program.cs`, `GetTileByLatLonQueryValidatorTests.cs`, `GetTileByLatLonValidationTests.cs` (indirect) | WebApi (production), Tests (unit + integration) | +| `UavUploadValidationFilter` | 2 | `Program.cs` (DI + endpoint filter wiring), `UavUploadValidationTests.cs` (indirect) | WebApi | +| `RejectUnknownQueryParamsEndpointFilter` | 1 (current) + N-future | `Program.cs` (`MapGet("/api/satellite/tiles/latlon", …).AddEndpointFilter(new RejectUnknownQueryParamsEndpointFilter(...))`); designed to be reused by every future query-param endpoint per AZ-811 AC-9 | WebApi | +| `GetTileByLatLonQuery` | 2 | `Program.cs` (handler signature `[AsParameters] GetTileByLatLonQuery`), `GetTileByLatLonQueryValidatorTests.cs` | WebApi (production), Tests (unit) | +| `RoutePoint.Latitude/Longitude` + `[JsonRequired]` | 4 | `RouteService.cs` (handler), `RouteCreationTests.cs`, `CreateRouteValidationTests.cs`, `RoutePointValidatorTests.cs` | WebApi, RouteManagement service, Tests | +| `CreateRouteRequest.*` + `[JsonRequired]` | 4 | `Program.cs`, `RouteService.cs`, `RouteCreationTests.cs`, `CreateRouteValidationTests.cs` | WebApi, RouteManagement service, Tests | +| `GeofencePolygon`/`Geofences`/`GeoPoint` + `[JsonRequired]` | 5 | `CreateRouteRequest.cs`, `RouteService.cs` (point-in-polygon geofence filtering), `GeofencePolygonValidatorTests.cs`, `CreateRouteValidationTests.cs`, `Json` deserializer | WebApi, RouteManagement service, Tests | + +## Doc refresh decisions + +All importers land inside components that either received targeted updates during Step 10 (Implement) or were verified-clean during this Step 13: + +- **WebApi (`Program.cs`)** — `_docs/02_document/modules/api_program.md` updated during the implementation phase with: (a) endpoint table entries for the 4 cycle-8 endpoints crediting their respective tasks + contract docs, (b) new `Api/Validators` section rows for every cycle-8 validator + envelope filter, (c) new `Api/DTOs` section for `GetTileByLatLonQuery`, (d) DI Registration item 14 listing every cycle-8 validator, (e) DI Registration entry for the `UavUploadValidationFilter` AddTransient + AddEndpointFilter wiring. Verified during Step 13 — no further changes needed. +- **Common (DTOs)** — `_docs/02_document/modules/common_dtos.md` updated with every modified DTO carrying its `[JsonRequired]` annotations explained, the AZ-812 `Lat/Lon` rename + JsonPropertyName attributes, and the route-endpoint input/output naming asymmetry caveat. Verified during Step 13 — no further changes needed. +- **RegionProcessing (`RegionService.cs`)** — no module doc update needed; the handler's behavior is unchanged (it still reads `.Lat`/`.Lon` from the request DTO to build the queue message — only the property names changed, not the values or the queue contract). The internal `RegionRequest` queue message remains on `Latitude`/`Longitude` per design (intentionally kept, see `common_dtos.md` line 43 commentary). +- **RouteManagement (`RouteService.cs`)** — no module doc update needed; the handler's behavior is unchanged. The `[JsonRequired]` annotations only affect the deserializer layer — once a payload passes, the handler sees the same shape it always did. +- **WebApi (`GlobalExceptionHandler.cs`)** — unchanged from cycle 7. The handler is now exercised by 4 more endpoints' deserializer-layer failures (missing `[JsonRequired]` axes, unknown fields, type mismatches) but the implementation is identical. +- **TileDownloader / DataAccess / DataAccess migrations** — not touched by cycle 8. +- **Architecture** — `architecture.md` updated during Step 13 with: (a) contracts inventory line bumped to mention `uav-tile-upload.md` v1.2.0 + the four new cycle-8 contracts, (b) new "Strict wire-format validation at the API edge" architectural principle describing the two-layer enforcement and the no-handler-without-validation rule. +- **System flows** — `system-flows.md` F1/F2/F4 updated during the implementation phase to credit cycle 8 and reference the new contract docs + error-shape contract. F6 (status query) + F8 (tile inventory bulk lookup) untouched — cycle 8 didn't change those endpoints. +- **Tests (unit + integration)** — `_docs/02_document/modules/tests_unit.md` and `tests_integration.md` are not strictly required to enumerate every new test file (cycle 7 didn't extend them past a "AZ-795 + AZ-796 — strict inventory validation" subsection). Cycle 8 keeps the same convention — the new test files are documented in the traceability matrix + test-spec sync (BT-28..BT-31) rather than re-listed per file in the module docs. +- **Tests (blackbox + traceability)** — `tests/blackbox-tests.md` and `tests/traceability-matrix.md` updated during Step 12 (Test-Spec Sync): BT-28..BT-31 added + 41 AC rows added (AZ-808 AC-1..AC-8 + AZ-809 AC-1..AC-10 + AZ-810 AC-1..AC-9 + AZ-811 AC-1..AC-9 + AZ-812 AC-1..AC-6) + Coverage Summary refresh. + +## No-ripple components + +These components were NOT touched by cycle-8 changes and require no doc update: + +- **DataAccess** — no schema, repository signature, or migration changes in cycle 8. The validation work is entirely at the API edge. +- **TileDownloader** — not touched. The four cycle-8 endpoints either don't trigger tile downloads at all (`POST /api/satellite/request`, `POST /api/satellite/route`) or trigger them only after the validator has passed (`GET /api/satellite/tiles/latlon`, `POST /api/satellite/upload`). +- **RegionProcessing background service** — not touched. The validator runs at the API edge before the request is enqueued. +- **RouteManagement processing** — not touched for the same reason. + +## Parse-failure / heuristic notes + +None — every symbol resolved via direct `Grep --type cs`. No fallback heuristic was needed. The cycle 8 surface is wider than cycle 7 (5 tasks vs 3) but still narrow architecturally — every change is a WebApi-layer concern + DTO annotations + per-endpoint test files. The shared infrastructure landed in cycle 7 (AZ-795); cycle 8 is the per-endpoint rollout. + +## AZ-795 epic posture + +Cycle 8 completes the per-endpoint rollout of the AZ-795 strict-validation epic. After this cycle, **every public-facing JSON, multipart, and query-param endpoint** in the satellite-provider workspace goes through one of the three approved validation paths: + +1. **JSON-body endpoints** — `WithValidation()` + `ValidationEndpointFilter` + `JsonSerializerOptions.UnmappedMemberHandling.Disallow`. Used by `POST /api/satellite/tiles/inventory` (AZ-796 cycle 7), `POST /api/satellite/request` (AZ-808 cycle 8), `POST /api/satellite/route` (AZ-809 cycle 8). +2. **Multipart endpoints** — bespoke `UavUploadValidationFilter` composing deserializer + FluentValidation + envelope cross-field. Used by `POST /api/satellite/upload` (AZ-810 cycle 8). +3. **Query-param endpoints** — `RejectUnknownQueryParamsEndpointFilter` + `WithValidation()`. Used by `GET /api/satellite/tiles/latlon` (AZ-811 cycle 8). + +The `architecture.md` § "Strict wire-format validation at the API edge" principle (added this Step 13) codifies this as a rule for future endpoints — there is no fourth approved path. The previously-open AZ-795 epic now has zero outstanding child tickets in this workspace; future endpoints will pick one of the three paths above and reuse the existing infrastructure without new shared-infra work. + +The endpoints NOT validated by the AZ-795 stack are: `GET /api/satellite/region/{id}` (path-only, framework-handled Guid binding — covered by the strict path binder), `GET /api/satellite/route/{id}` (same), `GET /api/satellite/tiles/mgrs` (stub returning empty — no input to validate), `GET /tiles/{z}/{x}/{y}` (path-only, framework-handled int binding — the strict path binder rejects malformed values; whether to range-check `z`/`x`/`y` against slippy-map bounds is a separate decision deferred to parent-suite team per AZ-811 Out of Scope). These exemptions are documented in `api_program.md` so future contributors know they're intentional, not omissions. diff --git a/_docs/_autodev_state.md b/_docs/_autodev_state.md index 3aa089a..be53b09 100644 --- a/_docs/_autodev_state.md +++ b/_docs/_autodev_state.md @@ -2,12 +2,12 @@ ## Current Step flow: existing-code -step: 13 -name: Update Docs +step: 14 +name: Security Audit status: not_started sub_step: phase: 0 - name: awaiting-invocation + name: awaiting-choice detail: "" retry_count: 0 cycle: 8