Files
satellite-provider/_docs/02_document/ripple_log_cycle8.md
T
Oleksandr Bezdieniezhnykh 6207ab7c27 [AZ-808] [AZ-809] [AZ-810] [AZ-811] [AZ-812] Cycle 8 docs sync
Phase 13 of autodev existing-code flow — document skill in task
mode. Targeted updates to system-level docs that the per-batch
implementation commits did not already cover. Per-module docs
(api_program.md, common_dtos.md, system-flows.md F1/F2/F4) and
the 4 new contract docs (region-request.md, route-creation.md,
tile-latlon.md, uav-tile-upload.md v1.2.0) were already updated
during Step 10 batch commits and were verified-clean here.

architecture.md
- Bump contracts inventory line to mention uav-tile-upload.md v1.2.0
  (was v1.1.0) and add the four cycle-8 contracts (region-request,
  route-creation, tile-latlon, error-shape) so the contract index
  in architecture.md is no longer stale relative to the implemented
  endpoints.
- Add new architectural principle "Strict wire-format validation
  at the API edge (AZ-795 epic, completed across cycles 7-8)" to
  the Architectural Principles list. Describes the two-layer
  enforcement (deserializer + FluentValidation), the three approved
  per-endpoint paths (WithValidation<T> for JSON bodies,
  UavUploadValidationFilter for multipart, RejectUnknownQueryParams
  EndpointFilter + WithValidation<TQuery> for query strings), and
  the no-handler-without-validation rule.

ripple_log_cycle8.md
- New cycle-8 ripple log following the cycle-7 template. Documents
  every directly-changed source file, the importer scan results,
  doc refresh decisions, and the no-ripple component list.
- Records the AZ-795 epic posture: cycle 8 closes the per-endpoint
  rollout. Every public-facing JSON, multipart, and query-param
  endpoint now goes through one of the three approved paths. The
  exempt endpoints (GET region/{id}, GET route/{id}, GET tiles/mgrs
  stub, GET tiles/{z}/{x}/{y}) are listed with justification.

State
- Advance autodev to Step 14 (Security Audit), sub_step phase 0
  awaiting-choice.

No production code change; no test code change.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 14:32:57 +03:00

21 KiB

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/LongitudeLat/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<RequestRegionRequest>() on MapPost("/api/satellite/request", …) (AZ-808).
    • .WithValidation<CreateRouteRequest>() on MapPost("/api/satellite/route", …) (AZ-809).
    • .AddEndpointFilter<UavUploadValidationFilter>() on MapPost("/api/satellite/upload", …) (AZ-810) — bespoke multipart filter, not the generic WithValidation<T>().
    • .AddEndpointFilter(new RejectUnknownQueryParamsEndpointFilter("lat", "lon", "zoom")) + .WithValidation<GetTileByLatLonQuery>() on MapGet("/api/satellite/tiles/latlon", …) (AZ-811) — two-filter chain so unknown-key rejection precedes range checks.
    • AddTransient<UavUploadValidationFilter>() 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<RequestRegionRequest> 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<UavTileBatchMetadataPayload> 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<GetTileByLatLonQuery> 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.
  • Architecturearchitecture.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 flowssystem-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 endpointsWithValidation<T>() + ValidationEndpointFilter<T> + 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 endpointsRejectUnknownQueryParamsEndpointFilter + WithValidation<TQuery>(). 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.