mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 05:41:14 +00:00
[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:
@@ -7,7 +7,7 @@ labels:
|
||||
|
||||
steps:
|
||||
- name: unit-tests
|
||||
image: mcr.microsoft.com/dotnet/sdk:8.0
|
||||
image: mcr.microsoft.com/dotnet/sdk:10.0
|
||||
commands:
|
||||
- dotnet restore SatelliteProvider.sln
|
||||
- dotnet test SatelliteProvider.Tests/SatelliteProvider.Tests.csproj --no-restore --configuration Release --logger "console;verbosity=normal" --logger "trx;LogFileName=test-results.trx" --results-directory /app/test-results
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
## System Overview
|
||||
|
||||
This is a .NET 8.0 ASP.NET Web API service that downloads, stores, and manages satellite imagery tiles from Google Maps. The service supports region-based tile requests, route planning with intermediate points, and geofencing capabilities.
|
||||
This is a .NET 10 ASP.NET Web API service that downloads, stores, and manages satellite imagery tiles from Google Maps. The service supports region-based tile requests, route planning with intermediate points, and geofencing capabilities.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **.NET 8.0** with ASP.NET Core Web API
|
||||
- **.NET 10** with ASP.NET Core Web API
|
||||
- **PostgreSQL** database (via Docker)
|
||||
- **Dapper** for data access (ORM)
|
||||
- **DbUp** for database migrations
|
||||
@@ -236,10 +236,12 @@ Development defaults:
|
||||
|
||||
## Dependencies and Versions
|
||||
|
||||
Key packages (all .NET 8.0):
|
||||
- Microsoft.AspNetCore.OpenApi 8.0.21
|
||||
Key packages (all .NET 10):
|
||||
- Microsoft.AspNetCore.OpenApi 10.0.7
|
||||
- Microsoft.AspNetCore.Authentication.JwtBearer 10.0.7
|
||||
- Microsoft.Extensions.* 10.0.7
|
||||
- Swashbuckle.AspNetCore 6.6.2
|
||||
- Serilog.AspNetCore 8.0.3
|
||||
- Serilog.AspNetCore 8.0.3 (intentional — 10.0.0 requires Serilog.Sinks.File ≥ 7.0.0; bumping Serilog.Sinks.File is out of AZ-500 scope per "no unrelated package bumps")
|
||||
- SixLabors.ImageSharp 3.1.11
|
||||
- Newtonsoft.Json 13.0.4
|
||||
- Dapper (check DataAccess csproj)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 8080
|
||||
EXPOSE 8081
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["SatelliteProvider.Api/SatelliteProvider.Api.csproj", "SatelliteProvider.Api/"]
|
||||
COPY ["SatelliteProvider.Common/SatelliteProvider.Common.csproj", "SatelliteProvider.Common/"]
|
||||
|
||||
@@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using SatelliteProvider.Api;
|
||||
using SatelliteProvider.Api.Authentication;
|
||||
@@ -108,36 +108,29 @@ builder.Services.AddSwaggerGen(c =>
|
||||
Description = "JWT Authorization header using the Bearer scheme. Example: 'Bearer {token}'"
|
||||
});
|
||||
|
||||
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
c.AddSecurityRequirement(_ => new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
Array.Empty<string>()
|
||||
new OpenApiSecuritySchemeReference("Bearer"),
|
||||
new List<string>()
|
||||
}
|
||||
});
|
||||
|
||||
c.MapType<UavTileBatchUploadRequest>(() => new OpenApiSchema
|
||||
{
|
||||
Type = "object",
|
||||
Properties = new Dictionary<string, OpenApiSchema>
|
||||
Type = JsonSchemaType.Object,
|
||||
Properties = new Dictionary<string, IOpenApiSchema>
|
||||
{
|
||||
["metadata"] = new()
|
||||
["metadata"] = new OpenApiSchema
|
||||
{
|
||||
Type = "string",
|
||||
Type = JsonSchemaType.String,
|
||||
Description = "JSON document `{ \"items\": [ { \"latitude\", \"longitude\", \"tileZoom\", \"tileSizeMeters\", \"capturedAt\" } ] }` where item ordinal index aligns with the matching file in `files`."
|
||||
},
|
||||
["files"] = new()
|
||||
["files"] = new OpenApiSchema
|
||||
{
|
||||
Type = "array",
|
||||
Type = JsonSchemaType.Array,
|
||||
Description = "UAV tile JPEG files in the same order as `metadata.items`.",
|
||||
Items = new OpenApiSchema { Type = "string", Format = "binary" }
|
||||
Items = new OpenApiSchema { Type = JsonSchemaType.String, Format = "binary" }
|
||||
}
|
||||
},
|
||||
Required = new HashSet<string> { "metadata", "files" }
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.25" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.25"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.7"/>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace SatelliteProvider.Api.Swagger;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
@@ -10,8 +10,8 @@
|
||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
||||
<PackageReference Include="Npgsql" Version="9.0.2" />
|
||||
<PackageReference Include="dbup-postgresql" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj", "SatelliteProvider.IntegrationTests/"]
|
||||
COPY ["SatelliteProvider.TestSupport/SatelliteProvider.TestSupport.csproj", "SatelliteProvider.TestSupport/"]
|
||||
@@ -10,7 +10,7 @@ RUN dotnet build "SatelliteProvider.IntegrationTests.csproj" -c Release -o /app/
|
||||
FROM build AS publish
|
||||
RUN dotnet publish "SatelliteProvider.IntegrationTests.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS final
|
||||
FROM mcr.microsoft.com/dotnet/runtime:10.0 AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "SatelliteProvider.IntegrationTests.dll"]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
+5
-5
@@ -1,16 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.7" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
+5
-5
@@ -1,16 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.7" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
+5
-5
@@ -1,16 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.7" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="8.8.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Architecture Vision
|
||||
|
||||
Satellite Provider is a self-hosted .NET 8.0 backend service that pre-downloads, caches, and composites Google Maps satellite imagery for offline use. It runs as a single containerized monolith with PostgreSQL, processing requests asynchronously via in-process queues. The dominant pattern is a layered architecture (API → Services → DataAccess → PostgreSQL) with background hosted services for long-running work.
|
||||
Satellite Provider is a self-hosted .NET 10 backend service that pre-downloads, caches, and composites Google Maps satellite imagery for offline use. It runs as a single containerized monolith with PostgreSQL, processing requests asynchronously via in-process queues. The dominant pattern is a layered architecture (API → Services → DataAccess → PostgreSQL) with background hosted services for long-running work.
|
||||
|
||||
**Components & responsibilities** (each owns its own `.csproj` since AZ-309):
|
||||
- **Common** (`SatelliteProvider.Common`) — Shared contracts: DTOs, service interfaces, common exceptions, configuration models, geospatial math
|
||||
@@ -64,7 +64,7 @@ The N-source storage contract is authoritative in `_docs/02_document/contracts/d
|
||||
| Layer | Technology | Version | Rationale |
|
||||
|-------|-----------|---------|-----------|
|
||||
| Language | C# | 12.0 | .NET ecosystem, strong typing |
|
||||
| Framework | ASP.NET Core (Minimal API) | 8.0 | Lightweight HTTP hosting |
|
||||
| Framework | ASP.NET Core (Minimal API) | 10.0 | Lightweight HTTP hosting |
|
||||
| Database | PostgreSQL | 15+ | Reliable RDBMS, spatial-friendly |
|
||||
| ORM | Dapper | latest | Micro-ORM, raw SQL control |
|
||||
| Migrations | DbUp | latest | Simple SQL-file-based schema migrations |
|
||||
|
||||
@@ -90,7 +90,7 @@ Source: cycle-3 perf-harness leftover replay surfaced the host SDK / project SDK
|
||||
|
||||
| Task | Title | Depends On | Points | Status |
|
||||
|------|-------|-----------|--------|--------|
|
||||
| AZ-500 | .NET 8 → .NET 10 migration (TFM + SDK pin + Docker images + CI images + Microsoft.AspNetCore.* + Microsoft.Extensions.* + Serilog.AspNetCore) | — | 5 | To Do |
|
||||
| AZ-500 | .NET 8 → .NET 10 migration (TFM + SDK pin + Docker images + CI images + Microsoft.AspNetCore.* + Microsoft.Extensions.* + Serilog.AspNetCore) | — | 5 | Done (In Testing) |
|
||||
|
||||
## Execution Order
|
||||
|
||||
|
||||
@@ -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:174–219 | `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 240–244 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).
|
||||
@@ -6,8 +6,8 @@ step: 10
|
||||
name: Implement
|
||||
status: in_progress
|
||||
sub_step:
|
||||
phase: 1
|
||||
name: prerequisite-checks
|
||||
phase: 15
|
||||
name: product-completeness-gate
|
||||
detail: ""
|
||||
retry_count: 0
|
||||
cycle: 4
|
||||
|
||||
@@ -1,48 +1,101 @@
|
||||
# Leftover — Cycle 3 perf harness execution
|
||||
|
||||
**Timestamp**: 2026-05-12T01:11:00Z (last replay attempt; original deferral at 2026-05-12T00:00:00Z)
|
||||
**Timestamp**: 2026-05-12T02:25:00Z (replay #2 — post AZ-500 .NET 10 migration; original deferral 2026-05-12T00:00:00Z)
|
||||
**Reason for deferral**: User skipped the Step 15 (Performance Test) gate of cycle 3. Per `meta-rule.mdc`, performance tests require explicit approval; a skipped question is not approval. Defaulted to skip + record-as-leftover to avoid blocking cycle-3 progress through Steps 16-17.
|
||||
|
||||
## Replay attempt — 2026-05-12 (cycle 4 /autodev start)
|
||||
## Replay attempt #1 — 2026-05-12T01:11:00Z (cycle 4 /autodev start, pre-migration)
|
||||
|
||||
User picked A (run perf harness now). Stack came up cleanly via `docker-compose up -d --build`. Perf script `scripts/run-performance-tests.sh` failed at the bootstrap step (`dotnet build SatelliteProvider.IntegrationTests` for the `--mint-only` JWT subcommand) because the host has only .NET 10.0.103 SDK installed and `global.json` pins `sdk.version=8.0.0` with `rollForward=latestMinor` (only rolls within 8.0.x). Exit code 3.
|
||||
User picked A (run perf harness now). Stack came up cleanly via `docker-compose up -d --build`. Perf script `scripts/run-performance-tests.sh` failed at the bootstrap step (`dotnet build SatelliteProvider.IntegrationTests` for the `--mint-only` JWT subcommand) because the host had only .NET 10.0.103 SDK installed and `global.json` pinned `sdk.version=8.0.0` with `rollForward=latestMinor` (only rolls within 8.0.x). Exit code 3.
|
||||
|
||||
Sibling script `scripts/run-tests.sh` does NOT have this problem because it shells out to `docker run --rm ... mcr.microsoft.com/dotnet/sdk:8.0` for every dotnet invocation. The perf script was written without that pattern.
|
||||
|
||||
Per cycle-3 lesson "scenarios accumulate as Unverified across cycles" — this is a real script bug, not just a host quirk.
|
||||
|
||||
## Resolution path
|
||||
## Replay attempt #2 — 2026-05-12T02:21:00Z (cycle 4, AC-5 of AZ-500 short bootstrap-smoke)
|
||||
|
||||
Cycle 4 will run a full **".NET 8 → .NET 10 migration"** task (see Step 9 of cycle 4). Once the project targets net10.0 and global.json no longer pins to 8.x, the host SDK becomes the project SDK and the perf script's bootstrap will succeed without modification (it shells out to `dotnet build` against the same project, which will then resolve against `mcr.microsoft.com/dotnet/sdk:10.0` for any Docker-side calls and against the host 10.x SDK for host-side calls). Cycle 4's deploy gate (Step 15) will then re-run this perf harness against the migrated build.
|
||||
After AZ-500 landed (.NET 10 migration: TFM, global.json `sdk.version=10.0.0`, all Docker images, all `Microsoft.AspNetCore.*` / `Microsoft.Extensions.*` packages, `scripts/run-performance-tests.sh:49` `bin/Release/net8.0/` → `bin/Release/net10.0/`), re-ran the AC-5 short variant:
|
||||
|
||||
## Pre-requisites for replay (after .NET 10 migration lands)
|
||||
```
|
||||
PERF_REPEAT_COUNT=2 PERF_UAV_BATCH_SIZE=2 ./scripts/run-performance-tests.sh
|
||||
```
|
||||
|
||||
Same as before — env vars must be present:
|
||||
against `docker-compose up -d --build` (api healthy on `:18980`, swagger 200, anonymous request 401). Trace summary:
|
||||
|
||||
- `JWT_SECRET` — ≥ 32 bytes; already in `.env`
|
||||
- `JWT_ISSUER` — already in `.env` as DEV-ONLY (AZ-494)
|
||||
- `JWT_AUDIENCE` — already in `.env` as DEV-ONLY (AZ-494)
|
||||
- `GOOGLE_MAPS_API_KEY` — already in `.env`
|
||||
| Step | Result |
|
||||
|------|--------|
|
||||
| Build `SatelliteProvider.IntegrationTests` (Release) | **OK** (build succeeded, 11 NU1902/CA2227 warnings, 0 errors, 41.5s) |
|
||||
| `--mint-only` JWT subcommand | **OK** (341-byte token, 4h lifetime) |
|
||||
| PT-01 cold tile download | **PASS** (2538ms / 30000ms threshold) |
|
||||
| PT-02 cached tile retrieval | **PASS** (195ms / 500ms) |
|
||||
| PT-03 region 200m / z18 | **PASS** (384ms / 60000ms) |
|
||||
| PT-04 region 500m / z18 + stitch | **PASS** (2202ms / 120000ms) |
|
||||
| PT-05 5 concurrent regions | **PASS** (3258ms / 300000ms) |
|
||||
| PT-06 route creation (2 points) | **PASS** (178ms / 5000ms) |
|
||||
| PT-07 cold/warm region request | **PASS** (warm p95 2340ms < cold p95 3241ms) |
|
||||
| PT-08 UAV batch upload | **CRASHED** at first batch summarisation — see below |
|
||||
|
||||
**Bootstrap step DID NOT exit with code 3** — host SDK / global.json mismatch is gone. AC-5 met.
|
||||
|
||||
## Replay attempt #2 — root cause of PT-08 crash (NOT an SDK / .NET 10 issue)
|
||||
|
||||
`bash -x` trace shows the script silently exits right after `rejected=0` and the cleanup trap fires. The script bug is at `scripts/run-performance-tests.sh:417`:
|
||||
|
||||
```bash
|
||||
rejected=$(grep -o '"status":"rejected"' "$PERF_TMP_DIR/pt08_resp.json" | wc -l | tr -d ' ')
|
||||
```
|
||||
|
||||
When the upload response has zero rejected items (the happy-path case), `grep -o` exits 1 (no matches). With `set -o pipefail` (line 16) the pipeline returns 1; with `set -e` the assignment kills the script. The sibling line at 416 for `accepted` only worked in this trace because the response had 2 accepted items so `grep` exited 0.
|
||||
|
||||
This bug pre-existed AZ-500. It was previously masked because the perf script never reached PT-08 — it failed at bootstrap (replay #1) due to the SDK mismatch. The .NET 10 migration unmasked it by clearing the bootstrap blocker. PT-01..PT-07 are unaffected (no `grep -c`/`grep -o` counts on potentially-empty matches).
|
||||
|
||||
The actual perf-relevant data PT-08 captured before crashing (one batch run completed): HTTP 200, batch latency 99ms (well under the AZ-488 2000ms p95 threshold), accepted=2, rejected=0. So the underlying perf is healthy; only the script's failure-counting harness is buggy.
|
||||
|
||||
## Resolution path (forward)
|
||||
|
||||
Two follow-up fixes are needed; **both are out of AZ-500 scope** per `coderule.mdc` "scope discipline":
|
||||
|
||||
1. **`scripts/run-performance-tests.sh:416-417`** — defensive grep-counting. Replace
|
||||
```bash
|
||||
accepted=$(grep -o '"status":"accepted"' "$PERF_TMP_DIR/pt08_resp.json" | wc -l | tr -d ' ')
|
||||
rejected=$(grep -o '"status":"rejected"' "$PERF_TMP_DIR/pt08_resp.json" | wc -l | tr -d ' ')
|
||||
```
|
||||
with a pipefail-tolerant variant such as
|
||||
```bash
|
||||
accepted=$(grep -c '"status":"accepted"' "$PERF_TMP_DIR/pt08_resp.json" || true)
|
||||
rejected=$(grep -c '"status":"rejected"' "$PERF_TMP_DIR/pt08_resp.json" || true)
|
||||
```
|
||||
(`grep -c` already counts; `|| true` neutralises the exit-1-on-no-match case when summed with `set -o pipefail`/`set -e`).
|
||||
|
||||
2. **Step 15 (Performance Test) of cycle 4** — re-run the *full* harness (default `PERF_REPEAT_COUNT=20 PERF_UAV_BATCH_SIZE=10`) after the script fix lands. Only then can the leftover be deleted (per `Constraints` last bullet of AZ-500: "leftover file is deleted ONLY when the full perf script runs cleanly").
|
||||
|
||||
## Pre-requisites for full replay
|
||||
|
||||
Same as before — env vars must be present (already in `.env`):
|
||||
|
||||
- `JWT_SECRET` — ≥ 32 bytes
|
||||
- `JWT_ISSUER` — DEV-ONLY (AZ-494)
|
||||
- `JWT_AUDIENCE` — DEV-ONLY (AZ-494)
|
||||
- `GOOGLE_MAPS_API_KEY`
|
||||
|
||||
Optionally:
|
||||
- `PERF_REPEAT_COUNT` (default 20)
|
||||
- `PERF_UAV_BATCH_SIZE` (default 10)
|
||||
|
||||
## How to replay (after .NET 10 migration)
|
||||
## How to replay (after the script fix lands)
|
||||
|
||||
```bash
|
||||
docker-compose up -d --build # bring up API on :18980
|
||||
./scripts/run-performance-tests.sh # ~3-5 minutes
|
||||
./scripts/run-performance-tests.sh # ~3-5 minutes; full PT-01..PT-08
|
||||
docker-compose down --remove-orphans
|
||||
```
|
||||
|
||||
## Why this is NOT a hard blocker
|
||||
|
||||
- AC-5 of AZ-500 only gates the bootstrap step ("does NOT exit with code 3"). That is met.
|
||||
- The cycle-3 implementation report and code review verdicts already note that the perf harness was statically verified (script grep + integration-test compile + AZ-492 AC-1/AC-4/AC-5/AC-6 covered).
|
||||
- The AZ-488 batch-p95 threshold was set in cycle 2 and existing integration tests do NOT regress at the cycle-3 build (Step 11 full suite all-green).
|
||||
- No cycle-3 change altered the production hot paths beyond JWT validation (AZ-494 adds two string comparisons per request — sub-microsecond).
|
||||
- The cycle-2 deploy also skipped this gate (Option B) without negative consequences.
|
||||
- The AZ-488 batch-p95 threshold was set in cycle 2; the one PT-08 batch we did capture (99ms) is far below the 2000ms threshold.
|
||||
- No cycle-3/cycle-4 change altered production hot paths beyond JWT validation (AZ-494 adds two string comparisons per request — sub-microsecond).
|
||||
|
||||
## Replay obligation
|
||||
|
||||
Defer until cycle 4's .NET 10 migration is deployed (Step 16). At that point, replay this leftover as part of cycle 4's Step 15 (Performance Test) gate. If perf passes — delete this file. If it fails — keep with failure detail.
|
||||
Open a new follow-up PBI for the `scripts/run-performance-tests.sh:416-417` grep fix. Once that lands and a full perf run is green, delete this file. Until then, this leftover stays.
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "8.0.0",
|
||||
"version": "10.0.0",
|
||||
"rollForward": "latestMinor",
|
||||
"allowPrerelease": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ if { [[ -z "${JWT_SECRET:-}" ]] || [[ -z "${JWT_ISSUER:-}" ]] || [[ -z "${JWT_AU
|
||||
fi
|
||||
|
||||
PERF_PROJECT="$PROJECT_ROOT/SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj"
|
||||
PERF_DLL="$PROJECT_ROOT/SatelliteProvider.IntegrationTests/bin/Release/net8.0/SatelliteProvider.IntegrationTests.dll"
|
||||
PERF_DLL="$PROJECT_ROOT/SatelliteProvider.IntegrationTests/bin/Release/net10.0/SatelliteProvider.IntegrationTests.dll"
|
||||
|
||||
# Pre-build IntegrationTests once so the --mint-only / --gen-uav-fixture
|
||||
# subcommands produce clean stdout (no interleaved Restore/Build chatter).
|
||||
|
||||
@@ -59,7 +59,7 @@ if [[ "$skip_format" == "true" ]]; then
|
||||
echo "Step 0: Skipping dotnet format check (--skip-format)"
|
||||
else
|
||||
echo "Step 0: dotnet format whitespace --verify-no-changes"
|
||||
if ! docker run --rm -v "$PROJECT_ROOT:/src" -w /src mcr.microsoft.com/dotnet/sdk:8.0 \
|
||||
if ! docker run --rm -v "$PROJECT_ROOT:/src" -w /src mcr.microsoft.com/dotnet/sdk:10.0 \
|
||||
dotnet format whitespace SatelliteProvider.sln --verify-no-changes; then
|
||||
echo ""
|
||||
echo "ERROR: Whitespace violations detected. Run 'dotnet format whitespace SatelliteProvider.sln' to fix."
|
||||
@@ -70,7 +70,7 @@ fi
|
||||
|
||||
if [[ "$mode" == "unit" ]]; then
|
||||
echo "Running unit tests only..."
|
||||
docker run --rm -v "$PROJECT_ROOT:/src" -w /src mcr.microsoft.com/dotnet/sdk:8.0 \
|
||||
docker run --rm -v "$PROJECT_ROOT:/src" -w /src mcr.microsoft.com/dotnet/sdk:10.0 \
|
||||
sh -c "dotnet restore SatelliteProvider.sln && dotnet test SatelliteProvider.Tests/SatelliteProvider.Tests.csproj --no-restore --configuration Release --collect:'XPlat Code Coverage' --results-directory /src/TestResults --logger 'console;verbosity=normal'"
|
||||
echo ""
|
||||
echo "=== Unit tests complete (coverage written to ./TestResults/) ==="
|
||||
@@ -113,7 +113,7 @@ export JWT_ISSUER
|
||||
export JWT_AUDIENCE
|
||||
|
||||
echo "Step 1: Unit tests"
|
||||
docker run --rm -v "$PROJECT_ROOT:/src" -w /src mcr.microsoft.com/dotnet/sdk:8.0 \
|
||||
docker run --rm -v "$PROJECT_ROOT:/src" -w /src mcr.microsoft.com/dotnet/sdk:10.0 \
|
||||
sh -c "dotnet restore SatelliteProvider.sln && dotnet test SatelliteProvider.Tests/SatelliteProvider.Tests.csproj --no-restore --configuration Release --collect:'XPlat Code Coverage' --results-directory /src/TestResults --logger 'console;verbosity=normal'"
|
||||
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user