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>
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 renamedLatitude/Longitude→Lat/Lonwith[JsonPropertyName("lat")]/[JsonPropertyName("lon")]. AZ-808 added[JsonRequired]toId+ the two coordinate axes +SizeMeters+ZoomLevel+StitchTilesso partial bodies are rejected at the deserializer layer.SatelliteProvider.Common/DTO/UavTileMetadata.cs(AZ-810, modified) —[JsonRequired]added toLatitude,Longitude,TileZoom,TileSizeMeters,CapturedAt.FlightIdintentionally NOT marked required (AZ-503 anonymous-flight semantics requirenullto be valid).UavTileBatchMetadataPayload.Itemsalso marked[JsonRequired].SatelliteProvider.Common/DTO/RoutePoint.cs(AZ-809, modified) —[JsonRequired]onLatitude+Longitude;[JsonPropertyName("lat")]/[JsonPropertyName("lon")]confirm the wire shape pre-rename (RoutePointwas already usinglat/lon, onlyRequestRegionRequestwas 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);DescriptionandGeofencesleft optional.SatelliteProvider.Common/DTO/GeofencePolygon.cs(AZ-809, modified) —[JsonRequired]onNorthWest+SouthEastso partial polygons fail at the deserializer.SatelliteProvider.Common/DTO/Geofences.cs(AZ-809, modified) —[JsonRequired]onPolygonssogeofences: {}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>()onMapPost("/api/satellite/request", …)(AZ-808)..WithValidation<CreateRouteRequest>()onMapPost("/api/satellite/route", …)(AZ-809)..AddEndpointFilter<UavUploadValidationFilter>()onMapPost("/api/satellite/upload", …)(AZ-810) — bespoke multipart filter, not the genericWithValidation<T>()..AddEndpointFilter(new RejectUnknownQueryParamsEndpointFilter("lat", "lon", "zoom"))+.WithValidation<GetTileByLatLonQuery>()onMapGet("/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
ParameterDescriptionFilterSwagger op filter trimmed the obsoleteLatitude/Longitude/ZoomLevelquery-param annotations (AZ-811 cleanup; lat/lon/zoom remain).
SatelliteProvider.Api/Validators/RegionRequestValidator.cs(AZ-808, new) —AbstractValidator<RequestRegionRequest>with rules for non-zeroid,lat/lonranges,sizeMeters∈ [100, 10000],zoomLevel∈ [0, 22].SatelliteProvider.Api/Validators/CreateRouteRequestValidator.cs(AZ-809, new) — root validator chainingRuleForEach(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, plusMust(req => !(req.CreateTilesZip && !req.RequestMaps)).WithName("createTilesZip")for the cross-field rule. Note theOverridePropertyNameis required because FluentValidation's default name policy drops the parent on deepreq.Geofences!.Polygonsexpressions.SatelliteProvider.Api/Validators/RoutePointValidator.cs(AZ-809, new) — per-point lat/lon range rules withOverridePropertyName("lat"/"lon")so error keys match the wire format.SatelliteProvider.Api/Validators/GeofencePolygonValidator.cs(AZ-809, new) — cross-field invariantsNW.Lat > SE.LatANDNW.Lon < SE.Lonfor axis-aligned bounding boxes.SatelliteProvider.Api/Validators/UavTileBatchMetadataPayloadValidator.cs(AZ-810, new) —itemscount 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,capturedAtwithin\[now - MaxAgeDays, now + CapturedAtFutureSkewSeconds\]. Uses an injectableTimeProvider(defaults toTimeProvider.System).SatelliteProvider.Api/Validators/UavUploadValidationFilter.cs(AZ-810, new) —IEndpointFilterforPOST /api/satellite/upload. Reads themetadataform field, deserializes via the strict globalJsonSerializerOptions, runsIValidator<UavTileBatchMetadataPayload>from DI, and enforces the envelope cross-field ruleitems.Count == files.Count. Error-map keys prefixed withmetadata.so paths surface to the caller aserrors["metadata.items[0].latitude"].SatelliteProvider.Api/Validators/GetTileByLatLonQueryValidator.cs(AZ-811, new) —AbstractValidator<GetTileByLatLonQuery>withlat/lon/zoomrules usingCascade(CascadeMode.Stop) → NotNull → InclusiveBetweenso 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 7807ValidationProblemDetails. 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 forPOST /api/satellite/request. Published directly withlat/lonper 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 forPOST /api/satellite/routecovering all 14 validation rules, the nested per-point / per-polygon structure, and thecreateTilesZip ⇒ requestMapscross-field invariant. Carries advisory notes for AZ-809 AC-9 (sizeMetersvsregionSizeMetersnaming 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 forGET /api/satellite/tiles/latloncovering 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 theerrors["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. NewApi/Validatorssection row for each cycle-8 validator. NewApi/DTOssection forGetTileByLatLonQuery. DI Registration item 14 lists every cycle-8 validator. New item documenting theUavUploadValidationFilterAddTransient + AddEndpointFilter wiring._docs/02_document/modules/common_dtos.md(AZ-808/809/810/812, modified) —RequestRegionRequestrenamed + JsonPropertyName documented;RoutePoint,CreateRouteRequest,GeofencePolygon,Geofences,GeoPoint,UavTileMetadata,UavTileBatchMetadataPayloadall carry[JsonRequired]annotations explained; input/outputlat/latitudeasymmetry on the route endpoint surfaced._docs/02_document/system-flows.md(AZ-808/809/811, modified) — F1 (single-tile download) updated to referencetile-latlon.md+ the unknown-query-param filter. F2 (region request) updated to referenceregion-request.md+ the validator. F4 (route creation) updated to referenceroute-creation.md+ the cross-field rules._docs/02_document/architecture.md(cycle 8, modified by Step 13) — (a) the contracts inventory line bumped to mentionuav-tile-upload.mdv1.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 eachRuleForchain.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.TimeProviderinjection 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 usesProblemDetailsAssertionsfrom 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_000produced 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.md2026-05-23 entry).SatelliteProvider.IntegrationTests/UavUploadValidationTests.cs(AZ-810, modified — same fix) —NextTestCoordinate()clamped to lat ∈ [-70, -50), lon ∈ [-40, -10) (non-overlapping withUavUploadTeststo 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 onprobe_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.mdupdated during the implementation phase with: (a) endpoint table entries for the 4 cycle-8 endpoints crediting their respective tasks + contract docs, (b) newApi/Validatorssection rows for every cycle-8 validator + envelope filter, (c) newApi/DTOssection forGetTileByLatLonQuery, (d) DI Registration item 14 listing every cycle-8 validator, (e) DI Registration entry for theUavUploadValidationFilterAddTransient + AddEndpointFilter wiring. Verified during Step 13 — no further changes needed. - Common (DTOs) —
_docs/02_document/modules/common_dtos.mdupdated with every modified DTO carrying its[JsonRequired]annotations explained, the AZ-812Lat/Lonrename + 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/.Lonfrom the request DTO to build the queue message — only the property names changed, not the values or the queue contract). The internalRegionRequestqueue message remains onLatitude/Longitudeper design (intentionally kept, seecommon_dtos.mdline 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.mdupdated during Step 13 with: (a) contracts inventory line bumped to mentionuav-tile-upload.mdv1.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.mdF1/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.mdandtests_integration.mdare 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.mdandtests/traceability-matrix.mdupdated 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:
- JSON-body endpoints —
WithValidation<T>()+ValidationEndpointFilter<T>+JsonSerializerOptions.UnmappedMemberHandling.Disallow. Used byPOST /api/satellite/tiles/inventory(AZ-796 cycle 7),POST /api/satellite/request(AZ-808 cycle 8),POST /api/satellite/route(AZ-809 cycle 8). - Multipart endpoints — bespoke
UavUploadValidationFiltercomposing deserializer + FluentValidation + envelope cross-field. Used byPOST /api/satellite/upload(AZ-810 cycle 8). - Query-param endpoints —
RejectUnknownQueryParamsEndpointFilter+WithValidation<TQuery>(). Used byGET /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.