[AZ-500] Cycle 4 Step 9: new-task .NET 10 migration

Closes Step 9 (New Task) of cycle 4. AZ-500 spec defines the
.NET 8 -> .NET 10 migration (TFM bump on 9 csprojs, global.json
SDK pin to 10.0.0, both Dockerfiles + run-tests.sh + woodpecker
to mcr.microsoft.com/dotnet/*:10.0, Microsoft.AspNetCore.* and
Microsoft.Extensions.* to the 10.x line, Serilog.AspNetCore to
10.x or documented 8.0.3 fallback, plus arch.md + AGENTS.md doc
sync). Closes the cycle-3 perf-harness leftover via AC-5
(bootstrap smoke after migration).

Also logs the cycle-4 perf-leftover replay attempt that
discovered the host-SDK / project-SDK mismatch and rolls the
state file from cycle 3 -> cycle 4 (Step 9 done -> Step 10
ready).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-12 04:35:52 +03:00
parent ca0ca9f2a4
commit c0f004d2c9
4 changed files with 243 additions and 20 deletions
+15
View File
@@ -84,6 +84,14 @@ Source: cycle-2 retrospective top-3 improvement actions + carried-forward securi
| AZ-495 | Resolve doc-folder convention for WebApi component | — | 1 | To Do |
| AZ-496 | Bump Microsoft.AspNetCore.OpenApi + JwtBearer to 8.0.25 | — | 2 | To Do |
### Step 9 cycle 4 — New Task: SDK migration
Source: cycle-3 perf-harness leftover replay surfaced the host SDK / project SDK mismatch; user directive at start of cycle 4 to migrate to .NET 10. Closes the cycle-3 leftover as a side effect (AC-5).
| 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 |
## Execution Order
### Step 6
@@ -121,6 +129,12 @@ Independent tracks — most tasks can run in parallel; the only ordering constra
5. AZ-492 (3 SP) — perf harness. After AZ-491 if Option B; else any time.
6. AZ-494 (2 SP) — JWT iss/aud validation. Gated on cross-team input; not blocked by other cycle-3 tasks.
### Step 9 cycle 4
Single task; coordinated cross-cutting bump.
1. AZ-500 (5 SP) — .NET 8 → .NET 10 migration. Self-contained but touches every csproj, both Dockerfiles, run-tests.sh, .woodpecker/01-test.yml, global.json, and two docs files.
## Total Effort
Step 6: 6 tasks, 17 story points
@@ -129,6 +143,7 @@ Step 8 (03-code-quality-refactoring): 27 tasks, ~66 story points
Step 9 cycle 1: 1 task created (AZ-484, 5 pts)
Step 9 cycle 2: 2 tasks created (AZ-487 = 2 pts, AZ-488 = 8 pts over-cap user-accepted) — total 10 pts
Step 9 cycle 3: 6 tasks created (AZ-491 = 3 pts, AZ-492 = 3 pts, AZ-493 = 2 pts, AZ-494 = 2 pts, AZ-495 = 1 pt, AZ-496 = 2 pts) — total 13 pts
Step 9 cycle 4: 1 task created (AZ-500 = 5 pts)
## Coverage Verification
@@ -0,0 +1,204 @@
# Migrate solution from .NET 8 LTS to .NET 10
**Task**: AZ-500_dotnet10_migration
**Name**: .NET 8 → .NET 10 migration
**Description**: Migrate every project in the solution from `net8.0` to `net10.0`, bump the SDK pin, switch every Docker base image and CI image from the `8.0` line to the `10.0` line, bump every `Microsoft.AspNetCore.*` and `Microsoft.Extensions.*` package from the 8.x / 9.x line to the matching 10.x line, and verify the full test + integration + build pipeline stays green. Also closes the cycle-3 perf-harness leftover that was blocked on the host SDK / project SDK mismatch.
**Complexity**: 5 points
**Dependencies**: None
**Component**: Cross-cutting (every component, every Dockerfile, every CI script, two docs)
**Tracker**: AZ-500
**Epic**: none (cycle-4 .NET 10 migration; cross-cutting infra)
## Problem
The project pins to .NET 8 LTS via `global.json` (`sdk.version=8.0.0`, `rollForward=latestMinor`) and every csproj targets `net8.0`. With .NET 10 GA since November 2025 (~6 months stable as of cycle 4), the host development environments are now provisioning .NET 10 SDK by default. The cycle-3 perf-harness replay surfaced this mismatch concretely: `scripts/run-performance-tests.sh` failed at its bootstrap step because the host had only .NET 10.0.103 and `global.json` would not roll forward (`latestMinor` only rolls within the 8.0.x band).
Beyond the immediate perf-script issue, staying on .NET 8 LTS means:
- The development experience drifts further from the host SDK with every machine refresh
- Microsoft.AspNetCore.* security patches are still flowing for 8.x today (LTS through Nov 2026), but the next major patch line will be 10.x
- New language/runtime features (C# 14, .NET 10 perf wins for ASP.NET pipeline) are inaccessible
This task lifts the entire solution to .NET 10 in a single coordinated change so the runtime, SDK, packages, Docker images, CI images, and docs all move together — preventing partial-state confusion that a phased migration would create.
## Outcome
- All 9 csproj files target `net10.0`.
- `global.json` pins `sdk.version=10.0.0` with `rollForward=latestMinor` (so any 10.0.x host SDK satisfies).
- Both Dockerfiles (`SatelliteProvider.Api/Dockerfile`, `SatelliteProvider.IntegrationTests/Dockerfile`) use `mcr.microsoft.com/dotnet/sdk:10.0`, `aspnet:10.0`, `runtime:10.0`.
- `scripts/run-tests.sh` and `.woodpecker/01-test.yml` reference `mcr.microsoft.com/dotnet/sdk:10.0`.
- `Microsoft.AspNetCore.Authentication.JwtBearer`, `Microsoft.AspNetCore.OpenApi`, `Serilog.AspNetCore` and every `Microsoft.Extensions.*` package are pinned to the matching 10.0.x line (or the highest available 10.0 patch at implementation time).
- All Microsoft.Extensions.* packages (currently 9.0.10) move to 10.0.x as a single coordinated bump.
- Full unit + integration test suite passes against the migrated build (no behavioral regression).
- Container image `docker-compose build` succeeds; API serves on `:18980` after `docker-compose up`.
- The cycle-3 perf-harness leftover (`_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md`) is replayed as part of cycle-4 Step 15 (Performance Test) gate; deleted on green or updated with failure detail.
- `_docs/02_document/architecture.md` Tech Stack table reflects `.NET 10` / `ASP.NET Core 10`.
- `AGENTS.md` Tech Stack section reflects `.NET 10`.
## Scope
### Included
- **TFM bump (9 csproj files)**: `<TargetFramework>net8.0</TargetFramework>``<TargetFramework>net10.0</TargetFramework>` in:
- `SatelliteProvider.Api/SatelliteProvider.Api.csproj`
- `SatelliteProvider.Common/SatelliteProvider.Common.csproj`
- `SatelliteProvider.DataAccess/SatelliteProvider.DataAccess.csproj`
- `SatelliteProvider.Tests/SatelliteProvider.Tests.csproj`
- `SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj`
- `SatelliteProvider.TestSupport/SatelliteProvider.TestSupport.csproj`
- `SatelliteProvider.Services.TileDownloader/SatelliteProvider.Services.TileDownloader.csproj`
- `SatelliteProvider.Services.RegionProcessing/SatelliteProvider.Services.RegionProcessing.csproj`
- `SatelliteProvider.Services.RouteManagement/SatelliteProvider.Services.RouteManagement.csproj`
- **SDK pin**: `global.json``sdk.version=10.0.0`, `rollForward=latestMinor` (`allowPrerelease=false` retained).
- **Microsoft.AspNetCore.* package bumps** (in `SatelliteProvider.Api/SatelliteProvider.Api.csproj`):
- `Microsoft.AspNetCore.Authentication.JwtBearer` 8.0.25 → 10.0.x (highest 10.0 patch on NuGet)
- `Microsoft.AspNetCore.OpenApi` 8.0.25 → 10.0.x
- Same `JwtBearer` bump in `SatelliteProvider.Tests/SatelliteProvider.Tests.csproj` (consistency rule)
- **Serilog bump** (in `SatelliteProvider.Api/SatelliteProvider.Api.csproj`):
- `Serilog.AspNetCore` 8.0.3 → latest 10.x if published, else stay on 8.0.3 with a one-line note in the batch report
- **Microsoft.Extensions.* coordinated bump** (every csproj that references them):
- `Microsoft.Extensions.Caching.Memory` 9.0.10 → 10.0.x (Tests, Services.TileDownloader)
- `Microsoft.Extensions.Configuration.Abstractions` 9.0.10 → 10.0.x (DataAccess)
- `Microsoft.Extensions.Configuration.Json` 9.0.10 → 10.0.x (Tests)
- `Microsoft.Extensions.DependencyInjection` 9.0.10 → 10.0.x (Tests)
- `Microsoft.Extensions.DependencyInjection.Abstractions` 9.0.10 → 10.0.x (RegionProcessing, RouteManagement)
- `Microsoft.Extensions.Hosting.Abstractions` 9.0.10 → 10.0.x (RegionProcessing, RouteManagement)
- `Microsoft.Extensions.Http` 9.0.10 → 10.0.x (Tests, Services.TileDownloader)
- `Microsoft.Extensions.Logging.Abstractions` 9.0.10 → 10.0.x (DataAccess, Tests, all 3 services)
- `Microsoft.Extensions.Logging.Console` 9.0.10 → 10.0.x (Tests)
- `Microsoft.Extensions.Options` 9.0.10 → 10.0.x (Tests)
- `Microsoft.Extensions.Options.ConfigurationExtensions` 9.0.10 → 10.0.x (RegionProcessing, RouteManagement, Services.TileDownloader)
- **Docker base image bumps**:
- `SatelliteProvider.Api/Dockerfile`: `mcr.microsoft.com/dotnet/aspnet:8.0``:10.0`, `mcr.microsoft.com/dotnet/sdk:8.0``:10.0`
- `SatelliteProvider.IntegrationTests/Dockerfile`: `mcr.microsoft.com/dotnet/sdk:8.0``:10.0`, `mcr.microsoft.com/dotnet/runtime:8.0``:10.0`
- **Script + CI image bumps**:
- `scripts/run-tests.sh`: 3 references to `mcr.microsoft.com/dotnet/sdk:8.0``:10.0`
- `.woodpecker/01-test.yml`: 1 reference to `mcr.microsoft.com/dotnet/sdk:8.0``:10.0`
- **Verification**:
- Full `./scripts/run-tests.sh --full` (unit + integration via docker-compose) passes after the migration.
- `docker-compose build && docker-compose up -d` succeeds; API responds with HTTP 401 on `/api/satellite/region/<id>` (expected for unauthenticated probe), and HTTP 301/200 on `/swagger`.
- **AC-5 perf-bootstrap smoke**: a dedicated smoke run of `./scripts/run-performance-tests.sh` (full PT-01..PT-08 OR a `PERF_REPEAT_COUNT=2` short variant if time-constrained) succeeds at the bootstrap step (no SDK error). This explicitly proves the cycle-3 leftover is closed; the leftover file is then deleted in the same commit OR (if a perf threshold fails) updated with the failure detail.
- **Docs**:
- `_docs/02_document/architecture.md` Tech Stack table (line ~67): `ASP.NET Core (Minimal API) | 8.0``10.0`. Also any `.NET 8.0` mention in the prose at the top of the file.
- `AGENTS.md` Tech Stack section: `.NET 8.0``.NET 10`.
### Excluded
- Bumping packages that are not in the `Microsoft.AspNetCore.*` / `Microsoft.Extensions.*` / `Serilog.AspNetCore` lineup: Dapper, Npgsql, dbup-postgresql, Newtonsoft.Json, SixLabors.ImageSharp, Microsoft.IdentityModel.Tokens, System.IdentityModel.Tokens.Jwt, xunit, Moq, FluentAssertions, coverlet.collector, Microsoft.NET.Test.Sdk, Swashbuckle.AspNetCore, Serilog.Sinks.File. These remain at their current pinned versions; the implementer only verifies they restore on net10 — if any fails to restore, surface and STOP, do not bump opportunistically.
- Adopting C# 14 features in production code. The TFM bump enables them, but no source-code rewrite is in scope.
- Performance optimizations enabled by .NET 10 (e.g., new ASP.NET pipeline improvements). Out of scope; should appear as separate PBIs if/when measured benefit is demonstrated.
- Production deployment (Step 16 Deploy in cycle 4 handles that — this PBI only ships the change to dev).
- Cycle-3 perf full retro-replay: only the bootstrap smoke is gated by AC-5. The full perf threshold check happens at Step 15 of cycle 4 (which also closes the leftover).
- Touching the suite-level `suite/_docs/10_auth.md` contract — JWT validation behavior is preserved exactly (JwtBearer 10.x is API-compatible with 8.x for the `TokenValidationParameters` surface used here).
## Acceptance Criteria
**AC-1: Every csproj targets net10.0**
Given the post-PBI repository
When `grep -r "<TargetFramework>" --include="*.csproj"` is run from the repo root
Then every match shows `<TargetFramework>net10.0</TargetFramework>` and zero matches show `net8.0` or any other TFM.
**AC-2: SDK pin is on the 10.x line**
Given the post-PBI `global.json`
When the file is read
Then `sdk.version` is `10.0.0` and `rollForward` is `latestMinor`.
**AC-3: All Docker base images and CI images are on the 10.0 line**
Given the post-PBI repository
When `grep -r "mcr.microsoft.com/dotnet/" --include="*Dockerfile" --include="*.yml" --include="*.sh"` is run
Then every match references the `:10.0` tag and zero matches reference `:8.0` (or any earlier tag).
**AC-4: Microsoft.AspNetCore.* + Microsoft.Extensions.* + Serilog.AspNetCore packages match the 10.x line**
Given the post-PBI csproj files
When `grep -E '(Microsoft\.AspNetCore\.|Microsoft\.Extensions\.|Serilog\.AspNetCore)' --include="*.csproj" -r` is run
Then every Microsoft.AspNetCore.* and Microsoft.Extensions.* match resolves to `Version="10.0.x"` (any 10.0 patch); Serilog.AspNetCore resolves to a 10.x version OR retains `8.0.3` with the batch report explaining why no 10.x was published; AND no Microsoft.AspNetCore/Extensions match resolves to an 8.x or 9.x version.
**AC-5: Perf-script bootstrap succeeds (closes cycle-3 leftover)**
Given the migrated repository AND `docker-compose up -d --build` has completed (API on :18980)
When `./scripts/run-performance-tests.sh` is invoked (either full run or `PERF_REPEAT_COUNT=2 PERF_UAV_BATCH_SIZE=2 ./scripts/run-performance-tests.sh` for a short bootstrap-only smoke)
Then the script does NOT exit with code 3 at the `dotnet build SatelliteProvider.IntegrationTests` bootstrap step (i.e. the host SDK / global.json mismatch is gone), AND `_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md` is either deleted (if all perf checks pass) or updated with the actual failure detail (if perf thresholds regress — no longer an SDK problem).
**AC-6: All tests pass against the migrated build**
Given the migrated repository
When `./scripts/run-tests.sh --full` is run
Then all unit + integration tests pass with no new failures vs. the pre-migration baseline (cycle-3 closing test report).
**AC-7: Container image build succeeds**
Given the migrated repository
When `docker-compose build` is run from the repo root
Then both the `api` and `integration-tests` images build successfully without warnings about package downgrade, framework conflict, or missing base image.
**AC-8: Documentation reflects the migration**
Given the post-PBI repository
When `_docs/02_document/architecture.md` Tech Stack table and the prose at the top of the file are read
Then both reference `.NET 10` / `ASP.NET Core 10` and zero references to `.NET 8.0` / `8.0` (in the framework-version sense) remain in that file. Same check for `AGENTS.md` Tech Stack section.
## Non-Functional Requirements
**Compatibility**
- 8.x → 10.x is a major framework bump. The cycle-3 architecture-compliance baseline (no Critical/High Architecture findings going into cycle 4) must hold after the migration — re-run a baseline scan on the migrated build (Step 12 Test-Spec Sync triggers this naturally). If new findings emerge from runtime behavior change, surface and decide before deploy.
- The JWT validation contract (signature + lifetime + iss + aud + 30s clock skew) MUST be preserved exactly. AZ-487 + AZ-494 integration tests are the gate; if any of them regress, STOP and root-cause before continuing.
- The `/api/satellite/upload` permissions-claim policy (AZ-488) MUST work unchanged. AZ-488's integration tests are the gate.
- The N-source tile storage contract (`_docs/02_document/contracts/data-access/tile-storage.md` v1.0.0) is unaffected — this is a runtime/SDK migration, not a data-model change.
**Performance**
- The full cycle-3 perf harness (PT-01..PT-08) when run at Step 15 of cycle 4 must not regress beyond the existing thresholds. .NET 10 generally improves ASP.NET pipeline perf, so we expect equal-or-better numbers — but the actual measurement gates this. AC-5 only proves the bootstrap, not the thresholds.
**Reliability**
- Any package that fails to restore against `net10.0` is a hard STOP — the implementer surfaces it and we decide (bump that package as part of this PBI, or roll back the migration). Silent fallback is not acceptable.
**Security**
- The migration MUST NOT regress the cycle-2/3 security posture. Specifically: `Microsoft.AspNetCore.Authentication.JwtBearer 10.0.x` must still validate signature + lifetime + iss + aud as configured in `Program.cs`. The cycle-3 dependency_scan.md needs a new pass after the bump (cycle-4 Step 14 Security Audit handles that).
## Unit Tests
| AC Ref | What to Test | Required Outcome |
|--------|-------------|-----------------|
| AC-6 | All existing unit tests in SatelliteProvider.Tests | Pass unchanged on net10.0 |
## Blackbox Tests
| AC Ref | Initial Data/Conditions | What to Test | Expected Behavior | NFR References |
|--------|------------------------|-------------|-------------------|----------------|
| AC-1 | Migrated repo | `grep -r "<TargetFramework>" --include="*.csproj"` | All matches show `net10.0`, none show any other TFM | — |
| AC-2 | Migrated repo | Read `global.json` | `sdk.version=10.0.0`, `rollForward=latestMinor` | — |
| AC-3 | Migrated repo | `grep -r "mcr.microsoft.com/dotnet/" --include="*Dockerfile" --include="*.yml" --include="*.sh"` | All matches reference `:10.0` | — |
| AC-4 | Migrated repo | `grep -E '(Microsoft\.AspNetCore\.\|Microsoft\.Extensions\.\|Serilog\.AspNetCore)' --include="*.csproj" -r` | M.AspNetCore.* and M.Extensions.* on 10.0.x; Serilog.AspNetCore on 10.x or documented 8.0.3 | — |
| AC-5 | Migrated repo + `docker-compose up -d --build` complete | `./scripts/run-performance-tests.sh` (full or short variant) | Bootstrap step exits 0; leftover file deleted or updated with non-SDK failure detail | Performance |
| AC-6 | Migrated repo | `./scripts/run-tests.sh --full` | All unit + integration tests pass | Compatibility, Reliability |
| AC-7 | Migrated repo | `docker-compose build` from repo root | Both images build without warnings about downgrade or framework conflict | Compatibility |
| AC-8 | Migrated repo | Read `_docs/02_document/architecture.md` and `AGENTS.md` | All `.NET 8.0` framework-version mentions are now `.NET 10` | — |
## Constraints
- **Coordinated bump**: TFM, SDK pin, Docker images, CI images, and Microsoft.AspNetCore.* / Microsoft.Extensions.* package versions ALL move in the same commit (or same set of commits inside one PR). Do NOT split into "TFM-first, packages-later" — that creates an intermediate state where `dotnet restore` may pick the wrong framework version of a package.
- **Implementer must verify package availability** before pinning a 10.0.x version. If `Microsoft.AspNetCore.Authentication.JwtBearer 10.0.x` is not yet on NuGet, STOP and ask — do not silently keep 8.0.25 (that defeats AC-4 in spirit even if it lets `dotnet restore` succeed).
- **Use the established Docker pattern for builds** (per AGENTS.md): `docker run --rm -v "$PROJECT_ROOT:/src" -w /src mcr.microsoft.com/dotnet/sdk:10.0 sh -c "..."`. Do NOT invoke host `dotnet build` from this agent's terminal — AGENTS.md is explicit about this. The single exception is during interactive debugging by the user.
- **Do not silently fold in unrelated package bumps**. If the implementer notices `Microsoft.NET.Test.Sdk 17.8.0` could go to 17.x — that is a separate PBI. Note it in the batch report; do not bump.
- **Do not rename any database objects** during this task (coderule.mdc constraint). The migration is runtime-only.
- **Cycle-3 perf leftover deletion**: AC-5 specifies that the leftover file is deleted ONLY when the full perf script runs cleanly. If only the short bootstrap-smoke variant is run during this PBI's implementation, the leftover stays in place and is closed by Step 15 of this same cycle (which runs the full perf harness against the migrated build).
## Risks & Mitigation
**Risk 1: One of the `Microsoft.AspNetCore.*` 10.0.x packages introduces a behavioral change in JWT validation**
- *Risk*: AZ-487/AZ-494 integration tests fail because JwtBearer 10.x changed default `TokenValidationParameters`, the lifetime tolerance behavior, or the WWW-Authenticate header shape.
- *Mitigation*: AC-6 (full test suite green) is the gate. If a regression appears, the implementer reads the JwtBearer 10.x release notes, narrows the diff, and either updates the configuration to preserve 8.x semantics OR surfaces and asks. Do not silently relax the test.
**Risk 2: `Microsoft.AspNetCore.OpenApi 10.x` breaks the Swagger UI generation**
- *Risk*: AZ-353 / cycle-3 swagger-ready endpoint surface regresses.
- *Mitigation*: AC-7 (image build) catches build-level breakage. After `docker-compose up`, manually probe `http://localhost:18980/swagger` — should return 301/200, not 500.
**Risk 3: Microsoft.Extensions.* 10.0.x cascade**
- *Risk*: Bumping all M.E.* to 10.0.x may pull in transitive updates that conflict with the still-pinned `Microsoft.IdentityModel.Tokens 7.0.3` / `System.IdentityModel.Tokens.Jwt 7.0.3`.
- *Mitigation*: `dotnet restore` (run via `docker run sdk:10.0`) surfaces conflicts immediately. If a conflict appears, the implementer documents it and either bumps the IdentityModel packages as part of this PBI (preferred — they're security-sensitive) OR keeps 7.0.3 with a downgrade-tolerated note.
**Risk 4: `Serilog.AspNetCore` has no 10.x line published**
- *Risk*: Implementer can't bump cleanly.
- *Mitigation*: Per Scope.Included, falling back to 8.0.3 on net10.0 is acceptable as long as it's documented in the batch report. Serilog targets netstandard 2.0; 8.0.3 should restore on net10 unless a transitive dep is the blocker.
**Risk 5: Cycle-3 perf-harness still fails after migration for a non-SDK reason**
- *Risk*: AC-5 short smoke succeeds at the bootstrap step but Step 15 (Performance Test) of cycle 4 still finds threshold regressions (e.g., PT-08 batch p95 > 2000ms because of a real .NET 10 ASP.NET pipeline change).
- *Mitigation*: AC-5 only gates the bootstrap step (the cycle-3 leftover's specific failure mode). Step 15 of cycle 4 is the real perf-threshold gate. If thresholds regress, that becomes a Step 15 decision (defer to leftover, raise threshold, or root-cause), not a blocker for THIS PBI.
**Risk 6: CI agent (`platform: arm64`) does not yet have a `mcr.microsoft.com/dotnet/sdk:10.0` arm64 image cached**
- *Risk*: First CI run after merge takes longer than usual; or worse, the arm64 manifest doesn't exist for the chosen tag.
- *Mitigation*: `mcr.microsoft.com/dotnet/sdk:10.0` is multi-arch (Microsoft publishes amd64 + arm64). Cycle 4 Step 16 Deploy is the integration point — verify the CI run succeeds before merging to dev.
+6 -6
View File
@@ -2,13 +2,13 @@
## Current Step
flow: existing-code
step: 9
name: New Task
status: not_started
step: 10
name: Implement
status: in_progress
sub_step:
phase: 0
name: awaiting-invocation
detail: "CYCLE 3 COMPLETE. Step 17 (Retrospective) wrote retro_2026-05-12_cycle3.md + structure_2026-05-12_cycle3.md + 3 new ring-buffer lessons in LESSONS.md. Verdict: first cycle with net-negative architecture delta (-3 Medium-or-above resolved, 0 new Medium); 100% review pass rate (5/5 batches); 5 of 6 tasks completed first attempt; all prior-retro actions translated to closed work. Loop back to Step 9 (New Task) for cycle 4 per existing-code.md § Re-Entry After Completion."
phase: 1
name: prerequisite-checks
detail: ""
retry_count: 0
cycle: 4
tracker: jira
@@ -1,19 +1,23 @@
# Leftover — Cycle 3 perf harness execution
**Timestamp**: 2026-05-12T00:00:00Z (UTC+3 wallclock 2026-05-12 ~03:00)
**Reason for deferral**: User skipped the Step 15 (Performance Test) gate. Per `meta-rule.mdc`, performance tests require explicit approval; a skipped question is not approval. Defaulted to skip + record-as-leftover to avoid blocking the autodev's progress through Steps 16-17.
**Timestamp**: 2026-05-12T01:11:00Z (last replay attempt; original deferral at 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.
## What was deferred
## Replay attempt — 2026-05-12 (cycle 4 /autodev start)
Execution of `scripts/run-performance-tests.sh` against the cycle-3 build. The harness was made runnable by AZ-492 and would exercise:
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.
- **PT-01..PT-06** — regression probes (latency / status checks for the existing endpoints with JWT attach, post-AZ-487 + post-AZ-494)
- **PT-07** — cold + warm region-tile distribution (cold p50/p95 vs warm p50/p95; fails if warm p95 ≥ cold p95)
- **PT-08** — UAV batch upload distribution (batch p50/p95 + per-item proxy; gated at AZ-488 NFR of batch p95 ≤ 2000 ms)
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.
## Pre-requisites for replay
Per cycle-3 lesson "scenarios accumulate as Unverified across cycles" — this is a real script bug, not just a host quirk.
The harness needs the same env vars that the cycle-3 test run consumes:
## Resolution path
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.
## Pre-requisites for replay (after .NET 10 migration lands)
Same as before — env vars must be present:
- `JWT_SECRET` — ≥ 32 bytes; already in `.env`
- `JWT_ISSUER` — already in `.env` as DEV-ONLY (AZ-494)
@@ -24,14 +28,14 @@ Optionally:
- `PERF_REPEAT_COUNT` (default 20)
- `PERF_UAV_BATCH_SIZE` (default 10)
## How to replay
## How to replay (after .NET 10 migration)
```bash
./scripts/run-performance-tests.sh
docker-compose up -d --build # bring up API on :18980
./scripts/run-performance-tests.sh # ~3-5 minutes
docker-compose down --remove-orphans
```
(brings up its own docker-compose stack; cleans up on exit; ~3-5 minutes for default counts)
## Why this is NOT a hard blocker
- 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).
@@ -41,4 +45,4 @@ Optionally:
## Replay obligation
Per `tracker.mdc` Leftovers Mechanism: at the start of the next `/autodev` invocation, this leftover should be replayed by running the perf harness, then either deleted (if it passes) or updated with the failure detail.
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.