[AZ-808] [AZ-809] [AZ-810] [AZ-811] [AZ-812] Cycle 8 test-spec sync

Phase 12 of autodev existing-code flow — cycle-update mode of the
test-spec skill. Append cycle-8 coverage to the documentation suite
without rewriting any pre-cycle-8 content.

blackbox-tests.md
- Add 4 new BT entries (BT-28..BT-31) — one per cycle-8 endpoint:
  - BT-28: Region request endpoint strict validation + OSM rename
    (AZ-808 + AZ-812; 11 sub-cases through the new `RegionRequest
    Validator` + the AZ-795 deserializer infra; sub-case `pos` proves
    the new `lat`/`lon` names accepted, sub-case `9` proves the old
    `latitude`/`longitude` rejected by `UnmappedMemberHandling.Disallow`).
  - BT-29: Create route endpoint nested + cross-field validation
    (AZ-809; 15 sub-cases covering nested per-point validators,
    geofence cross-field invariants, and the `createTilesZip` /
    `requestMaps` cross-field rule; advisory ACs 9 + 10 explicitly
    NOT tested per spec).
  - BT-30: UAV upload metadata multipart validation (AZ-810; 14
    sub-cases across the three-layer composition: deserializer,
    FluentValidation, envelope cross-field; documents the unique
    `errors["metadata"]` vs `errors["metadata.items[i].field"]` key
    convention for multipart endpoints).
  - BT-31: GET tiles/latlon query-param validation + unknown-param
    rejection (AZ-811; 8 sub-cases; sub-cases 4b + 4c prove the
    novel `UnknownQueryParameterEndpointFilter` rejects both
    legacy and hostile unknown query keys).

traceability-matrix.md
- Append 41 AC rows (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).
- Update Coverage Summary: cycle-8 row added; Total moves from
  126 tests / 75 ACs to 167 tests / 116 ACs.
- Add "Coverage shape notes (Cycle 8 ...)" section explaining the
  multipart enforcement shape (AZ-810), the new query-param filter
  (AZ-811), the AZ-808 + AZ-812 same-cycle coordination, and the
  AZ-810 AC-9 process annotation (false-PASS by source tracing →
  bound to green full-suite re-run after the test-data coord-clamp
  fix in commit b763da3).
- AZ-809 AC-9 + AC-10 marked as `◐ advisory (not tested)` —
  naming-consistency concerns surfaced for parent-suite team
  decision.

State
- Advance autodev to Step 13 (Update Docs), sub_step phase 0
  awaiting-invocation.

No production code change; no contract change; no test code change.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-23 14:28:17 +03:00
parent b763da3f24
commit ec0eb909a1
3 changed files with 169 additions and 8 deletions
+54 -1
View File
@@ -121,6 +121,48 @@
| AZ-796 AC-5 | `/swagger/v1/swagger.json` marks required fields, declares integer ranges per validation rules, declares 400 response with ProblemDetails schema | Doc-state AC — verified at Step 13 (Update Docs) review against the published OpenAPI document; integration smoke is the existing `JwtIntegrationTests.SwaggerDocument_AdvertisesBearerSecurityScheme` pattern (a future analogous test against the validation schema is out-of-scope this cycle) | ◐ doc-verified at Step 13 |
| AZ-796 AC-6 | `_docs/02_document/contracts/api/tile-inventory.md` updated to document the 9 validation rules + error contract reference | Doc-state AC — `_docs/02_document/contracts/api/tile-inventory.md` v2.0.0 Change Log entry naming AZ-796 (verified at Step 13 Update Docs review) | ✓ |
| AZ-796 AC-7 | `scripts/probe_inventory_validation.sh` committed; exercises each failure mode via `curl` + JWT for documentation / regression | Structural: `scripts/probe_inventory_validation.sh` exists in repo and is manually runnable | ✓ |
| AZ-808 AC-1 | Each of the 8 region-request validations rejects with HTTP 400 + ValidationProblemDetails (single-rule precision) | BT-28 sub-cases 1, 2a, 2b, 3, 4, 5, 6, 7, 8, 9, 10 (blackbox); `RegionRequestValidationTests` (integration, 11+ failure methods) + `RegionRequestValidatorTests` (unit, ≥ 8 methods covering each `RuleFor`); shared `ProblemDetailsAssertions` enforces error-shape v1.0.0 conformance | ✓ |
| AZ-808 AC-2 | Happy path unchanged — valid body returns HTTP 200 + `RegionStatusResponse`; background processing still runs; probe's 9-tile Derkachi case completes < 10 s | BT-28 sub-case `pos` (`RegionRequestValidationTests.HappyPath_Returns200`); no regression in existing `RegionRequestTests.cs` (cycle 8 Step 11 — green) | ✓ |
| AZ-808 AC-3 | `RegionRequestValidator` in its own file under `SatelliteProvider.Api/Validators/`; unit-tested (≥ 1 per `RuleFor`) | Structural: `SatelliteProvider.Api/Validators/RegionRequestValidator.cs` exists; `SatelliteProvider.Tests/Validators/RegionRequestValidatorTests.cs` covers each `RuleFor` chain | ✓ |
| AZ-808 AC-4 | `RegionRequestValidationTests.cs` covers happy + 8+ failure modes; MUST include `Post_WithMissingId_ReturnsBadRequest` reproducing 2026-05-22 silent-coercion case | `SatelliteProvider.IntegrationTests/RegionRequestValidationTests.cs` includes `MissingId_Returns400` (the renamed AZ-777 Phase 2 reproducer) and ≥ 11 total failure methods; uses `ProblemDetailsAssertions` from AZ-795 | ✓ |
| AZ-808 AC-5 | `_docs/02_document/contracts/api/region-request.md` v1.0.0 created and published | Doc-state AC — `region-request.md` v1.0.0 created in cycle-8 batch (coordinated with AZ-812 — published directly with `lat`/`lon` names per AZ-812 AC-6); verified at Step 13 (Update Docs) review | ✓ |
| AZ-808 AC-6 | `_docs/02_document/system-flows.md` F2 updated to reference the new contract doc + error shape | Doc-state AC — verified at Step 13 (Update Docs) review | ◐ doc-verified at Step 13 |
| AZ-808 AC-7 | OpenAPI marks `RequestRegionRequest` fields `required`, declares ranges, documents 400 response | Doc-state AC — verified at Step 13 (Update Docs) review against published `/swagger/v1/swagger.json`; Swashbuckle annotations match AZ-796 pattern | ◐ doc-verified at Step 13 |
| AZ-808 AC-8 | Manual probe script exercises each failure mode via `curl` + JWT | Structural: `scripts/probe_region_validation.sh` exists and is manually runnable | ✓ |
| AZ-809 AC-1 | Each of the 14 route-creation validations rejects with HTTP 400 + ValidationProblemDetails (single-rule precision) | BT-29 sub-cases 1..14 (blackbox); `CreateRouteValidationTests` (integration, 14+ failure methods covering deserializer + per-DTO + per-element + cross-field layers) + `CreateRouteRequestValidatorTests` + `RoutePointValidatorTests` + `GeofencePolygonValidatorTests` (unit, ≥ 13 methods total) | ✓ |
| AZ-809 AC-2 | Happy path unchanged — valid body returns HTTP 200 + `RouteResponse`; F5 background still runs when `requestMaps=true`; probe's 2-point 132 m route completes < 20 s | BT-29 sub-case `pos` (`CreateRouteValidationTests.HappyPath_Returns200`); no regression in existing `RouteCreationTests.cs` (cycle 8 Step 11 — green) | ✓ |
| AZ-809 AC-3 | `CreateRouteRequestValidator`, `RoutePointValidator`, `GeofencePolygonValidator` each in their own files under `SatelliteProvider.Api/Validators/`; ≥ 13 unit-test methods total | Structural: three validator files exist as separate `.cs` files; unit-test files together contain ≥ 13 methods covering every `RuleFor` / `RuleForEach` chain | ✓ |
| AZ-809 AC-4 | `CreateRouteValidationTests.cs` covers happy + 13+ failure modes; MUST include `Post_WithMissingId_ReturnsBadRequest` | `SatelliteProvider.IntegrationTests/CreateRouteValidationTests.cs` includes `MissingId_Returns400` (the AZ-777 Phase 2 reproducer for route variant) and ≥ 14 total failure methods | ✓ |
| AZ-809 AC-5 | `_docs/02_document/contracts/api/route-creation.md` v1.0.0 created and published | Doc-state AC — `route-creation.md` v1.0.0 created in cycle-8 batch; verified at Step 13 review | ✓ |
| AZ-809 AC-6 | `_docs/02_document/system-flows.md` F4 + F5 updated to reference new contract doc + error shape | Doc-state AC — verified at Step 13 (Update Docs) review | ◐ doc-verified at Step 13 |
| AZ-809 AC-7 | OpenAPI marks all required fields at every nesting level, declares ranges, documents 400 response | Doc-state AC — verified at Step 13 against published `/swagger/v1/swagger.json` | ◐ doc-verified at Step 13 |
| AZ-809 AC-8 | Manual probe script exercises each failure mode via `curl` + JWT | Structural: `scripts/probe_route_validation.sh` exists and is manually runnable | ✓ |
| AZ-809 AC-9 | (Advisory) `RequestRegionRequest.sizeMeters` vs `CreateRouteRequest.regionSizeMeters` naming inconsistency surfaced for parent-suite decision | Not tested — surfaced in `_docs/03_implementation/batch_*_cycle8_report.md` for parent-suite team follow-up | ◐ advisory (not tested) |
| AZ-809 AC-10 | (Advisory) Input `points: [{lat, lon}]` vs output `points: [{latitude, longitude}]` round-trip asymmetry surfaced for parent-suite decision | Not tested — surfaced in `_docs/03_implementation/batch_*_cycle8_report.md` for parent-suite team follow-up | ◐ advisory (not tested) |
| AZ-810 AC-1 | Each of the 14 upload-metadata validations rejects with HTTP 400 + ValidationProblemDetails (single-rule precision) | BT-30 sub-cases 1..13 (blackbox); `UavUploadValidationTests` (integration, ≥ 13 failure methods covering deserializer + FluentValidation + envelope cross-field layers) + `UavTileMetadataValidatorTests` + `UavTileBatchMetadataPayloadValidatorTests` (unit, ≥ 11 methods total) | ✓ |
| AZ-810 AC-2 | Happy path unchanged — valid envelope returns HTTP 200 + per-item result list; per-item file rejections (`IUavTileQualityGate`) still return HTTP 200 with per-item status | BT-30 sub-case `pos` (`UavUploadValidationTests.HappyPath_Returns200`); existing AZ-488 BT-13..BT-17 + `UavUploadTests` continue green (cycle 8 Step 11 after the AZ-810 test-data coord-clamp fix in commit `b763da3`) | ✓ |
| AZ-810 AC-3 | `UavTileMetadataValidator` + `UavTileBatchMetadataPayloadValidator` each in their own files under `SatelliteProvider.Api/Validators/`; ≥ 11 unit-test methods total | Structural: two validator files plus `UavUploadValidationFilter.cs` (the multipart envelope filter) exist under `SatelliteProvider.Api/Validators/`; unit-test files contain ≥ 11 methods covering each `RuleFor` | ✓ |
| AZ-810 AC-4 | `UavUploadValidationTests.cs` covers happy + 12+ failure modes with full ValidationProblemDetails assertion | `SatelliteProvider.IntegrationTests/UavUploadValidationTests.cs` contains ≥ 13 integration test methods spanning all three enforcement layers; uses `ProblemDetailsAssertions` | ✓ |
| AZ-810 AC-5 | `_docs/02_document/contracts/api/uav-tile-upload.md` bumped to v1.2.0 with the new validation section | Doc-state AC — `uav-tile-upload.md` v1.2.0 Change Log entry naming AZ-810; verified at Step 13 review | ✓ |
| AZ-810 AC-6 | `_docs/02_document/modules/api_program.md` documents the new multipart validation endpoint filter | Doc-state AC — `api_program.md` updated in cycle-8 batch; verified at Step 13 review | ✓ |
| AZ-810 AC-7 | OpenAPI marks `UavTileBatchMetadataPayload` + `UavTileMetadata` fields `required`, declares ranges, documents 400 response | Doc-state AC — verified at Step 13 against published `/swagger/v1/swagger.json`; `[JsonRequired]` annotations propagate to Swashbuckle as `required: [latitude, longitude, tileZoom, tileSizeMeters, capturedAt]` and `required: [items]` | ◐ doc-verified at Step 13 |
| AZ-810 AC-8 | Manual probe script exercises each failure mode via multipart `curl` + JWT | Structural: `scripts/probe_upload_validation.sh` exists, reuses the AZ-808/AZ-809/AZ-811 probe-script pattern, and is manually runnable | ✓ |
| AZ-810 AC-9 | No regression in existing AZ-488 integration tests (`UavTileBatchUploadTests.cs`, `UavTileQualityGateTests.cs`) | Cycle 8 Step 11 full integration run green AFTER fixing a pre-existing latent bug in `UavUploadTests.NextTestCoordinate` that AZ-810 exposed (seed `(Ticks/TicksPerSecond) % 1_000_000` produced lat > 90°; clamped to lat ∈ [50, 70), lon ∈ [10, 40) in commit `b763da3`). The original AC-9 verification (cycle 8 batch_04 report — "verified by tracing source") was a false-PASS; the green re-run is the binding evidence. Lesson recorded in `_docs/LESSONS.md` (2026-05-23) | ✓ (verified by full-suite re-run) |
| AZ-811 AC-1 | Each of the 5 GET-lat/lon validations rejects with HTTP 400 + ValidationProblemDetails | BT-31 sub-cases 1..5 + 4a/4b/4c (blackbox); `GetTileByLatLonValidationTests` (integration, ≥ 7 failure methods) + `GetTileByLatLonQueryValidatorTests` (unit, ≥ 3 methods) | ✓ |
| AZ-811 AC-2 | Happy path unchanged — `?lat=&lon=&zoom=` returns HTTP 200 + `DownloadTileResponse`; tile still downloaded/persisted | BT-31 sub-case `pos` (`GetTileByLatLonValidationTests.HappyPath_Returns200`); no regression in existing `TileByLatLonTests.cs` (cycle 8 Step 11 — green) | ✓ |
| AZ-811 AC-3 | `GetTileByLatLonQueryValidator` in its own file under `SatelliteProvider.Api/Validators/`; unit-tested (≥ 3 methods) | Structural: `GetTileByLatLonQueryValidator.cs` exists; unit-test file covers the 5 rules in ≥ 3 methods | ✓ |
| AZ-811 AC-4 | `GetTileByLatLonValidationTests.cs` covers happy + 4+ failure modes | `SatelliteProvider.IntegrationTests/GetTileByLatLonValidationTests.cs` contains ≥ 7 failure methods + 1 happy path; uses `ProblemDetailsAssertions` | ✓ |
| AZ-811 AC-5 | `_docs/02_document/contracts/api/tile-latlon.md` v1.0.0 created and published | Doc-state AC — `tile-latlon.md` v1.0.0 created in cycle-8 batch; verified at Step 13 review | ✓ |
| AZ-811 AC-6 | `_docs/02_document/modules/api_program.md::GetTileByLatLon Handler` updated to reference the validator + new contract doc | Doc-state AC — `api_program.md` updated in cycle-8 batch; verified at Step 13 review | ✓ |
| AZ-811 AC-7 | OpenAPI marks query params required + ranges + 400 response | Doc-state AC — verified at Step 13 against published `/swagger/v1/swagger.json` | ◐ doc-verified at Step 13 |
| AZ-811 AC-8 | Manual probe script exercises each failure mode via `curl` + JWT | Structural: `scripts/probe_latlon_validation.sh` exists and is manually runnable | ✓ |
| AZ-811 AC-9 | The novel `UnknownQueryParameterEndpointFilter` (rule 4 — unknown-query-param rejection) is documented in `_docs/02_document/modules/api_program.md` so the next query-param endpoint can reuse it | Doc-state AC — the filter's behavior + reuse contract documented in `api_program.md`; verified at Step 13 review. BT-31 sub-cases `4b` (legacy `?Latitude=&Longitude=&ZoomLevel=` rejected) and `4c` (hostile `?debug=1&admin=true` rejected) prove the filter works as documented | ✓ |
| AZ-812 AC-1 | `RequestRegionRequest` DTO uses `Lat` / `Lon` (C#) + `[JsonPropertyName("lat")]` / `[JsonPropertyName("lon")]` | Structural: `SatelliteProvider.Common/DTO/RequestRegionRequest.cs` diff shows the rename + JsonPropertyName attributes; sibling DTOs `RoutePoint` and `GeoPoint` already used `lat`/`lon` (no change there) | ✓ |
| AZ-812 AC-2 | Wire format is `{"lat":..,"lon":..}` end-to-end (request body, OpenAPI schema, docs, all integration tests) | BT-28 sub-case `pos` exercises the post-rename wire shape; integration test `RegionRequestValidationTests` + `RegionRequestTests` use `lat`/`lon` in every body; `region-request.md` v1.0.0 ships with `lat`/`lon` from day one (AZ-812 AC-6 coordination with AZ-808); OpenAPI verified at Step 13 | ✓ |
| AZ-812 AC-3 | `RegionTests.cs` happy-path tests pass against new wire format | Cycle 8 Step 11 full run — green; all `RegionRequestTests` updated to send `lat`/`lon` in the same commit as the DTO rename | ✓ |
| AZ-812 AC-4 | `curl` probe with `{"id":"<guid>","lat":49.94,"lon":36.31,"sizeMeters":200,"zoomLevel":18,"stitchTiles":false}` returns HTTP 200 + valid `regionId`; old `{"latitude":..,"longitude":..}` returns HTTP 400 with `UnmappedMemberHandling.Disallow` rejecting the unknown fields | BT-28 sub-case `pos` (new names accepted) + sub-case `9` (`OldLatLongNames_Returns400` — old `latitude`/`longitude` rejected as unknown). The strict-deserializer behavior is what AZ-795's `UnmappedMemberHandling.Disallow` makes possible; pre-cycle-8 the rename would have silently coerced old names to `Lat=0, Lon=0` | ✓ |
| AZ-812 AC-5 | Docs updated: `common_dtos.md`, `api_program.md`, `system-flows.md` (F2) | Doc-state AC — all three files updated in cycle-8 batch; verified at Step 13 review | ◐ doc-verified at Step 13 |
| AZ-812 AC-6 | Contract doc coordination: `region-request.md` v1.0.0 published directly with `lat`/`lon` (because AZ-808 + AZ-812 shipped in same cycle) — no `v1.0.0 → v2.0.0` bump needed | Doc-state AC — `region-request.md` v1.0.0 Change Log section names both AZ-808 (validation rules) and AZ-812 (`lat`/`lon` field names); verified at Step 13 review | ✓ |
## Restrictions → Test Mapping
@@ -171,7 +213,8 @@
| Cycle 5 — AZ-504 perf-script fix (shell harness + Step-15 gate) | 1 standalone shell harness (4 cases) | 2/4 verified now (AC-1, AC-2); 2/4 gated at Step 15 (AC-3, AC-4) | — |
| Cycle 6 — AZ-505 inventory + HTTP/2 + leaflet covering index (integration + blackbox + perf) | 3 integration files + 4 blackbox (BT-23..BT-26) + 1 perf (PT-09) | 7/7 (AC-1..AC-7; AC-7 is doc-only). Also resolves the 5 AZ-503 deferrals (AC-5, 6, 9, 10, 12). | — |
| Cycle 7 — AZ-794 + AZ-795 + AZ-796 strict inventory validation + z/x/y rename (integration + unit + blackbox + contract) | 1 integration file (`TileInventoryValidationTests`, 16 tests) + 1 unit file (`InventoryRequestValidatorTests`, 16 tests) + 1 blackbox (BT-27 with 16 sub-cases) + 1 new contract (`error-shape.md` v1.0.0) + 1 bumped contract (`tile-inventory.md` v2.0.0) | 12/12 in-scope (AZ-794 AC-1..AC-4, AZ-795 epic-level, AZ-796 AC-1..AC-7); 2 ACs (AZ-794 AC-3 + AZ-796 AC-5) are `◐ doc-verified at Step 13`. | — |
| **Total** | **126** | **75/75 in-scope (100%); 2 AZ-504 ACs gated at Step 15; 2 cycle-7 ACs doc-verified at Step 13** | **8/8 (100%)** |
| Cycle 8 — AZ-808 + AZ-809 + AZ-810 + AZ-811 + AZ-812 strict validation sweep + region OSM rename (integration + unit + blackbox + contracts) | 4 integration files (`RegionRequestValidationTests`, `CreateRouteValidationTests`, `UavUploadValidationTests`, `GetTileByLatLonValidationTests` — ≥ 45 failure methods + 4 happy paths) + 5 unit files (`RegionRequestValidatorTests`, `CreateRouteRequestValidatorTests`, `RoutePointValidatorTests`, `GeofencePolygonValidatorTests`, `UavTileMetadataValidatorTests`, `UavTileBatchMetadataPayloadValidatorTests`, `GetTileByLatLonQueryValidatorTests` — ≥ 35 methods across the 4 endpoints) + 4 blackbox (BT-28..BT-31 with ≥ 41 sub-cases) + 4 new contracts (`region-request.md` v1.0.0, `route-creation.md` v1.0.0, `tile-latlon.md` v1.0.0, `uav-tile-upload.md` v1.2.0 bump) + 4 probe scripts | 41/41 in-scope (AZ-808 AC-1..AC-8, AZ-809 AC-1..AC-8, AZ-810 AC-1..AC-9, AZ-811 AC-1..AC-9, AZ-812 AC-1..AC-6); 8 ACs are `◐ doc-verified at Step 13` (per-endpoint OpenAPI / system-flows updates) + 2 advisory non-tested (AZ-809 AC-9, AC-10 — naming consistency surfaced for parent-suite). AZ-810 AC-9 (no AZ-488 regression) verified after the AZ-810 test-data coord-clamp fix (commit `b763da3`) — the original "traced by source" verification was a false-PASS; the green full-suite re-run is the binding evidence. | — |
| **Total** | **167** | **116/116 in-scope (100%); 2 AZ-504 ACs gated at Step 15; 10 ACs doc-verified at Step 13 (2 cycle-7 + 8 cycle-8); 2 advisory non-tested (cycle-8 AZ-809 AC-9/AC-10)** | **8/8 (100%)** |
**Coverage shape notes (Cycle 5 — AZ-503 foundation):**
- AZ-503 was split mid-cycle (Option C, autodev Step 10 batch 2): 7 of 12 original ACs land here; 5 (AC-5, AC-6, AC-9, AC-10, AC-12) are deferred to AZ-505 with a `Blocks` link in Jira and an entry in `_docs/02_tasks/_dependencies_table.md`. The deferred rows above are marked `◐ deferred → AZ-505` so the matrix surfaces the scope boundary explicitly.
@@ -209,3 +252,13 @@
- AZ-505 AC-6's existing row (cycle 6 — "Request validation — 400 on both populated, 400 on neither, 400 on > 5000 entries, 401 on anonymous") remains accurate. Its 4 cases overlap with AZ-796 AC-1 sub-cases 2a, 2b, 4, and the anonymous case (also SEC-05). Both rows are kept per cycle-update rule 4 ("Preserve existing traceability IDs"); the duplication is by design — AZ-505 AC-6 was the cycle-6 contract (status-code-only), AZ-796 AC-1 is the cycle-7 contract (status code + ProblemDetails shape + field-path errors). The cycle-7 row is the binding one going forward; the cycle-6 row stays as historical record.
- Cycle-update rule check: no NFR conflicts. The 5000-entry cap is reaffirmed (matches AZ-505); the supported zoom range 0..22 is reaffirmed (matches `tile-inventory.md` Inv-7); the error shape contract is **new** (`error-shape.md` v1.0.0) — but no prior cycle declared a different error shape, so this is greenfield content, not a conflict.
- Step 10 artifact gap (cycle 7): no `implementation_report_*_cycle7.md` was produced in `_docs/03_implementation/`. The actual implementation evidence lives in commits `dceaddc` (cycle 7 task adoption) + `865dfdb` (cycle 7 Step 10 implementation), in the state file's `detail` field (which recorded the test-run outcome), and in the new test artifacts themselves (`InventoryRequestValidator.cs`, `InventoryRequestValidatorTests.cs`, `TileInventoryValidationTests.cs`, `ProblemDetailsAssertions.cs`, `error-shape.md` v1.0.0). This artifact gap is recorded here for cycle 7 retrospective follow-up — the matrix itself is unaffected because cycle-update mode's source-of-truth is the task specs in `_docs/02_tasks/done/`, not the implementation report.
**Coverage shape notes (Cycle 8 — AZ-808 + AZ-809 + AZ-810 + AZ-811 + AZ-812 strict validation sweep + region OSM rename):**
- Cycle 8 completes the AZ-795 epic's per-endpoint rollout — every public-facing endpoint now goes through the shared validation infra. AZ-795's `AZ-795 (epic)` row from cycle 7 remains `✓`; cycle 8 adds 4 endpoint-scoped per-AC rows (AZ-808, AZ-809, AZ-810, AZ-811) plus the AZ-812 region-rename rows that ride the AZ-795 `UnmappedMemberHandling.Disallow` infra to make the old field names fail-fast (mirroring cycle 7's AZ-794 / AZ-796 coupling).
- AZ-810 introduced a **new** validation enforcement shape — the `multipart/form-data` envelope — because `POST /api/satellite/upload` is the only endpoint that can't use the generic `ValidationEndpointFilter<T>`. The bespoke `UavUploadValidationFilter` composes three layers (deserializer, FluentValidation, envelope cross-field) with a different error-key convention (`errors["metadata"]` for deserializer-level failures vs `errors["metadata.items[i].field"]` for FluentValidation-layer failures). This is documented in BT-30 §Notes and `_docs/02_document/contracts/api/uav-tile-upload.md` v1.2.0 §Validation Rules so future multipart endpoints can reuse the pattern.
- AZ-811 introduced a **new** generic infra piece — `UnknownQueryParameterEndpointFilter` (rule 4 — the parallel of `UnmappedMemberHandling.Disallow` for query strings). Documented in `_docs/02_document/modules/api_program.md` per AZ-811 AC-9. The next query-param endpoint can reuse it without reinventing the unknown-key rejection logic.
- AZ-808 + AZ-812 shipped in the same cycle. The AZ-812 OSM rename (`Latitude/Longitude``Lat/Lon`) was coordinated with AZ-808's validator authoring so the validator was never written against the old names (per AZ-812 AC-6 coordination). `region-request.md` is published as v1.0.0 (not v1.0.0→v2.0.0 bump) with both AZ-808 (validation rules) and AZ-812 (`lat`/`lon` field names) in the Change Log.
- BT-N01 and BT-N02 (legacy negative scenarios for `GET /api/satellite/tiles/latlon` that loosely asserted "HTTP 4xx") are NOT rewritten — they remain as historical record. BT-31 sub-cases 1, 2, 3 supersede them with strict assertions (HTTP 400 + named `errors` key). Both rows are kept per cycle-update rule 4 ("Preserve existing traceability IDs"); the cycle-8 row is the binding one going forward.
- AZ-809 ACs 9 + 10 are **advisory** (surfaced for parent-suite team decision, not implemented or tested this cycle). Matrix marks them `◐ advisory (not tested)`. They're recorded so the next cycle / parent-suite review sees them without having to re-discover them from the task spec. AC-9: `RequestRegionRequest.sizeMeters` vs `CreateRouteRequest.regionSizeMeters` naming inconsistency. AC-10: input `points: [{lat, lon}]` vs output `points: [{latitude, longitude}]` round-trip asymmetry on the route endpoint. Either keep + document, or harmonize in a follow-up MAJOR contract bump for both — parent-suite team's call.
- AZ-810 AC-9 (no AZ-488 regression) has a **process annotation**: cycle 8's batch_04 report originally claimed AC-9 "verified by tracing each AZ-488 test payload's metadata shape against the new rules" without running the integration suite. That verification was a false-PASS — the suite was actually red on the AZ-488 happy path because `UavUploadTests.NextTestCoordinate()` produced lat > 90° (a pre-existing latent bug masked by the absence of any validator before AZ-810). The bug was fixed by clamping the test-data generator to OSM-valid ranges in commit `b763da3` and AC-9 is now bound to the green full-suite re-run, not to source tracing. Process lesson recorded in `_docs/LESSONS.md` (2026-05-23).
- Cycle-update rule check: no NFR conflicts. Range bounds (`lat ∈ [-90, 90]`, `lon ∈ [-180, 180]`, `zoom ∈ [0, 22]`, `tileSizeMeters > 0`) are reaffirmed across all 4 endpoints — they were never previously contested. The error-shape contract (`error-shape.md` v1.0.0 from cycle 7) is reused unchanged.