# Batch Report

**Batch**: 01 (cycle 6)
**Tasks**: AZ-505 — Tile inventory endpoint + HTTP/2 + Leaflet covering index
**Date**: 2026-05-12

## Task Results

| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|------|--------|---------------|-------|-------------|--------|
| AZ-505_tile_inventory_http2_leaflet_index | Done | 13 source + 9 doc + 1 migration | New: `TileInventoryTests.cs` (6 sub-tests), `Http2MultiplexingTests.cs`, `LeafletPathIndexOnlyTests.cs`. Wired into both smoke + full suites. | 6/6 functional ACs covered; AC-7 doc-gate satisfied | None remaining; one Medium / Maintainability auto-fix landed (consolidate `ComputeLocationHash` → `Uuidv5.LocationHashForTile`). |

## Changes

### Production code

- `SatelliteProvider.Common/DTO/TileInventory.cs` (new) — `TileInventoryRequest` (XOR `Tiles` / `LocationHashes`), `TileCoord`, `TileInventoryResponse`, `TileInventoryEntry`, `TileInventoryLimits.MaxEntriesPerRequest = 5000`.
- `SatelliteProvider.Common/Utils/Uuidv5.cs` — added `LocationHashForTile(int z, int x, int y)` static. Single source-of-truth for the cross-repo `UUIDv5(TileNamespace, "{z}/{x}/{y}")` formula consumed by repository, service, and tests.
- `SatelliteProvider.DataAccess/Repositories/ITileRepository.cs` — added `GetTilesByLocationHashesAsync(IReadOnlyList<Guid>) → Task<IReadOnlyDictionary<Guid, TileEntity>>`.
- `SatelliteProvider.DataAccess/Repositories/TileRepository.cs` — rewrote `GetByTileCoordinatesAsync` to filter by `location_hash` (index-only-scannable against `tiles_leaflet_path`); implemented `GetTilesByLocationHashesAsync` via `NpgsqlCommand` + `NpgsqlDbType.Array | Uuid` parameter binding (Dapper's `IEnumerable` expansion is incompatible with `ANY($1::uuid[])`); removed the AZ-505 first-pass `ComputeLocationHash` helper in favour of `Uuidv5.LocationHashForTile`.
- `SatelliteProvider.Services.TileDownloader/TileService.cs` — added `GetInventoryAsync(TileInventoryRequest, CancellationToken)` implementing the contract's ordering / present-absent / Form-A-vs-Form-B shaping rules; consolidated `locationHashName` in `BuildTileEntity` to the same helper.
- `SatelliteProvider.Api/Program.cs` — registered `POST /api/satellite/tiles/inventory` with `.RequireAuthorization()`, `.Accepts<TileInventoryRequest>("application/json")`, `.Produces<TileInventoryResponse>(200)`, `.ProducesProblem(400)`, and OpenAPI description referencing the contract. Configured Kestrel `HttpProtocols.Http1AndHttp2` on all listener endpoints.
- `SatelliteProvider.DataAccess/Migrations/015_AddTilesLeafletPathIndex.sql` (new) — `CREATE INDEX tiles_leaflet_path (location_hash, captured_at DESC, updated_at DESC, id DESC) INCLUDE (file_path, source)`; `DROP INDEX IF EXISTS idx_tiles_location_hash`. Forward + back-migration documented in the header; lock-window caveat captured per AZ-505 Risk 2.

### Tests

- `SatelliteProvider.IntegrationTests/TileInventoryTests.cs` (new) — AC-1 (ordering + present/absent shaping, 25-entry interleaved fixture), AC-2 (DB-level proof that the most-recent-via-location_hash selection rule survives the rewrite), AC-4 (perf budget, full-suite only), AC-6 (4 validation cases).
- `SatelliteProvider.IntegrationTests/Http2MultiplexingTests.cs` (new) — AC-5 (20 concurrent GETs over a single H2 connection on h2c).
- `SatelliteProvider.IntegrationTests/LeafletPathIndexOnlyTests.cs` (new) — AC-3 (EXPLAIN ANALYZE + Index Only Scan regex + Heap Fetches ≤ 1).
- `SatelliteProvider.IntegrationTests/Program.cs` — wired the three new test entry points into both `RunSmokeSuite` and `RunFullSuite`. `Http2MultiplexingTests.RunAll` runs early in `Main` because it sets a process-wide `AppContext` switch.

### Documentation

- `_docs/02_document/contracts/api/tile-inventory.md` v1.0.0 (new) — Form A / Form B XOR validation, 5000-entry cap, 7-line invariant table, response field reference, test-case matrix, change log entry.
- `_docs/02_document/contracts/data-access/tile-storage.md` v1.0.0 → v2.0.0 (major bump) — captures AZ-503-foundation identity columns + `idx_tiles_unique_identity` integer UPSERT + `tiles_leaflet_path` covering index + `location_hash`-keyed read rule + new `GetTilesByLocationHashesAsync` method. Added Inv-7 / Inv-8 / Inv-9. Change Log entry names both producing tasks (AZ-503-foundation + AZ-505).
- `_docs/02_document/architecture.md`, `_docs/02_document/module-layout.md`, `_docs/02_document/glossary.md`, `_docs/02_document/data_model.md`, `_docs/02_document/modules/api_program.md`, `_docs/02_document/modules/dataaccess_tile_repository.md`, `_docs/02_document/components/02_data_access/description.md` — endpoint row, repo method row, query/index table updates, Location Hash entry promotion, migration 015 entry, AZ-505 + v2.0.0 freeze references.

## AC Test Coverage

6/6 functional ACs covered (AC-1, AC-2, AC-3, AC-4, AC-5, AC-6). AC-7 (contract artifacts) is a documentation gate verified by file presence + version-string + Change Log entry.

## Code Review Verdict

PASS — see `_docs/03_implementation/reviews/batch_01_cycle6_review.md`. One Medium / Maintainability auto-fix landed during review (consolidate `ComputeLocationHash` duplication into `Uuidv5.LocationHashForTile`). No remaining findings.

## Auto-Fix Attempts

1 — `ComputeLocationHash` duplication; resolved in a single pass.

## Stuck Agents

None.

## Notes

- **Cross-repo invariant preserved**: `Uuidv5.TileNamespace = 5b8d0c2e-7f1a-4d3b-9c5e-1f3a8e7d2b6c` unchanged; consumed by `LocationHashForTile`, which the repository / service / tests all now route through. The Python sibling (`gps-denied-onboard/components/c6_tile_cache/_uuid.py:TILE_NAMESPACE`) is not touched by this PBI per AZ-505 Constraints — sibling-repo concern.
- **Onboard consumer feature flag**: `gps-denied-onboard` AZ-316 retains `c11.use_bulk_list_endpoint=false` default until this PBI is deployed (per AZ-505 Constraints + Risk 4). Surfaced for cross-cycle visibility; no in-repo work.
- **Leftover replay (cycle 3 perf-harness)**: `_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md` remains open. AZ-505 does NOT touch `scripts/run-performance-tests.sh` (out of scope; the open leftover names DNS pre-warmup + cloud perf rerun as the unblocking steps). No replay attempted this cycle per the leftover's "no immediate replay planned" entry.

## Next Batch

All tasks complete. Cycle 6 implementation closes here. Hand off to Step 11 (test-run) for the full integration-test gate via Docker Compose.
