[AZ-500] .NET 8 -> .NET 10 migration

Coordinated cross-cutting bump: 9 csproj TFMs net8.0 -> net10.0;
global.json sdk.version 8.0.0 -> 10.0.0; all Dockerfiles + scripts/
+ .woodpecker on mcr.microsoft.com/dotnet/{sdk,aspnet,runtime}:10.0;
all Microsoft.AspNetCore.* (8.0.25) and Microsoft.Extensions.* (9.0.10)
packages -> 10.0.7. Serilog.AspNetCore retained at 8.0.3 (10.0.0
requires Serilog.Sinks.File >= 7.0.0; out of AZ-500 scope per "no
unrelated package bumps") -- documented in AGENTS.md. Swashbuckle
9.x bumped to 10.1.7 to track Microsoft.OpenApi 2.x; Program.cs +
ParameterDescriptionFilter.cs refactored for the 2.x namespace
(Microsoft.OpenApi), OpenApiSecuritySchemeReference, JsonSchemaType
enum, and IOpenApiSchema dictionary properties. Fixed implicit AC-5
prereq: scripts/run-performance-tests.sh PERF_DLL path bin/Release/
net8.0 -> net10.0. Docs sync: architecture.md + AGENTS.md.

ACs verified: AC-1..AC-4 + AC-7 + AC-8 by grep + build; AC-6 by
./scripts/run-tests.sh --full (271/271 unit tests + full integration
suite green); AC-5 short bootstrap-smoke (PERF_REPEAT_COUNT=2
PERF_UAV_BATCH_SIZE=2) succeeded at the bootstrap step (no exit 3),
PT-01..PT-07 PASS. PT-08 surfaced a pre-existing grep-pipefail bug
in run-performance-tests.sh:417 -- not an SDK problem; recorded as
follow-up in the perf-cycle3 leftover. Code review verdict:
PASS_WITH_WARNINGS (2 Medium deferred per scope discipline:
WithOpenApi ASPDEPR002 deprecation x8, CS8604 nullable in
ParameterDescriptionFilter.cs; both targeted at follow-up PBIs).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-12 05:28:01 +03:00
parent c0f004d2c9
commit 813136326f
25 changed files with 317 additions and 90 deletions
@@ -0,0 +1,95 @@
# Batch Report
**Batch**: 1 (cycle 4)
**Tasks**: AZ-500_dotnet10_migration
**Date**: 2026-05-12
## Task Results
| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|------|--------|---------------|-------|-------------|--------|
| AZ-500_dotnet10_migration | Done | 18 source/config + 2 doc + 1 leftover | 271/271 unit + full integration suite PASS | 8/8 ACs covered | 2 Medium (deferred, scope-protected) + 1 Low scope-doc |
## Files Modified (21 total)
**Build / project files (12)**:
- `global.json`
- `SatelliteProvider.Api/SatelliteProvider.Api.csproj`
- `SatelliteProvider.Common/SatelliteProvider.Common.csproj`
- `SatelliteProvider.DataAccess/SatelliteProvider.DataAccess.csproj`
- `SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj`
- `SatelliteProvider.Services.RegionProcessing/SatelliteProvider.Services.RegionProcessing.csproj`
- `SatelliteProvider.Services.RouteManagement/SatelliteProvider.Services.RouteManagement.csproj`
- `SatelliteProvider.Services.TileDownloader/SatelliteProvider.Services.TileDownloader.csproj`
- `SatelliteProvider.TestSupport/SatelliteProvider.TestSupport.csproj`
- `SatelliteProvider.Tests/SatelliteProvider.Tests.csproj`
- `SatelliteProvider.Api/Dockerfile`
- `SatelliteProvider.IntegrationTests/Dockerfile`
**Source code (2)**:
- `SatelliteProvider.Api/Program.cs` — Microsoft.OpenApi 2.x refactor (namespace + `OpenApiSecuritySchemeReference` + `JsonSchemaType` + `IOpenApiSchema` properties)
- `SatelliteProvider.Api/Swagger/ParameterDescriptionFilter.cs` — namespace update
**Scripts / CI (3)**:
- `scripts/run-tests.sh` — 3× `mcr.microsoft.com/dotnet/sdk:8.0``:10.0`
- `scripts/run-performance-tests.sh``bin/Release/net8.0/``bin/Release/net10.0/` (necessary for AC-5)
- `.woodpecker/01-test.yml``mcr.microsoft.com/dotnet/sdk:8.0``:10.0`
**Documentation (2)**:
- `_docs/02_document/architecture.md` — Tech Stack table + Architecture Vision prose updated to .NET 10
- `AGENTS.md` — Tech Stack section + Key packages list updated; Serilog.AspNetCore 8.0.3 fallback documented
**Process (2)**:
- `_docs/_autodev_state.md` — phase tracking
- `_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md` — replay #2 result added (bootstrap clean; PT-08 grep-pipefail bug surfaced as new follow-up)
## Per-Task Notes
### AZ-500: .NET 8 → .NET 10 migration
**Implementation order** (single batch, sequential):
1. Inventory current state: 9 csproj files on `net8.0`; `global.json sdk.version=8.0.0`; 4 Docker base image refs; 19 `Microsoft.AspNetCore.*` / `Microsoft.Extensions.*` package refs.
2. Bump `global.json` SDK pin to `10.0.0` / `latestMinor`.
3. Bump every `<TargetFramework>` from `net8.0` to `net10.0`.
4. Bump all `Microsoft.AspNetCore.*` (8.0.25) and `Microsoft.Extensions.*` (9.0.10) packages to `10.0.7` (highest 10.0 patch on NuGet at run time).
5. Initial attempt with `Serilog.AspNetCore 10.0.0` failed `dotnet restore` with NU1605 (transitive dep `Serilog.Sinks.File >= 7.0.0` conflicts with the project's pinned `6.0.0`). Per Risk #4 mitigation + "no unrelated package bumps" constraint, reverted Serilog.AspNetCore to `8.0.3` and documented in AGENTS.md.
6. Bump Docker base images in 2 Dockerfiles (`aspnet:10.0`, `sdk:10.0`, `runtime:10.0`).
7. Bump CI/script images: `scripts/run-tests.sh` (3 refs) + `.woodpecker/01-test.yml` (1 ref).
8. Initial `Microsoft.AspNetCore.OpenApi 10.0.7` upgrade pulled `Microsoft.OpenApi 2.x` which removed the `Microsoft.OpenApi.Models` namespace. Compilation failed (CS0234, CS0246, CS7069) in `Program.cs` and `ParameterDescriptionFilter.cs`. User chose: bump Swashbuckle to 10.1.7 (which targets `Microsoft.OpenApi 2.4.1`) + refactor source to the 2.x API surface (`OpenApiSecuritySchemeReference`, `JsonSchemaType`, `IOpenApiSchema` dictionary properties, `using Microsoft.OpenApi;`).
9. After build clean, found one remaining compile error: `OpenApiSecurityRequirement` collection initializer expected `List<string>` and Swashbuckle's `AddSecurityRequirement` expected `Func<OpenApiDocument, OpenApiSecurityRequirement>`. Fixed by wrapping in lambda + `new List<string>()`.
10. `./scripts/run-tests.sh --full` → exit 0 (271/271 unit + full integration suite green).
11. `docker compose up -d --build` → API healthy on `:18980` (anonymous request → 401, swagger → 200).
12. Found `scripts/run-performance-tests.sh:49` had a hardcoded `bin/Release/net8.0/` path that would fail post-migration; bumped to `net10.0/`.
13. AC-5 perf-bootstrap smoke (`PERF_REPEAT_COUNT=2 PERF_UAV_BATCH_SIZE=2`): build succeeded (no exit 3 — bootstrap clean), JWT mint OK, PT-01..PT-07 PASS. PT-08 crashed at `rejected=$(grep -o ... | wc -l | tr -d ' ')` because `grep -o` exits 1 on zero matches and `set -o pipefail` propagates. Pre-existing script bug, surfaced now because cycle 3 never reached PT-08. Documented in the perf-leftover file as a new follow-up; AC-5's bootstrap-only gate is satisfied.
14. AC-8 docs verified via `grep` over `architecture.md` and `AGENTS.md` — both reflect `.NET 10` / `ASP.NET Core 10`.
**Unresolved questions**: none.
**Blockers**: none.
## AC Test Coverage
All 8 ACs covered (full trace in `reviews/batch_01_cycle4_review.md` → "AC Verification Trace"). Verification mix matches the task spec's Blackbox Tests table: AC-1/2/3/4/8 by `grep`, AC-6 by `./scripts/run-tests.sh --full`, AC-7 by `docker compose build`, AC-5 by manual smoke run.
## Code Review Verdict
**PASS_WITH_WARNINGS** — see `_docs/03_implementation/reviews/batch_01_cycle4_review.md`.
Findings (all deferred per "scope discipline"; see review report Findings section + Recommended Follow-Up PBIs):
1. F1 Medium — `WithOpenApi(...)` deprecation (ASPDEPR002) at 8 callsites in `Program.cs`. Migration intentionally preserved API surface per Risk #2.
2. F2 Medium — CS8604 nullable warning in `ParameterDescriptionFilter.cs:25` from Microsoft.OpenApi 2.x stricter annotations.
3. F3 Low — `scripts/run-performance-tests.sh:49` path fix was implicit in AC-5 but not in `Scope/Included`. Necessary, accepted.
## Auto-Fix Attempts
0 (no FAIL findings; PASS_WITH_WARNINGS proceeds without auto-fix).
## Stuck Agents
None.
## Next Batch
All AZ-500 work complete. Next: implement-skill Step 13 (archive task to `_docs/02_tasks/done/`) → Step 15 (Product Implementation Completeness Gate) → Step 16 (full test run is already green; record handoff to test-run skill in autodev Step 11) → autodev Step 11 (Run Tests) → Step 12 (Test-Spec Sync) → ...
@@ -0,0 +1,84 @@
# Code Review Report
**Batch**: 1 (cycle 4) — `AZ-500_dotnet10_migration`
**Date**: 2026-05-12
**Verdict**: PASS_WITH_WARNINGS
## Findings
| # | Severity | Category | File:Line | Title |
|---|----------|----------|-----------|-------|
| 1 | Medium | Maintainability | SatelliteProvider.Api/Program.cs:174219 | `WithOpenApi(...)` extension is deprecated in ASP.NET Core 10 (ASPDEPR002) — slated for removal |
| 2 | Medium | Maintainability | SatelliteProvider.Api/Swagger/ParameterDescriptionFilter.cs:25 | CS8604 — `parameter.Name` is nullable in Microsoft.OpenApi 2.x; missing null guard |
| 3 | Low | Scope | scripts/run-performance-tests.sh:49 | Fix to `bin/Release/netX.0/` path was implicit in AC-5 but not explicitly listed in task spec's `Scope/Included` |
### Finding Details
**F1: `WithOpenApi(...)` extension is deprecated in ASP.NET Core 10 (ASPDEPR002)** (Medium / Maintainability)
- Location: `SatelliteProvider.Api/Program.cs:174,178,182,187,199,207,211,219` (8 endpoint mappings)
- Description: `OpenApiEndpointConventionBuilderExtensions.WithOpenApi<TBuilder>(TBuilder, Func<OpenApiOperation, OpenApiOperation>)` is marked obsolete with diagnostic ID `ASPDEPR002` ("WithOpenApi is deprecated and will be removed in a future release"). The build emits 8 deprecation warnings — one per endpoint that calls `.WithOpenApi(op => new(op) { Summary = ... })`. The migration intentionally kept the existing surface to minimise behavioural risk (Risk #2 in AZ-500 task spec), but this is a known follow-up. See https://aka.ms/aspnet/deprecate/002 for the recommended replacement (`.WithSummary(...)` / `.WithDescription(...)` / `.WithName(...)` minimal-API metadata extensions).
- Suggestion: Open a follow-up PBI to migrate the 8 callsites to the new minimal-API metadata extensions (`WithSummary`, `WithDescription`, `WithName`, `WithTags`). Out of AZ-500 scope per `coderule.mdc` "scope discipline" — AZ-500's contract is "preserve behaviour during runtime/SDK migration, do not adopt new APIs."
- Task: AZ-500
**F2: CS8604 — `parameter.Name` is nullable in Microsoft.OpenApi 2.x** (Medium / Maintainability)
- Location: `SatelliteProvider.Api/Swagger/ParameterDescriptionFilter.cs:25`
```csharp
if (parameterDescriptions.TryGetValue(parameter.Name, out var description))
```
- Description: Microsoft.OpenApi 2.x added stricter nullable annotations — `OpenApiParameter.Name` is now declared `string?`, while `Dictionary<string, string>.TryGetValue(string key, ...)` requires non-null. The build emits one CS8604 warning. At runtime any parameter without a `Name` would NRE here, although in practice every Swashbuckle-emitted parameter has a non-null `Name`. Trivial defensive fix.
- Suggestion (one-line, can land in this batch if user prefers):
```csharp
if (parameter.Name is { } name && parameterDescriptions.TryGetValue(name, out var description))
```
Otherwise defer to a follow-up nullable-cleanup PBI.
- Task: AZ-500
**F3: `scripts/run-performance-tests.sh:49` path fix was implicit, not explicitly listed in task scope** (Low / Scope)
- Location: `scripts/run-performance-tests.sh:49` — `PERF_DLL=".../bin/Release/net8.0/..."` → `.../bin/Release/net10.0/...`
- Description: AZ-500's `Scope/Included` lists `scripts/run-tests.sh` and `.woodpecker/01-test.yml` for image bumps but does NOT explicitly mention `scripts/run-performance-tests.sh`. However, AC-5 requires the perf-bootstrap to succeed; without this path fix, `dotnet "$PERF_DLL"` would always fail (file not found at the old path). This is necessary scope creep, not opportunistic — AC-5 cannot pass without it. Recorded as Low/Scope rather than Spec-Gap because the fix is required by AC-5's letter.
- Suggestion: Accept as in-scope. No code change needed — this finding is documentary.
- Task: AZ-500
## Phase Summary
| Phase | Result | Notes |
|-------|--------|-------|
| 1. Context Loading | OK | Task spec read; 21 changed files mapped to AZ-500 |
| 2. Spec Compliance | PASS | All 8 ACs satisfied (verified by grep + build + test run + manual smoke) |
| 3. Code Quality | PASS_WITH_WARNINGS | F1 + F2 flagged; otherwise pure version-bump diff |
| 4. Security Quick-Scan | PASS | No new code paths; JWT validation contract preserved (AC-6 covers AZ-487/AZ-494 regression) |
| 5. Performance Scan | PASS | No new logic; .NET 10 ASP.NET pipeline expected to improve, not regress (gated at Step 15) |
| 6. Cross-Task Consistency | N/A | Single-task batch |
| 7. Architecture Compliance | PASS | No layer-direction violations; no new cross-component imports; `using Microsoft.OpenApi;` is external |
## AC Verification Trace
| AC | Verification | Result |
|----|-------------|--------|
| AC-1 (every csproj net10.0) | `grep -r "<TargetFramework>" --include="*.csproj"` → 9/9 net10.0, 0 net8.0 | PASS |
| AC-2 (global.json sdk.version=10.0.0) | `cat global.json` → `"version": "10.0.0", "rollForward": "latestMinor"` | PASS |
| AC-3 (all docker images :10.0) | `grep -rE "mcr.microsoft.com/dotnet/" --include="*Dockerfile" --include="*.yml" --include="*.sh"` → 7/7 references on `:10.0` | PASS |
| AC-4 (M.AspNetCore.* / M.Extensions.* on 10.0.7) | `grep` over csprojs → 19 references on `10.0.7`; Serilog.AspNetCore at `8.0.3` (documented per Risk #4 in AGENTS.md:244) | PASS |
| AC-5 (perf-bootstrap succeeds, leftover updated) | `PERF_REPEAT_COUNT=2 PERF_UAV_BATCH_SIZE=2 ./scripts/run-performance-tests.sh` → exit code 1 (NOT 3 — bootstrap succeeded, build OK, JWT mint OK, PT-01..PT-07 PASS, PT-08 crashed on pre-existing grep-pipefail bug). Leftover file updated with non-SDK failure detail. Per AZ-500 Constraints last bullet, leftover stays in place — closed by Step 15 of cycle 4 after the script grep fix lands. | PASS |
| AC-6 (`./scripts/run-tests.sh --full` green) | Step 0 dotnet format → PASS; Step 1 unit tests → 271/271 passed in 4.2s; Step 2 integration tests → docker-compose with `--abort-on-container-exit --exit-code-from integration-tests` exited 0; visible PASSED markers cover JWT/UAV/Tile/Region/Route + 4 complex/extended route scenarios | PASS |
| AC-7 (`docker-compose build` succeeds, no downgrade warnings) | Both `run-tests.sh` Step 2 build AND a separate `docker compose up -d --build` succeeded; only warnings emitted are F1 (ASPDEPR002) + F2 (CS8604) — neither is "package downgrade", "framework conflict", or "missing base image" | PASS |
| AC-8 (docs reflect .NET 10) | `_docs/02_document/architecture.md:5` ".NET 10", line 67 Tech Stack table "10.0"; `AGENTS.md:9` ".NET 10", lines 240244 packages list updated with Serilog fallback note | PASS |
## Auto-Fix Eligibility
Per `implement/SKILL.md` Step 10 matrix:
- F1 (Medium / Maintainability) → eligible, but **deferred** — fix would expand scope beyond AZ-500's "preserve behaviour" contract; needs a follow-up PBI to migrate 8 endpoints to the new metadata API. Surfaced for user decision.
- F2 (Medium / Maintainability) → eligible auto-fix. Trivial 1-line null guard. User can opt to fold into this batch or defer to a nullable-cleanup PBI.
- F3 (Low / Scope) → no fix needed, documentary only.
## Verdict Logic
- 0 Critical, 0 High → no FAIL gate triggered.
- 2 Medium + 1 Low → **PASS_WITH_WARNINGS**.
## Recommended Follow-Up PBIs (out of AZ-500 scope)
1. **`scripts/run-performance-tests.sh:416-417` grep-pipefail fix** (1 point) — replace `grep -o ... | wc -l` with `grep -c ... || true`; unblocks Step 15 perf full-run + closes the perf-cycle3 leftover. See `_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md` for the trace.
2. **Migrate `WithOpenApi(...)` callsites to ASP.NET Core 10 minimal-API metadata extensions** (3 points) — 8 callsites in `Program.cs`; covers F1.
3. **Microsoft.OpenApi 2.x nullable cleanup** (1 point) — covers F2 plus any other nullable annotations exposed by the migration (none others detected in this batch, but a project-wide grep is recommended).