# Cumulative Code Review Report (K=3)

**Batches**: 22, 23, 24
**Tasks covered**:
- B22: AZ-372 (C19 — dotnet format + analyzers + Coverlet)
- B23: AZ-376 (C23), AZ-378 (C25), AZ-379 (C26), AZ-380 (C27)
- B24: AZ-375 (C22), AZ-377 (C24)
**Date**: 2026-05-11
**Mode**: Cumulative (all 7 phases on union of changed files since last cumulative review)
**Verdict**: PASS_WITH_WARNINGS

## Scope (changed files since cumulative review @ batch 21 close)

Production source:
- `SatelliteProvider.Common/Configs/MapConfig.cs`
- `SatelliteProvider.Common/Utils/GeoUtils.cs`
- `SatelliteProvider.DataAccess/Repositories/ITileRepository.cs`
- `SatelliteProvider.DataAccess/Repositories/TileRepository.cs`
- `SatelliteProvider.DataAccess/Repositories/RegionRepository.cs`
- `SatelliteProvider.DataAccess/Repositories/RouteRepository.cs`
- `SatelliteProvider.Services.TileDownloader/GoogleMapsDownloaderV2.cs`
- `SatelliteProvider.Api/Program.cs`

Tooling / config:
- `.editorconfig`
- `Directory.Build.props`
- `scripts/run-tests.sh`

Tests added or extended:
- `SatelliteProvider.Tests/ToolingConfigurationTests.cs`
- `SatelliteProvider.Tests/RepositoryRefactorTests.cs`
- `SatelliteProvider.Tests/GeoUtilsRefactorTests.cs`
- `SatelliteProvider.Tests/DownloaderRefactorTests.cs`

## Findings

| # | Severity | Category | File:Line | Title |
|---|----------|----------|-----------|-------|
| 1 | Low | Architecture | `SatelliteProvider.DataAccess/SatelliteProvider.DataAccess.csproj` + `_docs/02_document/architecture_compliance_baseline.md` (F5) | DataAccess→Common dependency surface widened; baseline doc still claims "no Common dependency" |
| 2 | Low | Maintainability | `SatelliteProvider.DataAccess/Repositories/RouteRepository.cs:38` | `route_points` column list still inline (single use) |
| 3 | Low | Spec-Gap | `_docs/02_tasks/done/AZ-37*` task ACs | Stale "37 unit + 5 smoke" test count in AC text |
| 4 | Low | Maintainability | `SatelliteProvider.Common/Configs/MapConfig.cs` | `TileSizePixels` is now both a property default *and* a `const`; small surface duplication for compile-time consumers |

### Finding Details

**F1: DataAccess→Common dependency surface widened** (Low / Architecture)
- Location: `SatelliteProvider.DataAccess/SatelliteProvider.DataAccess.csproj` (project reference); affects `architecture_compliance_baseline.md` Finding F5.
- Description: AZ-377 introduces `using SatelliteProvider.Common.Configs;` and `using SatelliteProvider.Common.Utils;` in `TileRepository.cs`. The baseline doc (Resolved F5) and `module-layout.md` claim DataAccess has no Common dependency. In reality the `<ProjectReference>` to Common already exists in the csproj and 5 files in DataAccess (`RegionRepository.cs`, `IRegionRepository.cs`, `Models/RegionEntity.cs`, `Models/RoutePointEntity.cs`, `TypeHandlers/EnumStringTypeHandler.cs`) already import `SatelliteProvider.Common.Enums`. Batch 24 widens the surface to also include `Common.Configs` and `Common.Utils`.
- Impact: Documentation drift only. The actual dependency graph has been DataAccess→Common since at least the AZ-309 cleanup; the baseline marked F5 "Resolved" prematurely. No new layering violation — Common stays at the leaf and DataAccess remains one level above it.
- Suggestion: in the next documentation pass, update `_docs/02_document/module-layout.md` (DataAccess `Imports from`) and the baseline F5 entry to match reality (`SatelliteProvider.Common.Enums`, `SatelliteProvider.Common.Configs`, `SatelliteProvider.Common.Utils`). Do not invert the actual code dependency — the constants legitimately belong in Common and DataAccess legitimately needs them.
- Task: AZ-377

**F2: `route_points` column list still inline** (Low / Maintainability) — carry-over from batch 23
- Location: `SatelliteProvider.DataAccess/Repositories/RouteRepository.cs:38-46`
- Description: `GetRoutePointsAsync` keeps its 8-column list inline; AZ-379's "extract once per repository" target is satisfied by the `routes` ColumnList.
- Suggestion: extract only when a second SELECT against `route_points` arrives.
- Task: AZ-379

**F3: Stale "37 unit + 5 smoke" count in AC text** (Low / Spec-Gap) — carry-over from batch 23
- Location: AC-3 of AZ-375, AZ-376, AZ-377, AZ-378, AZ-379, AZ-380.
- Description: Tasks were authored when the unit count was 37; suite now reports 200/200 passing after batch 24. The "scripts/run-tests.sh --smoke" command still gates correctly; only the literal count in the AC narrative is stale.
- Suggestion: leave as-is — the gate is the script's exit code, not the printed number; rewriting historical AC text would create churn without changing what is verified.
- Task: documentation/process

**F4: `MapConfig.TileSizePixels` exists as both property and `const`** (Low / Maintainability)
- Location: `SatelliteProvider.Common/Configs/MapConfig.cs:12-14`
- Description: AZ-377 introduces `public const int DefaultTileSizePixels = 256;` so DataAccess (which cannot inject `IOptions<MapConfig>` from a Repository constructor) can reference a single source. The property `TileSizePixels` keeps its setter for config binding and is now wired to the const as its default. Small surface duplication, deliberate.
- Impact: Two access paths to the same value — `MapConfig.DefaultTileSizePixels` (compile-time) and `IOptions<MapConfig>.Value.TileSizePixels` (runtime). If a deployment ever overrides the property in `appsettings.json`, DataAccess will silently disagree with config-bound consumers because it reads the const.
- Suggestion: accepted trade-off for now — `appsettings.json` does not currently override the value and DI in DataAccess is a separate refactor (would require pushing `IOptions` through the repository interface). Document the decision in `MapConfig.cs` (already added an XML-style comment in batch 24) and revisit if anyone overrides `TileSizePixels` in config.
- Task: AZ-377

## Phase results (summary)

**Phase 2 — Spec compliance**
- AZ-372: format gate present (`Step 0` in `run-tests.sh`), Directory.Build.props has analyzers + Coverlet, ToolingConfigurationTests pin them. ✓
- AZ-376: `FindExistingTileAsync` removed from interface and impl (RepositoryRefactorTests AZ376_AC1 passes). ✓
- AZ-378: RegionRepository, RouteRepository constructors take only the connection string; TileRepository keeps `_logger` and uses it for slow-query warnings (RepositoryRefactorTests AZ378_AC1, AC2 pass). ✓
- AZ-379: All three repositories define exactly one `private const string ColumnList`; SELECTs use the const (RepositoryRefactorTests AZ379_AC1, AC2 pass). ✓
- AZ-380: `CalculatePolygonDiagonalDistance` removed (GeoUtilsRefactorTests AZ380_AC1 passes). ✓
- AZ-375: HashSet<(int, int, int)> built once and `Contains((x, y, zoomLevel))` replaces the per-cell `FirstOrDefault`; `_processingConfig.LatLonTolerance` no longer referenced at this site (DownloaderRefactorTests AZ375_AC1 passes; grep confirms `0.0001` only remains in the unrelated `ProcessingConfig` default and tests). ✓
- AZ-377: `EarthRadiusMeters`, `EarthEquatorialCircumferenceMeters`, `MetersPerDegreeLatitude` are `public const double` on `GeoUtils`; `MapConfig.DefaultTileSizePixels` is a `public const int` mirroring the property default; all source-code occurrences of `6378137`, `40075016.686`, `111000`, and source-code occurrences of `256` are confined to those declarations (DownloaderRefactorTests + GeoUtilsRefactorTests AZ377_AC1, AC2 pass). ✓

**Phase 3 — Code quality**
- All edits under SRP — no method gained more than one responsibility; HashSet builder is a small, named, local computation in the same method; logger removal from RegionRepository/RouteRepository is purely subtractive.
- No bare catches introduced. No new debug-noise logs. No obvious duplication.

**Phase 4 — Security quick-scan**
- No new SQL string interpolation (Dapper named params throughout, ColumnList is a `const string`, not user input).
- No new secrets, no new external inputs.
- Slow-query warning logs query duration and SQL text, both safe.

**Phase 5 — Performance scan**
- AZ-375 directly removes an O(N) per-cell scan inside an O(grid) loop: a region of NxN tiles previously did O(N² · existingTiles) work; now O(existingTiles) preprocessing + O(1) per cell.
- No N+1 queries introduced; the existing-tiles query batch shape is unchanged.
- No blocking I/O in async paths added.

**Phase 6 — Cross-task consistency**
- Batch 23 + 24 collectively touch the same three repositories and `GoogleMapsDownloaderV2`; constants moved into Common are consumed by DataAccess and the downloader uniformly. No divergent patterns.
- AZ-379's ColumnList convention applied identically across all three repos (`private const string ColumnList = @"…"`, used via `$@"… SELECT {ColumnList} …"`).

**Phase 7 — Architecture compliance**
- Layer direction: Common (leaf) ← DataAccess ← Services.TileDownloader ← Api. All new imports respect the table.
- Public API respect: `GeoUtils`, `MapConfig`, and their new constants are top-level public types in `Common`. No internal-file imports.
- New cyclic deps: none introduced (DataAccess→Common is one-way; Common does not reference DataAccess).
- Duplicate symbols: the previous duplicate Earth literals across `GoogleMapsDownloaderV2.cs` and `TileRepository.cs` are now collapsed to one declaration in `GeoUtils.cs`. The `MapConfig.TileSizePixels` property + `MapConfig.DefaultTileSizePixels` const live in the same class — see F4.

## Baseline Delta

| Status | Severity | Title | Note |
|--------|----------|-------|------|
| Carried over | Low | F5 — DataAccess Common dependency drift | Documentation drift; widened by AZ-377 (see F1 above). |
| Resolved | Low | duplicate Earth literals across modules | Cumulative review @ batch 21 had flagged the same literals in two files; AZ-377 collapses them to a single source in `GeoUtils`. |
| Newly introduced | — | none | No new Critical / High / Medium architectural findings. |

Per-category counts (current scope):

| Category | Critical | High | Medium | Low |
|----------|----------|------|--------|-----|
| Architecture | 0 | 0 | 0 | 1 |
| Maintainability | 0 | 0 | 0 | 2 |
| Spec-Gap | 0 | 0 | 0 | 1 |
| Security | 0 | 0 | 0 | 0 |
| Performance | 0 | 0 | 0 | 0 |
| Bug | 0 | 0 | 0 | 0 |

## Test posture

- `scripts/run-tests.sh --unit-only`: 200/200 PASS (10 new tests added across batches 23-24).
- Format gate (`dotnet format whitespace --verify-no-changes`): PASS.
- Full smoke / integration suite: not re-run as part of this cumulative review (next session boundary will run it before commit if the engine requires).

## Verdict logic

- 0 Critical, 0 High, 0 Medium, 4 Low → **PASS_WITH_WARNINGS**.
- No FAIL conditions; no auto-fix loop required.

## Recommended follow-ups (non-blocking)

1. Refresh `_docs/02_document/module-layout.md` and the baseline F5 entry to reflect the actual DataAccess→Common dependency (Common.Enums, Common.Configs, Common.Utils).
2. Decide whether `MapConfig.DefaultTileSizePixels` should remain a const or be threaded through `IOptions` into DataAccess; defer until someone overrides the value in config.
3. Consider a future task to update the AC test-count narrative across the AZ-37x tickets if/when the project does a doc-sweep.
