Step 12 (Test-Spec Sync): adds BT-27 for the AZ-796 9-rule validation surface and 12 cycle-7 AC rows + Coverage Summary update to traceability-matrix.md. Step 13 (Update Docs): module-layout + module docs for the new SatelliteProvider.Api/Validators namespace + GlobalExceptionHandler + updated TileInventory DTO; tests_unit + tests_integration document the new InventoryRequestValidatorTests (16 unit tests covering all 9 rules) + TileInventoryValidationTests (16 integration tests) + ProblemDetailsAssertions support; glossary entries for Validation Problem Details / FluentValidation / Unmapped Member Handling; system-flows F8 (Tile Inventory Bulk Lookup) expanded with deserializer + validator gates and a 13-row Validation Surface table; data_parameters § Tile Inventory documents the v2 input schema + constraints; ripple_log_cycle7 captures the doc-side ripple decisions. Step 14 (Security Audit): 5-phase audit ran; verdict PASS_WITH_WARNINGS (3 Low findings — D-AZ795-1 FluentValidation 12.0.0 -> 12.1.1 recommended bump, F-AZ795-1 JsonException.Message leak in 400 detail, F-AZ795-2 BadHttpRequestException.Message leak). No Critical / High; auth runs before validation (confirmed in Program.cs); two NuGet additions (FluentValidation 12.0.0 + .DependencyInjectionExtensions 12.0.0) both CVE-clean. Per-phase reports plus consolidated security_report_cycle7.md. Step 15 (Performance Test): docker compose stack used for perf run, scripts/run-performance-tests.sh exited 0 with 8/8 scenarios PASS (second consecutive clean exit-0); added PT-09 cycle-7 smoke probe (v2 z/x/y schema, 2500-tile all-miss batch) measuring min=27ms median=44ms p95=73ms max=86ms (13.7x under AZ-505 AC-4 1000ms budget). PT-07/08 improvements traced to the cycle-6 TLS handshake-overhead identification, not application-side change. Co-authored-by: Cursor <cursoragent@cursor.com>
11 KiB
Cycle 7 — Documentation Ripple Log
Cycle: 7 (AZ-794 z/x/y rename + AZ-795 strict-validation epic + AZ-796 inventory-endpoint validation)
Generated by: /document skill (task mode) during autodev Step 13
Resolution method: Grep --type cs against every new or changed symbol introduced by the three tasks. C# using-based import analysis on TileCoord (renamed fields + [JsonRequired]), InventoryRequestValidator, ValidationEndpointFilter<T>, GlobalExceptionHandler, GlobalValidatorConfig, plus ProblemDetailsAssertions and ValidatorTestModuleInitializer in the test projects. No static-analyzer (NDepend, etc.) was used — the new surface is shallow and lives almost entirely behind Program.cs + the two new test files, so the literal usage scan is exhaustive.
Directly-changed source files (cycle 7)
SatelliteProvider.Common/DTO/TileInventory.cs(AZ-794, modified) —TileCoordproperties renamedTileZoom/TileX/TileY→Z/X/Ywith[JsonRequired]on each;TileInventoryEntryecho fields renamed in lockstep. Wire field names arez/x/yper the camelCase resolver.SatelliteProvider.Api/Program.cs(AZ-795 + AZ-796, modified) —ConfigureHttpJsonOptions(o => o.SerializerOptions.UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow)(AZ-795).AddProblemDetails(...)global ProblemDetails configurator (AZ-795).AddExceptionHandler<GlobalExceptionHandler>()+UseExceptionHandler()middleware order (AZ-795).AddValidatorsFromAssemblyContaining<Program>()+GlobalValidatorConfig.ApplyOnce()at startup (AZ-795 + AZ-796)..WithValidation<TileInventoryRequest>()on theMapPost("/api/satellite/tiles/inventory", …)builder (AZ-796).- Endpoint summary / description bumped to reference
tile-inventory.mdv2.0.0 +error-shape.mdv1.0.0.
SatelliteProvider.Api/Validators/InventoryRequestValidator.cs(AZ-796, new) —AbstractValidator<TileInventoryRequest>+ nestedTileCoordValidatorwith 9 rules (XOR, per-array cap, Z/X/Y ranges).SatelliteProvider.Api/Validators/ValidationEndpointFilter.cs(AZ-795, new) — genericIEndpointFilter<T>that runs the registeredIValidator<T>and emitsResults.ValidationProblem(...)on failure.SatelliteProvider.Api/Validators/ValidationEndpointFilterExtensions.cs(AZ-795, new) — opt-inRouteHandlerBuilder.WithValidation<T>()extension; intentionally orthogonal to per-endpoint authorization configuration.SatelliteProvider.Api/Validators/GlobalValidatorConfig.cs(AZ-795 + AZ-796, new) — idempotentApplyOnce()configures FluentValidation's globalPropertyNameResolverto camelCase (tiles[0].zinstead ofTiles[0].Z) pererror-shape.mdInv-4. Called fromProgram.csand from the unit-test assembly's[ModuleInitializer].SatelliteProvider.Api/GlobalExceptionHandler.cs(AZ-795, new) —IExceptionHandlerthat interceptsBadHttpRequestException(JsonException)(the System.Text.Json strict-parse path: unknown fields,[JsonRequired]violations, type mismatches) and emits the sameValidationProblemDetailsshape that FluentValidation produces. 5xx paths pass through with sanitised body + correlation id (continuation of the AZ-353 contract)._docs/02_document/contracts/api/error-shape.md(AZ-795, new) — v1.0.0 uniform error-body contract. Single source of truth for theValidationProblemDetailswire shape across both layers and across all future child tickets of the AZ-795 epic._docs/02_document/contracts/api/tile-inventory.md(AZ-794 + AZ-796, modified) — bumped to v2.0.0; documents the 9 validation rules + thez/x/yrename;Producer taskblock extended to credit AZ-505 + AZ-794 + AZ-796.SatelliteProvider.Tests/Validators/InventoryRequestValidatorTests.cs(AZ-796, new) — 16 unit tests against the validator viaTestValidate(...).SatelliteProvider.Tests/TestSupport/ValidatorTestModuleInitializer.cs(AZ-795, new) — callsGlobalValidatorConfig.ApplyOnce()at test-assembly load.SatelliteProvider.IntegrationTests/ProblemDetailsAssertions.cs(AZ-795, new) — shared response-shape helper consumed by every future per-endpoint validation test.SatelliteProvider.IntegrationTests/TileInventoryValidationTests.cs(AZ-796, new) — 16 end-to-end tests; one per validation rule (with sub-cases) plus a happy path.SatelliteProvider.IntegrationTests/TileInventoryTests.cs(AZ-794, modified) — updatedtileZoom/tileX/tileYJSON payloads toz/x/y; reduced the synthetic x/y values to stay inside the slippy-map bounds enforced byTileCoordValidator.SatelliteProvider.IntegrationTests/IdempotentPostTests.cs(AZ-795, modified) — route-point payload PascalCase → camelCase (lat/lon) because the post-cycle-7 strict deserializer no longer silently drops the wrong field names that the test had been sending pre-cycle 7.scripts/probe_inventory_validation.sh(AZ-796, new) — manual probe script; exercises each failure mode end-to-end and captures responses for change-review evidence.
Importer scan results
| Symbol | Importer count | Importer files | Component touched |
|---|---|---|---|
TileCoord.Z / TileCoord.X / TileCoord.Y (renamed properties; wire names z/x/y) |
5 | TileService.cs (Uuidv5.LocationHashForTile), TileInventoryTests.cs, TileInventoryValidationTests.cs, InventoryRequestValidatorTests.cs, TileInventory.cs self-references in TileInventoryEntry |
TileDownloader (production), Tests (unit + integration) |
[JsonRequired] on TileCoord.Z/X/Y |
n/a | enforced at runtime by System.Text.Json + caught by GlobalExceptionHandler (no compile-time consumer) |
WebApi (deserializer + handler) |
InventoryRequestValidator / TileCoordValidator |
3 | Program.cs (assembly-scan registration via AddValidatorsFromAssemblyContaining<Program>()), InventoryRequestValidatorTests.cs, TileInventoryValidationTests.cs (indirect through the running API) |
WebApi (production), Tests (unit + integration) |
ValidationEndpointFilter<T> / WithValidation<T>() |
1 (current) + N-future | Program.cs (MapPost("/api/satellite/tiles/inventory", …).WithValidation<TileInventoryRequest>()) |
WebApi |
GlobalValidatorConfig.ApplyOnce |
2 | Program.cs (production), ValidatorTestModuleInitializer.cs (unit-test assembly load) |
WebApi, Tests (unit) |
GlobalExceptionHandler |
1 | Program.cs (DI registration + middleware order) |
WebApi |
ProblemDetailsAssertions.AssertValidationProblem |
1 (current) + N-future | TileInventoryValidationTests.cs; designed to be reused by every future per-endpoint child task under AZ-795 |
Tests (integration) |
FluentValidation package (12.0.0) |
4 | SatelliteProvider.Api.csproj, SatelliteProvider.Tests.csproj, InventoryRequestValidator.cs, InventoryRequestValidatorTests.cs |
WebApi, Tests (unit) |
Doc refresh decisions
All importers land inside components that already received targeted updates during Step 10 (Implement) and this Step 13:
- WebApi (
Program.cs) — updated_docs/02_document/modules/api_program.mdwith the new endpoint description, the newApi/Validatorssection (filter + extensions + validator + global config), the newApi/GlobalExceptionHandlersection, expanded DI registration (ProblemDetails + GlobalExceptionHandler + strict JSON + FluentValidation), and the new dependency entries. - Common (DTOs) — updated
_docs/02_document/modules/common_dtos.md:TileCoordnow documents the rename +[JsonRequired]markers + ValidationProblemDetails fallout;TileInventoryRequestdocuments the XOR enforcement byInventoryRequestValidator;TileInventoryEntrydocuments the rename echo;TileInventoryLimitsdocuments the validator as the enforcer. - Validators (new subfolder) — captured under
module-layout.mdwith two new entries:Api/Validators/{InventoryRequestValidator,TileCoordValidator,ValidationEndpointFilter,ValidationEndpointFilterExtensions,GlobalValidatorConfig}.Api/GlobalExceptionHandler.
- Tests (unit) — updated
_docs/02_document/modules/tests_unit.mdwith the new "AZ-795 + AZ-796 — strict inventory validation (cycle 7)" subsection, the new[ModuleInitializer]helper, the new FluentValidation/TestHelper NuGet entry, and the cycle 7 unit-suite totals (311 tests). - Tests (integration) — updated
_docs/02_document/modules/tests_integration.md: newTileInventoryValidationTestsentry, newProblemDetailsAssertionshelper entry, cycle-7 stability note onTileInventoryTests, and the cycle-7 fix onIdempotentPostTests(payload rename forced by strict deserializer).
System-level docs also updated this pass:
architecture.md— already carries the new "§ 9 Input Validation (AZ-795)" section (was added during the implementation phase along with the validator coverage table). No further changes needed; the AZ-794 wire-format rename is captured at the inventory-contract level rather than in architecture prose.system-flows.md— F8 flow header updated to credit cycle 7; sequence diagram annotated with the two new validation gates (deserializer + filter); Validation Surface table expanded from 4 rows to 13 rows covering every failure mode fromerror-shape.md.glossary.md—Tile Inventoryentry updated to v2.0.0 wire shape + cite the cycle-7 validator; added three new entries:Validation Problem Details,FluentValidation,Unmapped Member Handling.module-layout.md— Last Updated bumped + cycle-7 changelog line prepended.tests/blackbox-tests.mdandtests/traceability-matrix.md— updated during Step 12 (Test-Spec Sync): BT-27 added + 12 AC rows added (AZ-794 AC-1..AC-4 + AZ-795 epic-level + AZ-796 AC-1..AC-7) + Coverage Summary refresh.
No-ripple components
These components were NOT touched by cycle-7 changes and require no doc update:
- DataAccess — no schema or repository signature changes in cycle 7. The cycle-6
tiles_leaflet_pathcovering index and the cycle-5 identity columns are unaffected by the wire-format rename or by the new validators. - TileDownloader (
TileService.GetInventoryAsync) — the algorithm is unchanged: it still computesUuidv5.LocationHashForTile(z, x, y)per coord. Only the property names on the DTO changed (TileZoom→Z, etc.); the value contract is identical. - RegionProcessing / RouteManagement — no imports against cycle-7 symbols.
- DataAccess migrations — no new migration in cycle 7; the existing identity columns and indices already carry the production load.
Parse-failure / heuristic notes
None — every symbol resolved via direct Grep --type cs. No fallback heuristic was needed. The cycle 7 surface is intentionally narrow (3 tasks, all WebApi-layer concerns) which keeps the ripple log short.
AZ-795 epic posture
The architecture.md § 9 table classifies all other public endpoints as partial and tags them as "future AZ-795 child" — the epic remains open. Cycle 7 lands the shared infrastructure + the first per-endpoint application (AZ-796). Subsequent child tickets will reuse ValidationEndpointFilter<T>, ProblemDetailsAssertions, and the error-shape.md contract without adding new infrastructure.