# 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 `` 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` 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.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.