Files
satellite-provider/_docs/02_document/tests/traceability-matrix.md
T
Oleksandr Bezdieniezhnykh af4219fce6
ci/woodpecker/push/01-test Pipeline was successful
ci/woodpecker/push/02-build-push Pipeline was successful
[AZ-500] Cycle 4 Steps 12-15 sync (test-spec / docs / security / perf)
Step 12 (Test-Spec Sync) - cycle-update mode
  - traceability-matrix: 8 AZ-500 AC rows + .NET 10 runtime
    restriction supersession + Cycle-4 coverage shape note
    (no new tests; ACs verified by re-running existing 78-test
    suite + build pipeline + manifest grep)

Step 13 (Update Docs) - task mode
  - FINAL_report, 00_discovery, architecture, module-layout,
    api_program, tests_unit: .NET 8 -> .NET 10 / C# 12 -> 14 /
    Swashbuckle 6.6.2 -> 10.1.7 + Microsoft.OpenApi 2.x
    refactor note in api_program; Serilog.AspNetCore 8.0.3
    fallback documented inline per AZ-500 Risk #4
  - deployment/{containerization, ci_cd_pipeline}: Docker
    aspnet/sdk:8.0 -> :10.0
  - ripple_log_cycle4: empty import-graph ripple recorded
    (Program.cs is entry point; ParameterDescriptionFilter only
    consumed by Program.cs; csproj/global.json/Dockerfile have
    no import edges)

Step 14 (Security Audit) - resume mode
  - dependency_scan_cycle4: AZ-500 19-package delta scanned;
    cycle-3 D1+D3 (CVE-2026-26130) closed by major-version
    bump; cycle-3 D2 (Test.Sdk 17.8.0 NuGet.Frameworks flag)
    carried over - explicitly out of AZ-500 scope
  - security_report_cycle4: PASS_WITH_WARNINGS (only carry-over
    Medium open; AZ-500 introduced 0 new Critical/High); cycle-3
    static_analysis/owasp_review/infrastructure_review carried
    forward unchanged (AZ-500 made no source-level edits to
    those surfaces)

Step 15 (Performance Test) - perf mode, full default-param run
  - perf_2026-05-12_cycle4: 7 Pass + 1 Unverified (PT-08 hit
    pre-existing scripts/run-performance-tests.sh:417 grep-
    pipefail bug, NOT a .NET 10 regression)
  - PT-07 warm p95 = 301ms (7.7x improvement vs cycle-3 short
    variant - .NET 10 pipeline + N=20 dilution); cold p95 =
    2782ms (-14%); PT-06 90ms (-49%)
  - AZ-500 NFR (Performance) MET for 7/8 scenarios
  - Cycle-3 perf-harness leftover updated with replay #3
    results; STAYS OPEN per AZ-500 Constraint (deletes only on
    fully clean run)

Recommended follow-up PBIs (out of cycle-4 scope, surfaced for
the backlog):
  - 1 SP fix scripts/run-performance-tests.sh:416-417 grep-
    pipefail (replace grep -o ... | wc -l with grep -c ... ||
    true) - unblocks PT-08 + closes the cycle-3 perf leftover
  - 3 SP migrate WithOpenApi(...) callsites to ASP.NET Core 10
    minimal-API metadata extensions (clears 8 ASPDEPR002
    warnings; recorded in batch_01_cycle4_review.md)
  - 1 SP Microsoft.OpenApi 2.x nullable cleanup (CS8604 in
    ParameterDescriptionFilter.cs:25)
  - 1 SP bump Microsoft.NET.Test.Sdk 17.8.0 -> 17.13.0+
    (closes cycle-3 D2 NuGet.Frameworks transitive flag)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 06:05:29 +03:00

141 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Traceability Matrix
## Acceptance Criteria → Test Mapping
| AC | Description | Tests | Coverage |
|----|-------------|-------|----------|
| T1 | Tiles cached, not re-downloaded | BT-02 | ✓ |
| T2 | Concurrent download limit | RS-05, RL-03 | ✓ |
| T3 | Tile stored with correct path | BT-01 | ✓ |
| T4 | Tile metadata persisted | BT-01 | ✓ |
| R1 | Region state transitions | BT-03, BT-04, BT-05 | ✓ |
| R2 | CSV manifest generated | BT-03, BT-04, BT-05 | ✓ |
| R3 | Summary file generated | BT-03, BT-04, BT-05 | ✓ |
| R4 | Stitched image when requested | BT-05 | ✓ |
| R5 | Stitched image valid content | BT-05 | ✓ |
| R6 | Region processing bounded | RL-04 | ✓ |
| RT1 | Points interpolated at ~200m | BT-06 | ✓ |
| RT2 | Point types correctly assigned | BT-06 | ✓ |
| RT3 | Total distance calculated | BT-06 | ✓ |
| RT4 | Geofence filtering applied | BT-11 | ✓ |
| RT5 | ZIP ≤ 50 MB | BT-09, RL-01 | ✓ |
| RT6 | Route map stitched | BT-08, BT-10, BT-12 | ✓ |
| A1 | Region request returns immediately | BT-03 | ✓ |
| A2 | Status endpoint reflects state | BT-03, BT-07 | ✓ |
| A3 | Route returns computed metadata | BT-06 | ✓ |
| S1 | Migrations run on startup | RS-02 | ✓ |
| S2 | Queue rejects when full | RS-04, RL-02 | ✓ |
| S3 | Failed regions marked failed | RS-03 | ✓ |
| AZ-484 AC-1 | Schema accepts source + captured_at; multi-source rows coexist under 5-col unique index | `MultiSourceInsertCoexistsUnderNewIndex_AZ484_AC1`, `NewUniqueConstraintIncludesSourceColumn_AZ484_AC1` (integration) | ✓ |
| AZ-484 AC-2 | Read returns most-recent across sources | `MostRecentAcrossSourcesSelection_AZ484_AC2` (integration) | ✓ |
| AZ-484 AC-3 | Same-source UPSERT collapses to one row with refreshed captured_at | `SameSourceUpsertReplacesPreviousRow_AZ484_AC3` (integration) | ✓ |
| AZ-484 AC-4 | Migration 013 backfill leaves no orphans (count preserved, source='google_maps', captured_at=created_at) | `BackfillUpdateAssignsGoogleMapsAndCapturedAt_AZ484_AC4` (integration) | ✓ |
| AZ-484 AC-5 | Google Maps download path stamps Source='google_maps' (wire) + CapturedAt UTC | `BuildTileEntity_SetsGoogleMapsSourceAndUtcCapturedAt_AZ484_AC5` (unit) | ✓ |
| AZ-484 AC-6 | Existing region/route flows unchanged post-T1 (200 unit + smoke baseline preserved) | Full unit suite (213 tests) + integration smoke scenarios BT-01..BT-12 | ✓ |
| AZ-484 AC-7 | Vision + contract docs amended (architecture.md, glossary.md, module-layout.md, tile-storage.md frozen v1.0.0) | doc-state AC; verified by `monorepo-document` reviews | ✓ |
| AZ-487 AC-1 | Anonymous request returns 401 from every authenticated endpoint | SEC-05 (blackbox); `JwtIntegrationTests.AnonymousRequest_*_Returns401` (integration) | ✓ |
| AZ-487 AC-2 | Expired token returns 401; no internal leak in body | SEC-06 (blackbox); `JwtIntegrationTests.ExpiredToken_Returns401` (integration) | ✓ |
| AZ-487 AC-3 | Tampered signature returns 401 | SEC-07 (blackbox); `JwtIntegrationTests.InvalidSignature_Returns401` (integration) | ✓ |
| AZ-487 AC-4 | Valid token reaches handler with identical response | SEC-09, BT-18 (blackbox); `JwtIntegrationTests.ValidToken_Returns200_OnHealthyEndpoint` (integration) | ✓ |
| AZ-487 AC-5 | Startup fails on missing / short `JWT_SECRET` | SEC-08 (behavioral); `AuthenticationServiceCollectionExtensionsTests.AddSatelliteJwt_Throws*` (unit) | ✓ |
| AZ-487 AC-6 | `HttpContext.User` exposes claims (`sub`, `permissions`, …) | `JwtTokenFactoryTests.Create_WithExtraClaims_PropagatesClaimsThroughValidation` (unit) + indirect via AZ-488 AC-6 (live permission check) | ✓ |
| AZ-487 AC-7 | Swagger UI Authorize button works | `JwtIntegrationTests.SwaggerDocument_AdvertisesBearerSecurityScheme` (integration; programmatic equivalent of UI flow) | ◐ doc-verified |
| AZ-487 AC-8 | All existing tests pass with attached test token | Full `scripts/run-tests.sh --full` run (cycle 2 Step 11 — passed) | ✓ |
| AZ-488 AC-1 | Happy-path 1-item batch persists with `source='uav'` | BT-13 (blackbox); `UavUploadTests.HappyPathSingleItem_PersistsRow` (integration) | ✓ |
| AZ-488 AC-2 | 3-item mixed batch returns per-item results | BT-14 (blackbox); `UavUploadTests.MixedBatch_ReturnsPerItemResults` (integration) | ✓ |
| AZ-488 AC-3 | UAV upload coexists with pre-seeded `google_maps` row | BT-15 (blackbox); `UavUploadTests.MultiSourceCoexistence_AZ484_Cycle2` (integration); reuses AZ-484 AC-1 + AC-2 invariants | ✓ |
| AZ-488 AC-4 | Same-source UPSERT keeps one `source='uav'` row | BT-16 (blackbox); `UavUploadTests.SameSourceUpsert_AZ484_Cycle2` (integration); reuses AZ-484 AC-3 invariant | ✓ |
| AZ-488 AC-5 | Unauthenticated upload returns 401 (covered by AZ-487) | `UavUploadTests.NoToken_Returns401` (integration); AZ-487 AC-1 row covers contract | ✓ |
| AZ-488 AC-6 | Authenticated request without `GPS` permission returns 403 | SEC-10 (blackbox); `UavUploadTests.ValidTokenWithoutGpsPermission_Returns403` (integration); `PermissionsRequirementTests` (unit) | ✓ |
| AZ-488 AC-7a | `INVALID_FORMAT` reject reason on wrong content-type or magic bytes | `UavTileQualityGateTests.Validate_NonJpegContentType_*` and `Validate_WrongMagicBytes_*` (unit) | ✓ |
| AZ-488 AC-7b | `SIZE_OUT_OF_BAND` reject reason on bytes outside `[MinBytes, MaxBytes]` | RL-06 (resource-limit); `UavTileQualityGateTests.Validate_BytesBelowMin_*` and `Validate_BytesAboveMax_*` (unit) | ✓ |
| AZ-488 AC-7c | `WRONG_DIMENSIONS` reject reason on non-256×256 images | BT-14, BT-17 (blackbox); `UavTileQualityGateTests.Validate_WrongDimensions_*` (unit) | ✓ |
| AZ-488 AC-7d | `CAPTURED_AT_FUTURE` / `CAPTURED_AT_TOO_OLD` reject reasons | `UavTileQualityGateTests.Validate_CapturedAtFuture_*` and `Validate_CapturedAtTooOld_*` (unit) | ✓ |
| AZ-488 AC-7e | `IMAGE_TOO_UNIFORM` reject reason on uniform / low-variance JPEGs | `UavTileQualityGateTests.Validate_UniformGreyImage_RejectsImageTooUniform` (unit) | ✓ |
| AZ-488 AC-7 (ordering) | First-applicable rule wins (e.g. format-fail beats dimensions-fail) | BT-17 (blackbox); `UavTileQualityGateTests.Validate_MultipleViolations_*` (unit) | ✓ |
| AZ-488 AC-8 | Oversized batch (> `MaxBatchSize`) returns 400 envelope error | RL-05 (resource-limit); `UavUploadTests.OversizedBatch_Returns400` (integration) | ✓ |
| AZ-488 AC-9 | Contract `uav-tile-upload.md` v1.0.0 frozen and matches implementation | doc-state AC; verified by Step 13 (Update Docs) review | ✓ |
| AZ-488 AC-10 | All existing tests + new AZ-487/AZ-488 tests pass; no AZ-484 regression | Full `scripts/run-tests.sh --full` run (cycle 2 Step 11 — passed) | ✓ |
| AZ-494 AC-1 | Wrong `iss` token returns 401 | SEC-12 (blackbox); `JwtIntegrationTests.WrongIssuer_Returns401` (integration) | ✓ |
| AZ-494 AC-2 | Wrong `aud` token returns 401 | SEC-13 (blackbox); `JwtIntegrationTests.WrongAudience_Returns401` (integration) | ✓ |
| AZ-494 AC-3 | Matching iss + aud accepted | `JwtIntegrationTests.ValidToken_Returns200_OnHealthyEndpoint` (integration; updated to mint via env iss/aud) | ✓ |
| AZ-494 AC-4 | Missing config fails fast | `AuthenticationServiceCollectionExtensionsTests.AddSatelliteJwt_ThrowsOnMissingIssuer` + `_ThrowsOnEmptyIssuer` + `_ThrowsOnMissingAudience` + `_ThrowsOnEmptyAudience` (unit) | ✓ |
| AZ-494 AC-5 | Existing tests pass with matched fixtures | Full integration suite reruns at Step 16 with `JwtTestHelpers.MintAuthenticated` (auto-fills iss/aud from env) | ✓ (gate verified at Step 16) |
| AZ-494 AC-6 | Security artifacts updated (F-AUTH-2 → Resolved) | `_docs/05_security/security_report.md` + `owasp_review.md` updated this batch | ✓ |
| AZ-494 AC-7 | Suite contract reflects reality | `suite/_docs/10_auth.md` lives outside this workspace; this cycle's deploy report documents that satellite-provider validates iss/aud locally and the prod values are admin-team-confirmed at deploy time | ◐ deferred (cross-repo write) |
| AZ-491 AC-1 | Single source of truth — only one `JwtTokenFactory` exists in source | Structural: repo-wide grep returns exactly `SatelliteProvider.TestSupport/JwtTokenFactory.cs`; the legacy `SatelliteProvider.Tests/TestUtilities/JwtTokenFactory.cs` was deleted in batch 02 | ✓ |
| AZ-491 AC-2 | Existing integration tests pass unchanged | Full integration suite at Step 11 (`./scripts/run-tests.sh --full`) — all green | ✓ |
| AZ-491 AC-3 | Existing unit tests pass unchanged | Unit suite at Step 11 (Step 1 of `run-tests.sh`) — all green | ✓ |
| AZ-491 AC-4 | Runner-side concerns preserved in `JwtTestHelpers` (env reads, HttpClient mutation stay in IntegrationTests) | Structural: `JwtTokenFactory` (pure) in TestSupport; `JwtTestHelpers` (side-effectful) in IntegrationTests — documented in `module-layout.md` | ✓ |
| AZ-491 AC-5 | Cycle-2 fixes remain effective (AZ-487/AZ-488 token-validation invariants preserved) | Integration scenarios `JwtIntegrationTests.AnonymousRequest_*`, `_ExpiredToken_Returns401`, `_InvalidSignature_Returns401`, `_ValidToken_Returns200_OnHealthyEndpoint`, `UavUploadTests.*` — all migrated to `MintAuthenticated` and still PASS at Step 11 | ✓ |
| AZ-491 AC-6 | Code-review rule lands to prevent re-duplication | `.cursor/skills/code-review/SKILL.md` Phase 6 rule added in batch 02 (Cycle-3 review SKILL update) | ✓ |
| AZ-493 AC-1 | Empty-state on startup — no leftover rows from previous run | `IntegrationTestDatabaseReset.ResetAsync` invoked at runner start; uniqueness assumptions in `UavUploadTests` (`source='uav'` rows per coordinate) hold without the wall-clock workaround | ✓ |
| AZ-493 AC-2 | Wallclock workaround no longer needed | Structural: `UavUploadTests` no longer offsets coordinates by `DateTime.UtcNow.Ticks % …` to dodge stale rows; coordinates are now deterministic per scenario | ✓ |
| AZ-493 AC-3 | Opt-out preserves state (`--keep-state` flag skips reset) | `scripts/run-tests.sh` parses `--keep-state`, sets `INTEGRATION_TEST_DB_RESET=skip`, and `Program.cs` honours that env var | ✓ |
| AZ-493 AC-4 | Reset only fires in test environment (two-guard model) | Unit: `IntegrationTestResetGuardTests` (env sentinel + Host allowlist `postgres`/`localhost`/`127.0.0.1`; production-shape hostnames rejected) | ✓ |
| AZ-493 AC-5 | Documentation reflects new convention | doc-state AC — `_docs/02_document/module-layout.md` + `_docs/02_document/modules/tests_integration.md` updated in batch 03 | ✓ |
| AZ-493 AC-6 | Existing tests pass unchanged | Full integration suite at Step 11 — all green | ✓ |
| AZ-495 AC-1..AC-N | Doc folder convention formalized | doc-state AC — `.cursor/skills/new-task/SKILL.md` updated in batch 01; `_docs/02_document/module-layout.md` carries the convention | ✓ |
| AZ-496 AC-1 | `Microsoft.AspNetCore.Authentication.JwtBearer` bumped 8.0.21 → 8.0.25 in `SatelliteProvider.Api.csproj` | Structural: csproj diff visible in batch 01 commit; transitive update propagates to `Tests.csproj` via `ProjectReference` | ✓ |
| AZ-496 AC-2..AC-N | Suite still green at the new version | Full unit + integration suite at Step 11 — all green; SEC-05..SEC-11 + AZ-494 AC-1/AC-2 (which depend on `JwtBearer`) all PASS | ✓ |
| AZ-500 AC-1 | Every csproj targets `net10.0` | Structural: `grep -r "<TargetFramework>" --include="*.csproj"` returns 9/9 `net10.0`, 0 `net8.0` (verified at cycle 4 Step 11) | ✓ |
| AZ-500 AC-2 | `global.json` `sdk.version=10.0.0`, `rollForward=latestMinor` | Structural: file contents asserted; SDK roll-forward exercised by host running .NET 10.0.103 | ✓ |
| AZ-500 AC-3 | All Docker base images + CI images on `:10.0` | Structural: `grep -rE "mcr.microsoft.com/dotnet/" --include="*Dockerfile" --include="*.yml" --include="*.sh"` → 7/7 on `:10.0` | ✓ |
| AZ-500 AC-4 | `Microsoft.AspNetCore.*` + `Microsoft.Extensions.*` on `10.0.7`; `Serilog.AspNetCore` documented fallback `8.0.3` | Structural: csproj diff (19 references on `10.0.7`); Serilog.AspNetCore fallback rationale recorded in `AGENTS.md:244` per Risk #4 | ✓ |
| AZ-500 AC-5 | Perf-script bootstrap step succeeds (no exit 3) — closes cycle-3 SDK-mismatch leftover | `PERF_REPEAT_COUNT=2 PERF_UAV_BATCH_SIZE=2 ./scripts/run-performance-tests.sh` exit 1 (NOT 3 — bootstrap clean, build OK, JWT mint OK, PT-01..PT-07 PASS); leftover `_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md` updated with new (non-SDK) PT-08 grep-pipefail finding; full perf gate runs at Step 15 of cycle 4 | ✓ |
| AZ-500 AC-6 | All unit + integration tests pass on the migrated build | Full `./scripts/run-tests.sh --full` at cycle 4 Step 11 — 271/271 unit + integration suite green | ✓ |
| AZ-500 AC-7 | `docker-compose build` succeeds with no downgrade / framework / missing-image warnings | `run-tests.sh` Step 2 build path + `docker compose up -d --build` both succeeded; only warnings emitted are CS8604 nullable + ASPDEPR002 deprecation (neither category gated) | ✓ |
| AZ-500 AC-8 | Documentation reflects .NET 10 | `_docs/02_document/architecture.md` lines 5 + 67 (Tech Stack table) updated; `AGENTS.md` lines 9 + 240244 updated incl. Serilog fallback note | ✓ |
## Restrictions → Test Mapping
| Restriction | Tests | Coverage |
|-------------|-------|----------|
| .NET 10 runtime (cycle 4 — was .NET 8.0 LTS through cycle 3) | All (via Docker image `mcr.microsoft.com/dotnet/aspnet:10.0`); cycle 4 Step 11 full suite green | ✓ |
| PostgreSQL 16 | All (via docker-compose) | ✓ |
| Single instance | PT-05 (concurrent regions on one instance) | ✓ |
| Max 4 concurrent downloads | RS-05, RL-03 | ✓ |
| Max 20 concurrent regions | RL-04 | ✓ |
| Queue capacity 1000 | RS-04, RL-02 | ✓ |
| Max ZIP 50 MB | RL-01 | ✓ |
| ~~No authentication~~ → JWT (HS256) on every endpoint, `GPS` permission on UAV upload | SEC-01..SEC-04 (input handling); SEC-05..SEC-09 (AZ-487 JWT layer); SEC-10..SEC-11 (AZ-488 permission + leak hygiene) | ✓ (superseded by AZ-487 + AZ-488, cycle 2) |
## NFRs → Test Mapping
| NFR | Source | Tests | Coverage |
|-----|--------|-------|----------|
| AZ-484 Perf — `GetTilesByRegionAsync` p95 ≤ 1.10 × pre-AZ-484 baseline | AZ-484 task spec § Non-Functional Requirements | PT-07 (Implemented in AZ-492 — cold + warm distribution, p50/p95 reported; cross-commit baseline comparison remains operator-driven at Step 15) | ✓ |
| AZ-484 Compatibility — no public HTTP response field added/removed; vestigial `maps_version`/`version` columns preserved (nullable) | AZ-484 task spec § Non-Functional Requirements | Existing integration suite (no API contract change observable); BT-01 / region status responses verify response shape | ✓ |
| AZ-487 Performance — JWT validation < 1 ms overhead per request | AZ-487 task spec § Non-Functional Requirements | Not separately measured (HMAC-SHA256 + claims parse is sub-millisecond on any modern x86; no caching needed). Re-measure if PT-07 / PT-08 (AZ-492 harness) shows aggregate regression. | ◐ recorded |
| AZ-487 Security — `RequireSignedTokens`, `RequireExpirationTime`, `ClockSkew = 30 s`, secret ≥ 32 bytes, `iss` + `aud` validated (extended by AZ-494) | AZ-487 + AZ-494 task specs § Non-Functional Requirements + Constraints | `AuthenticationServiceCollectionExtensionsTests` (unit) + SEC-05..SEC-09 + AZ-494 AC-1/AC-2 wrong-iss/aud (integration) | ✓ |
| AZ-487 Reliability — Fail-fast on missing / short `JWT_SECRET` at startup (extended by AZ-494 to iss + aud) | AZ-487 + AZ-494 task specs § Non-Functional Requirements | SEC-08 (behavioral) + unit `AddSatelliteJwt_ThrowsOnMissingSecret` + `_ThrowsOnMissingIssuer` + `_ThrowsOnMissingAudience` | ✓ |
| AZ-488 Performance — Per-item gate cost < 50 ms; p95 batch-of-10 < 2 s | AZ-488 task spec § Non-Functional Requirements | PT-08 (Implemented in AZ-492 — 20-batch distribution, batch p95 gated at 2000 ms; per-item gate cost reported as derived proxy `batch_p95 / batch_size`. True per-call `UavTileQualityGate.Validate` timing requires server-side instrumentation — follow-up). | ✓ (batch p95) / ◐ (per-item proxy only) |
| AZ-488 Reliability — File-first then DB row; per-item failures never fail the batch envelope (except 400/401/403) | AZ-488 task spec § Non-Functional Requirements | BT-14 (mixed-batch shows per-item isolation); `UavTileUploadHandlerTests.*PersistAsync*` (unit); reject reason `STORAGE_FAILURE` defined in contract for the orphan-row recovery path | ✓ |
| AZ-488 Compatibility — Replaces 501 stub; coexists with AZ-484 `tile-storage` v1.0.0 contract on the write side | AZ-488 task spec § Non-Functional Requirements + Contract | `StubAndErrorContractTests` updated to drop the stub-501 expectation; BT-15 + BT-16 validate the AZ-484 invariants under live UAV writes | ✓ |
| AZ-488 Security — Reject details never leak server internals; integer-only file-path construction | AZ-488 task spec § Non-Functional Requirements + Risk 2 | SEC-11 (blackbox); `UavTileFilePathTests` (unit) | ✓ |
## Coverage Summary
| Category | Total Tests | ACs Covered | Restrictions Covered |
|----------|-------------|-------------|---------------------|
| Blackbox (positive) | 12 | 19/22 | — |
| Blackbox (negative) | 5 | — | — |
| Performance | 8 | 4 | 1 |
| Resilience | 6 | 4 | 3 |
| Security | 11 | 9 (AZ-487 AC-1..AC-7, AZ-488 AC-6, leak-hygiene NFR) | 1 (AZ-487 supersedes "No authentication") |
| Resource Limits | 7 | 5 | 4 |
| Cycle 1 — AZ-484 (integration + unit) | 6 | 7/7 | — |
| Cycle 2 — AZ-487 (integration + unit + behavioral) | 4 integration + 3 unit + 1 behavioral | 8/8 | — |
| Cycle 2 — AZ-488 (integration + unit + blackbox) | 7 integration + 14 unit + 6 blackbox | 10/10 | — |
| **Total** | **78** | **47/47 (100%)** | **8/8 (100%)** |
**Coverage shape notes (Cycle 2):**
- AZ-487 AC-7 (Swagger UI Authorize) is verified programmatically (`SwaggerDocument_AdvertisesBearerSecurityScheme`) rather than via a real UI flow; marked `◐ doc-verified`. The end-to-end browser-UI Authorize-button check remains a manual smoke before deploy.
- AZ-487 perf NFR (< 1 ms JWT overhead) remains `◐ recorded`; not separately gated. AZ-488 perf NFR (PT-08) moved from `◐ recorded (Deferred)` to `✓` for batch p95 — see PT-08 row above. AZ-484 perf NFR (PT-07) moved from `◐ recorded` to `✓` — see PT-07 row above. The harness work landed in AZ-492 (cycle 3) along with the `Authorization: Bearer …` attach that AZ-487 silently broke for the perf script.
**Coverage shape notes (Cycle 4 — AZ-500 .NET 8 → .NET 10 migration):**
- All 8 AZ-500 ACs are infrastructure-level (TFM/SDK pin/Docker base/package version/build/test-suite/doc) and are verified by re-running the **existing** test suite, the build pipeline, and `grep` over manifests. **No new test cases were added** — the contract being tested is "the previous 78 tests still pass on the new toolchain", which Step 11 confirmed (271 unit + integration green). Total counts above are unchanged.
- AZ-500 AC-5 (perf-script bootstrap) demoted the cycle-3 SDK-mismatch leftover to a script-bug leftover (PT-08 grep-pipefail at `scripts/run-performance-tests.sh:417`). The full PT-01..PT-08 perf gate moves to cycle 4 Step 15 (Performance Test). The PT-07 / PT-08 coverage rows above remain `✓` because they reflect the harness's *measurement capability*, not the per-cycle measurement run.
- AZ-500 NFRs (Compatibility / Performance / Reliability / Security) propagate to existing rows rather than introducing new gates: Compatibility ⇒ cycle-3 architecture-compliance baseline (verified by Step 11 suite); Performance ⇒ Step 15 perf gate (PT-07/PT-08); Reliability ⇒ no `dotnet restore` failures in the migrated state (Step 11 build path); Security ⇒ Step 14 dependency-scan re-run.
- Restriction "**.NET 8.0 runtime**" was rewritten to "**.NET 10 runtime**" — this is a supersession (toolchain bump) not a new gate, so no Choose was needed per cycle-update rule 3.