mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 15:41:13 +00:00
[AZ-350] Refactor 03 Phase 2: roadmap + 27 task specs + safety net
Adds Phase 0 (baseline metrics, .gitignore tweaks), Phase 1 (research findings, list-of-changes), and Phase 2 (refactoring roadmap, epic AZ-350, 27 task specs AZ-351..AZ-380, dependency table updates) for the 03-code-quality-refactoring run. Phase 3 (Safety Net) re-verified: 40/40 unit + 5/5 smoke integration pass; documented in test_specs/existing_coverage.md. Coverage % gating deferred to ticket C19 (AZ-372) which adds Coverlet + reportgenerator. Auto-chains to Phase 4 (Execution) via /implement starting at batch 1 (Phase 1 critical fixes). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
# Phase 2 — Refactoring Roadmap (03-code-quality-refactoring)
|
||||
|
||||
**Date**: 2026-05-10
|
||||
**Total changes**: 27 (C01–C27)
|
||||
**Selected hardening tracks**: Track A — Technical Debt (extra sweep produced C23–C27)
|
||||
**Total estimated complexity**: ~66 story points across 4 execution phases
|
||||
|
||||
## Weak-Point Assessment (per area)
|
||||
|
||||
| Area | Symptom | Files | Driver change(s) |
|
||||
|------|---------|-------|-------------------|
|
||||
| API exception handling | per-endpoint try/catch leaks `ex.Message` to clients | `Program.cs` (six endpoints) | C03 |
|
||||
| API security defaults | CORS opens to `*` when `AllowedOrigins` empty | `Program.cs:37-47` | C04 |
|
||||
| API contract honesty | stub endpoints return 200 OK | `Program.cs:177-185` | C05 |
|
||||
| Startup observability | null logger handed to migrator | `Program.cs:82-83` | C01 |
|
||||
| Tile cache lifecycle | year-based version invalidates cache annually | `TileService.cs`, `TileRepository.cs`, migrations | C06 |
|
||||
| Async pipeline failure mode | 9-way duplicated catch ladder | `RegionService.cs:148-197` | C07 |
|
||||
| DI hygiene | service-locator pattern in worker | `RouteProcessingService.cs:18-22` | C08 |
|
||||
| API idempotency | retried POST → 500 on duplicate `Id` | `Program.cs`, `RegionService.cs`, `RouteService.cs` | C09 |
|
||||
| Concurrency | non-atomic counters | `RegionRequestQueue.cs:12-13` | C10 |
|
||||
| God-class | 750-LOC `BackgroundService` with 6 responsibilities | `RouteProcessingService.cs` | C11 |
|
||||
| Long method | 165-LOC `CreateRouteAsync` | `RouteService.cs:27-211` | C12 |
|
||||
| Duplication | Haversine, CSV, stitcher, Earth constants, SQL columns | multiple | C13, C14, C15, C24, C26 |
|
||||
| Inline DTOs | six DTOs at the bottom of `Program.cs` | `Program.cs:272-353` | C16 |
|
||||
| Typing | status / point-type bare strings (+ AC drift) | `RegionService.cs`, `RouteService.cs`, `acceptance_criteria.md` | C17 |
|
||||
| Configuration | magic 5 min, 200 m, 5 s, 0.0001, retry delays, allowed zooms | multiple | C18 |
|
||||
| Tooling | no formatter / analyzer / coverage | solution root | C19 |
|
||||
| Versioning semantics | `MapsVersion` is a date label, not a version | `TileService.cs:154` | C20 |
|
||||
| HTTP client setup | per-call header configuration repeated × 3 | `GoogleMapsDownloaderV2.cs` | C21 |
|
||||
| Algorithmic | O(N²) existing-tile lookup | `GoogleMapsDownloaderV2.cs:245-265` | C22 |
|
||||
| Dead code (Phase 2a sweep) | unused `FindExistingTileAsync` | `TileRepository.cs:51-76` | C23 |
|
||||
| Dead code (Phase 2a sweep) | duplicate Earth constants + magic 111000 | `TileRepository.cs:82-91` etc. | C24 |
|
||||
| Dead code (Phase 2a sweep) | unused `_logger` fields in repositories | `*Repository.cs:11` | C25 |
|
||||
| Dead code (Phase 2a sweep) | repeated SELECT column lists | `*Repository.cs` | C26 |
|
||||
| Dead code (Phase 2a sweep) | trivial alias `CalculatePolygonDiagonalDistance` | `GeoUtils.cs:129-132` | C27 |
|
||||
|
||||
## Gap Analysis (versus acceptance criteria)
|
||||
|
||||
| AC | Current state | Gap | Closed by |
|
||||
|----|---------------|-----|-----------|
|
||||
| T1 | Cache key includes `version`; invalidates yearly | Wording must change to "(lat, lon, zoom_level, tile_size_meters); duplicates collapsed via DB unique constraint" | C06 (also updates restrictions.md K6) |
|
||||
| T2 | Concurrent download limit (4) enforced | None | — |
|
||||
| T3 | Tile stored on disk | None | — |
|
||||
| T4 | Tile metadata persisted | None | — |
|
||||
| R1 | Region transitions through correct states | bare strings → enum | C17 |
|
||||
| R2 / R3 / R4 / R5 / R6 | Files generated, sizes correct | None (preserved by C11/C14/C15) | — |
|
||||
| RT1 | Intermediate points every ~200 m | 200 m is hardcoded | C18 |
|
||||
| RT2 | "original / intermediate" point types | **Drift K8**: code uses `start`/`end`/`action`/`intermediate` (4 values). User-confirmed option α (keep code, update AC). | C17 |
|
||||
| RT3 | Total distance via Haversine | Two implementations exist | C13 |
|
||||
| RT4 | Geofence filtering | None | — |
|
||||
| RT5 | ZIP ≤ 50 MB | None (preserved by C11's `TilesZipBuilder`) | — |
|
||||
| RT6 | Route map stitched | None (preserved by C11's `RouteImageRenderer`) | — |
|
||||
| A1 / A2 / A3 | Endpoints behave correctly | 500 leakage on errors; 500 on duplicate POST | C03, C09 |
|
||||
| S1 | Migrations run on startup | Null logger to migrator | C01 |
|
||||
| S2 | Queue rejects when full | None | — |
|
||||
| S3 | Failed regions marked failed | 9-way catch ladder; one classification helper still leaves S3 satisfied | C07 |
|
||||
|
||||
No AC is **regressed** by this run; T1 / RT2 wording must be updated alongside C06 / C17 to track the implementation.
|
||||
|
||||
## Phased Execution Plan
|
||||
|
||||
The four phases are designed to be **executed in order**, each independently shippable. Each phase ends with smoke + unit suite green.
|
||||
|
||||
### Execution Phase 1 — Critical fixes (cheap, high return)
|
||||
**Estimated**: 12 pts · 6 changes · low risk overall
|
||||
|
||||
| Order | ID | Title | Pts | Risk |
|
||||
|-------|----|-------|-----|------|
|
||||
| 1 | C01 | Fix null logger to `DatabaseMigrator` | 2 | low |
|
||||
| 2 | C02 | Remove empty catch in `ExtractTileCoordinatesFromFilename` | 2 | low |
|
||||
| 3 | C10 | Delete write-only counters in `RegionRequestQueue` | 1 | low |
|
||||
| 4 | C05 | Stub endpoints return 501 | 2 | low |
|
||||
| 5 | C04 | Strict CORS by default | 2 | low |
|
||||
| 6 | C03 | Sanitize 5xx responses via `IExceptionHandler` | 3 | medium (changes 500 body shape) |
|
||||
|
||||
Why first: each one is self-contained, fixes a real correctness/security issue, and leaves the codebase observably better.
|
||||
|
||||
### Execution Phase 2 — High-value correctness
|
||||
**Estimated**: 11 pts · 3 changes · medium risk
|
||||
|
||||
| Order | ID | Title | Pts | Risk |
|
||||
|-------|----|-------|-----|------|
|
||||
| 7 | C07 | Consolidate `RegionService.ProcessRegionAsync` catch ladder | 3 | low |
|
||||
| 8 | C06 | Drop tile `Version`; latest row wins; new migration | 5 | medium (DB migration) |
|
||||
| 9 | C09 | Idempotency contract for caller-supplied GUIDs | 3 | medium (API behavior change on duplicate POST) |
|
||||
|
||||
Why second: C06 + C09 change DB / API behavior. Doing them after Phase 1 means the safety net (smoke + unit suite) is already operating against the sanitized error paths from C03. C06 must precede C20 in Phase 4.
|
||||
|
||||
### Execution Phase 3 — Structural cleanup (SRP + duplication)
|
||||
**Estimated**: 21 pts · 7 changes · medium risk
|
||||
|
||||
| Order | ID | Title | Pts | Risk |
|
||||
|-------|----|-------|-----|------|
|
||||
| 10 | C13 | Consolidate Haversine + filename parser | 2 | low |
|
||||
| 11 | C24 | Consolidate Earth constants and 111000 (mostly into `GeoUtils`) | 2 | low |
|
||||
| 12 | C15 | Shared `TileCsvWriter` | 2 | low |
|
||||
| 13 | C14 | Shared `TileGridStitcher` (region + route) | 3 | medium (image output verified) |
|
||||
| 14 | C16 | Move inline DTOs out of `Program.cs` | 2 | low |
|
||||
| 15 | C12 | Decompose `RouteService.CreateRouteAsync` (validator + builder + grid + mapper) | 5 | low |
|
||||
| 16 | C11 | Decompose `RouteProcessingService` (6 collaborators) | 5 | medium (large file, tested end-to-end) |
|
||||
| 17 | C08 | Replace `IServiceProvider` with `IRegionService` (folded into C11) | 2 | low |
|
||||
|
||||
C13/C24/C15/C14 land first so the bigger decompositions (C11/C12) reuse them.
|
||||
|
||||
### Execution Phase 4 — Typing, config, tooling, polish
|
||||
**Estimated**: 22 pts · 11 changes · low risk
|
||||
|
||||
| Order | ID | Title | Pts | Risk |
|
||||
|-------|----|-------|-----|------|
|
||||
| 18 | C18 | Move magic numbers to `ProcessingConfig` / `MapConfig` | 3 | low |
|
||||
| 19 | C17 | Status / point-type enums + AC RT2 update | 3 | low |
|
||||
| 20 | C20 | Clarify / drop `MapsVersion` | 2 | low |
|
||||
| 21 | C21 | Typed `HttpClient` for Google Maps | 2 | low |
|
||||
| 22 | C22 | O(N) existing-tile lookup (HashSet) | 2 | low |
|
||||
| 23 | C23 | Delete unused `FindExistingTileAsync` | 1 | low |
|
||||
| 24 | C25 | `_logger` fields: delete or use for slow-query log | 1 | low |
|
||||
| 25 | C26 | Extract repository SELECT column constants | 2 | low |
|
||||
| 26 | C27 | Delete `CalculatePolygonDiagonalDistance` | 1 | low |
|
||||
| 27 | C19 | Add `dotnet format`, NetAnalyzers, Coverlet | 3 | low |
|
||||
|
||||
C18 first so C22 can pick up its tolerance constant from config. C19 last — the analyzer flood is easiest to address once the larger refactors have settled the surface.
|
||||
|
||||
## Hardening Track Items (Track A — Technical Debt)
|
||||
|
||||
The user selected Track A. Items C23–C27 were generated by the Phase 2a sweep and slot into Execution Phase 4. No additional hardening items remain unaddressed.
|
||||
|
||||
Tracks B (Performance) and C (Security) were not selected. Their incidental coverage in this run:
|
||||
- Performance — only C22 addresses an algorithmic hotspot. No deeper profiling is performed.
|
||||
- Security — C03 (info disclosure) and C04 (CORS default) cover the two most concrete findings; no OWASP sweep performed.
|
||||
|
||||
## Applicability Gate (per skill: every roadmap item is `Selected`)
|
||||
|
||||
All 27 items have `Selected` status in `research_findings.md` (with C06 / C17 documentation updates explicitly approved by the user — α + drop-version directions). Zero items are `Rejected`, `Experimental only`, or `Needs user decision`. **Gate cleared.**
|
||||
|
||||
## Constraints Re-Verified at Roadmap Time
|
||||
|
||||
- **K1** (.NET 8 LTS): no upgrade proposed; all `IExceptionHandler`, `IOptions`, Dapper type handlers are .NET 8-native.
|
||||
- **K2** (Postgres 16): C06's `INSERT … ON CONFLICT … DO UPDATE` is supported.
|
||||
- **K3** (ImageSharp 3.1.11): C14's stitcher uses the existing dependency.
|
||||
- **K4** (single instance): no distributed-system idioms introduced.
|
||||
- **K5** (no auth): not affected by this run.
|
||||
- **K6 / K7** (year-based versioning, T1 wording): updated by C06 ticket.
|
||||
- **K8** (RT2 drift): updated by C17 ticket.
|
||||
- **K9** (50 MB ZIP cap): preserved by C11's `TilesZipBuilder`.
|
||||
- **K10** (smoke + unit green): each ticket runs the suite at the end.
|
||||
|
||||
## Self-Verification
|
||||
|
||||
- [x] All AC mapped to changes; only T1 + RT2 require wording updates (already attached to C06 / C17).
|
||||
- [x] All 27 changes are `Selected` per Phase 2a constraint-fit table.
|
||||
- [x] No item exceeds 5 complexity points (largest are C06, C11, C12 at 5 each).
|
||||
- [x] Hardening track A items (C23–C27) are accounted for in Execution Phase 4.
|
||||
- [x] Phase ordering respects dependencies (C18 before C22, C03 before C09, C13/C24/C14/C15 before C11/C12).
|
||||
- [x] No circular dependencies between change IDs.
|
||||
- [x] Roadmap stays inside the constraint matrix; no ❌ or ❓ cells in `research_findings.md`.
|
||||
@@ -0,0 +1,142 @@
|
||||
# Phase 2a — Research Findings (03-code-quality-refactoring)
|
||||
|
||||
**Date**: 2026-05-10
|
||||
**Mode**: Automatic
|
||||
**Run scope**: 22 changes from `list-of-changes.md` (post-user-edit on C06 and C10).
|
||||
|
||||
## Project Constraint Matrix
|
||||
|
||||
Extracted from `_docs/00_problem/restrictions.md`, `_docs/00_problem/acceptance_criteria.md`, and current code.
|
||||
|
||||
| # | Constraint | Source | Implication for this run |
|
||||
|---|-----------|--------|---------------------------|
|
||||
| K1 | Runtime: .NET 8.0 (LTS) | `restrictions.md` §Software | Pattern recommendations limited to .NET 8 features. No upgrade to .NET 9. |
|
||||
| K2 | Database: PostgreSQL 16 | `restrictions.md` §Software | C06 migration must be Postgres-compatible (`INSERT … ON CONFLICT … DO UPDATE`). |
|
||||
| K3 | Image processing: SixLabors.ImageSharp 3.1.11 | `restrictions.md` §Software | C14 (shared `TileGridStitcher`) keeps ImageSharp; no replacement library considered. |
|
||||
| K4 | Single-instance deployment | `restrictions.md` §Operational | C10 stays simple; no need for distributed counters. C09 idempotency handled in-process via DB unique constraints. |
|
||||
| K5 | No authentication middleware | `restrictions.md` §Environment | C03 sanitization is still needed (5xx leakage); C04 CORS hardening is still needed. Auth itself is out of scope. |
|
||||
| K6 | Tile versioning policy: year-based integer | `restrictions.md` line 23 | **CONFLICT with user-edited C06**: C06 removes year-based versioning. Roadmap must include a documentation update — the line in `restrictions.md` becomes "no version concept; latest row wins". User confirmed this direction in chat. |
|
||||
| K7 | Acceptance T1 — cache key includes `version` | `acceptance_criteria.md` T1 | **CONFLICT with C06**: T1 must be rewritten as "0 duplicate downloads for same (lat, lon, zoom_level, tile_size_meters); duplicates collapsed via DB unique constraint". User-confirmed direction. |
|
||||
| K8 | Acceptance RT2 — point types are `original` and `intermediate` | `acceptance_criteria.md` RT2 | **PRE-EXISTING DRIFT**: actual code uses `start`/`end`/`action`/`intermediate`. C17 enum work must reconcile — pick one canonical set and update either AC or code. Surfaced for user decision. |
|
||||
| K9 | Max ZIP archive size: 50 MB | `restrictions.md` line 22 | C11 (decompose `RouteProcessingService`) keeps the 50 MB cap; refactor must not weaken `RT5`. |
|
||||
| K10 | Smoke + unit suite must remain green | this run's Phase 0 goals | All changes verified at Phase 6. |
|
||||
|
||||
No `_docs/02_document/contracts/` directory exists, so there are no formal contract files to drift against. Module ownership is governed by `_docs/02_document/module-layout.md`, which the recent AZ-315 sync brought current.
|
||||
|
||||
## Current-State Analysis (by concern)
|
||||
|
||||
### Error handling
|
||||
- **Strengths**: most repository / downloader exceptions are caught, logged, and re-thrown. `GoogleMapsDownloaderV2.ExecuteWithRetryAsync` correctly distinguishes 429 / 5xx (retry) from 401 / 403 (don't retry).
|
||||
- **Weaknesses**:
|
||||
- **Silent suppression** in `RouteProcessingService.ExtractTileCoordinatesFromFilename` (empty `catch { }`) — `coderule.mdc` violation.
|
||||
- **9-way duplicated catch ladder** in `RegionService.ProcessRegionAsync` — single-reason-to-change rule.
|
||||
- **Information leakage** at every API endpoint: `Results.Problem(detail: ex.Message, statusCode: 500)` ships internal text to the client.
|
||||
- **Per-endpoint try/catch boilerplate** repeats six times in `Program.cs`.
|
||||
|
||||
### Single Responsibility
|
||||
- **Strengths**: project boundaries (post-`02-coupling`) are clean. The three `Services.*` siblings each own one concern.
|
||||
- **Weaknesses**:
|
||||
- `RouteProcessingService` (~750 LOC) does queue polling + region matching + CSV I/O + summary writing + image stitching + drawing + ZIP creation + cleanup.
|
||||
- `RouteService.CreateRouteAsync` is one 165-LOC method doing validation + interpolation + persistence + geofence-grid + region-request orchestration.
|
||||
- `Program.cs` hosts six DTOs and one Swagger filter at the bottom.
|
||||
|
||||
### Duplication
|
||||
- **Strengths**: very little duplication across DI extensions or repositories.
|
||||
- **Weaknesses**:
|
||||
- Haversine implemented twice (`GeoUtils.CalculateDistance` and `RouteProcessingService.CalculateDistance`).
|
||||
- CSV writer duplicated (`RegionService.GenerateCsvFileAsync` and `RouteProcessingService.GenerateRouteCsvAsync`).
|
||||
- Image stitching duplicated (region: red cross at center; route: rectangles + crosses) over the same primitive grid loop.
|
||||
- Magic `0.0001` lat/lon tolerance duplicated (RouteService geofence check; downloader existing-tile match).
|
||||
- Per-call `HttpClient` configuration duplicated across three call sites in `GoogleMapsDownloaderV2`.
|
||||
|
||||
### Magic numbers / strings
|
||||
- 5-minute region timeout, 200 m max point spacing, 5 s polling, retry 1/30 s base/max, 256 px tile, 50 MB cap implied — all hardcoded.
|
||||
- Status strings (`queued`/`processing`/`completed`/`failed`) and point-type strings (`start`/`end`/`action`/`intermediate`) are bare literals across multiple files.
|
||||
|
||||
### Configuration / cancellation
|
||||
- Most async paths accept `CancellationToken`, but at least three known sites do not propagate it (e.g., `Program.cs:GetTileByLatLon` → `DownloadAndStoreSingleTileAsync` drops `httpContext.RequestAborted`).
|
||||
- `_serviceProvider`-based scope creation in `RouteProcessingService` masks the real dependency on `IRegionService`.
|
||||
|
||||
### Tooling
|
||||
- No `.editorconfig`-driven formatter, no `dotnet format` in CI, no Roslyn analyzers beyond defaults, no Coverlet for coverage. Style and basic correctness drift through unchecked.
|
||||
|
||||
## Modern-Approach Survey
|
||||
|
||||
For each concern, the right answer is **a built-in .NET 8 feature**, not a new library. No replacement library/SDK is being introduced by the structural changes. The only exception is C19, which adds two well-known **tooling** packages (analyzer + coverage collector). Therefore the `context7`-MVE protocol applies only to C19; the structural changes use existing capabilities of the runtime and existing project libraries.
|
||||
|
||||
| Concern | Current pattern | .NET 8 idiom (selected) | Adopted in change |
|
||||
|---------|-----------------|-------------------------|-------------------|
|
||||
| Endpoint exception leakage | per-endpoint try/catch returning `Results.Problem(detail: ex.Message)` | `IExceptionHandler` (NET8) registered via `builder.Services.AddExceptionHandler<>()` + `app.UseExceptionHandler()` returns sanitized `ProblemDetails`; correlation ID via `Activity.Current?.Id` | C03 |
|
||||
| Service-locator | constructor-inject `IServiceProvider` and `CreateScope()` per loop | constructor-inject the actual dependency (`IRegionService`); for true scoped consumption use `IServiceScopeFactory` instead of `IServiceProvider` | C08 |
|
||||
| Status / type magic strings | bare strings | `enum` + Dapper `SqlMapper.AddTypeHandler<MyEnum>(new EnumStringTypeHandler<MyEnum>())` to keep DB schema unchanged | C17 |
|
||||
| Config sprawl | `private const` literals | `IOptions<ProcessingConfig>` / `IOptions<MapConfig>` (already in use elsewhere) | C18 |
|
||||
| HttpClient configuration | `_httpClientFactory.CreateClient()` + per-call `User-Agent` setup | `builder.Services.AddHttpClient("GoogleMapsTiles", c => { c.DefaultRequestHeaders.UserAgent.ParseAdd(USER_AGENT); c.Timeout = …; })` | C21 |
|
||||
| Region 9-way catch ladder | one `catch` per exception type with shared body | one `catch (Exception ex)` + an exception-classifier helper returning a typed `RegionFailureCategory` enum used to build the error message | C07 |
|
||||
| Existing-tile lookup O(N²) | linear `FirstOrDefault` per cell | `HashSet<(int x, int y, int z)>` built once before the loop | C22 |
|
||||
| Idempotency for caller GUIDs | `INSERT` + 500 on duplicate | DB unique constraint + repository upsert pattern (`INSERT … ON CONFLICT (id) DO NOTHING` + read-back) returning 200 with the existing resource | C09 |
|
||||
|
||||
## Constraint-Fit Table (per change)
|
||||
|
||||
For each change ID, status is one of: `Selected`, `Rejected`, `Experimental only`, `Needs user decision`.
|
||||
|
||||
| ID | Title (short) | Pinned approach | Constraint conflicts | Status |
|
||||
|----|---------------|-----------------|---------------------|--------|
|
||||
| C01 | Fix null logger to migrator | `GetRequiredService<ILogger<DatabaseMigrator>>()` | None | Selected |
|
||||
| C02 | Remove empty catch in tile-coord parser | log+rethrow narrow exception types | None | Selected |
|
||||
| C03 | Sanitize 500 responses | `IExceptionHandler` + correlation ID | None | Selected |
|
||||
| C04 | Strict CORS by default | fail-fast in Production if `AllowedOrigins` empty | None | Selected |
|
||||
| C05 | Stub endpoints return 501 | `Results.StatusCode(501)` | None | Selected |
|
||||
| C06 | Drop `Version` concept; latest tile wins | repository upsert with unique `(lat, lon, zoom, size)` | **K6** (`restrictions.md` line 23), **K7** (T1) — both require doc updates as part of the change | Selected (user-confirmed; doc updates included in ticket) |
|
||||
| C07 | Consolidate 9-way catch ladder | one catch + classifier | None | Selected |
|
||||
| C08 | Replace `IServiceProvider` with `IRegionService` | direct DI of singleton | None | Selected |
|
||||
| C09 | Idempotency contract for caller GUIDs | upsert + 200 on duplicate | None (T1 is unaffected; A1 still 200) | Selected |
|
||||
| C10 | Remove counters from `RegionRequestQueue` | delete fields | None | Selected |
|
||||
| C11 | Decompose `RouteProcessingService` | extract 6 collaborators | K9 (50 MB cap) preserved by `TilesZipBuilder` | Selected |
|
||||
| C12 | Decompose `RouteService.CreateRouteAsync` | extract validator + builder + grid + mapper | None | Selected |
|
||||
| C13 | Consolidate Haversine + filename parser | move to `GeoUtils`/`StorageConfig` | None | Selected |
|
||||
| C14 | Shared `TileGridStitcher` | new class, ImageSharp-based | K3 (ImageSharp pinned) preserved | Selected |
|
||||
| C15 | Shared `TileCsvWriter` | new class | None | Selected |
|
||||
| C16 | Move inline DTOs out of `Program.cs` | move to `Common/DTO/` | None | Selected |
|
||||
| C17 | Status / point-type enums | enum + Dapper type handler; DB shape unchanged | **K8** (RT2 drift) — must pick canonical names; needs user decision (see below) | Needs user decision |
|
||||
| C18 | Magic numbers → config | `ProcessingConfig` / `MapConfig` defaults preserve current values | None | Selected |
|
||||
| C19 | Add formatter + analyzers + coverage | `Microsoft.CodeAnalysis.NetAnalyzers`, `coverlet.collector` | None — see C19 evidence below | Selected |
|
||||
| C20 | Clarify `MapsVersion` semantics | drop alongside C06, or keep as forensic label | Coupled with C06; user already chose "drop" direction | Selected |
|
||||
| C21 | Typed `HttpClient` for Google Maps | `AddHttpClient("GoogleMapsTiles", …)` | None | Selected |
|
||||
| C22 | O(N) existing-tile lookup | `HashSet<(x,y,z)>` | None | Selected |
|
||||
|
||||
### Items needing user decision
|
||||
|
||||
- **C17 — point-type canonical names (resolves K8 drift)**:
|
||||
- Option α: Keep code's `Start`, `End`, `Action`, `Intermediate`. Update `acceptance_criteria.md` RT2.
|
||||
- Option β: Adopt AC's `Original`, `Intermediate`. Rewrite the four code sites that emit string literals.
|
||||
- Recommendation: α — the code is more expressive (`Start`/`End`/`Action` carry more meaning than `Original`), and there are 4 emit sites vs. one AC line to edit.
|
||||
|
||||
## C19 — replacement library evidence
|
||||
|
||||
C19 adds two **tooling** packages. Both are single-mode build-time tools, not multi-mode runtime SDKs, so the full per-mode MVE protocol doesn't apply. Recorded here for the audit trail.
|
||||
|
||||
| Package | Version policy | Mode | Evidence |
|
||||
|---------|---------------|------|----------|
|
||||
| `Microsoft.CodeAnalysis.NetAnalyzers` | Take latest 8.x (LTS-aligned) | Roslyn analyzer at build time; reports `CA*` diagnostics | Microsoft-published, included by default in .NET 8 SDK builds; making it explicit pins the version. Documentation: <https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/> |
|
||||
| `coverlet.collector` | Take latest 6.x | DataCollector loaded by `dotnet test`; emits `coverage.cobertura.xml` | Standard .NET coverage collector; declared once on each test csproj. Documentation: <https://github.com/coverlet-coverage/coverlet> |
|
||||
|
||||
No multi-mode confusion: each package has one production use case (analyze at build time / collect coverage on test runs). No `mve_evidence.md` is produced.
|
||||
|
||||
## Prioritized Recommendations (input to roadmap)
|
||||
|
||||
1. **Critical & cheap first** (small risk, big correctness/security win): C01, C02, C03, C04, C05, C10.
|
||||
2. **High-value correctness** (one bigger or more invasive change each): C06 (with migration), C09 (idempotency), C07 (catch ladder).
|
||||
3. **Structural cleanup** (medium-risk, medium-cost): C11, C12, C13, C14, C15, C16, C08.
|
||||
4. **Typing & config hygiene**: C17, C18, C20.
|
||||
5. **Polish / tooling / micro-perf**: C19, C21, C22.
|
||||
|
||||
The roadmap document (`refactoring_roadmap.md`) maps these into three execution phases and surfaces the C17 / K8 question for the user.
|
||||
|
||||
## Self-Verification (Phase 2a)
|
||||
|
||||
- [x] Project Constraint Matrix extracted from `restrictions.md` and `acceptance_criteria.md`.
|
||||
- [x] Each change in `list-of-changes.md` has a constraint-fit row.
|
||||
- [x] No recommendation introduces a new library/SDK in a multi-mode runtime context. C19's two tooling adds are documented; full per-mode MVE not applicable (single-mode build-time tools).
|
||||
- [x] Conflicts surfaced explicitly: K6/K7 (C06 ↔ doc updates), K8 (C17 ↔ point-type canonical names — needs user decision).
|
||||
- [x] Recommendations grounded in actual code (file:line references in `list-of-changes.md`).
|
||||
- [x] No paraphrased capability claims for new libraries — there are no new runtime libraries to claim about.
|
||||
Reference in New Issue
Block a user