From 65cdfae970b834c7c8f43a1ffd96ced921086eef Mon Sep 17 00:00:00 2001 From: Oleksandr Bezdieniezhnykh Date: Tue, 12 May 2026 03:42:10 +0300 Subject: [PATCH] [AZ-491] [AZ-492] [AZ-493] [AZ-494] [AZ-495] [AZ-496] Cycle 3 Step 15 skip + Step 16 deploy report Step 15 (Performance Test): SKIPPED. User skipped the optional gate question. Per meta-rule.mdc, performance tests require explicit approval; a skipped question is not approval. Recorded as leftover at _docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md for replay at next /autodev invocation. Step 16 (Deploy): COMPLETED. Produced deploy_cycle3.md mirroring the cycle-2 shape. Covers: - 9 cycle-3 commits + zero DB migration - Config changes (JWT_ISSUER/JWT_AUDIENCE env vars w/ fail-fast, 8.0.25 package bumps, new TestSupport project) - Pre-deploy gate recap (Steps 11-15) - Cycle-3 operational risks R1-R4 (admin-team iss/aud confirm, cross-repo doc deferral, cycle-2 R1/R3 carry-overs, test-runner log line) - Rollback plan, post-deploy verification (incl. wrong-iss / wrong- aud smoke probes), CI/CD push policy - Resolved this cycle: F-AUTH-2, D1, D3, PT-07/PT-08 leftover - Follow-up backlog: D4 NU1902 bump, F-DBR-2 third guard, F-PERF-1 token-history hardening, image-fixture consolidation, AC-7 cross- repo write, no-revocation-list residual Next: Step 17 (Retrospective, cycle-end mode). Co-authored-by: Cursor --- _docs/03_implementation/deploy_cycle3.md | 171 ++++++++++++++++++ _docs/_autodev_state.md | 10 +- ...026-05-12_perf-cycle3-harness-execution.md | 44 +++++ 3 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 _docs/03_implementation/deploy_cycle3.md create mode 100644 _docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md diff --git a/_docs/03_implementation/deploy_cycle3.md b/_docs/03_implementation/deploy_cycle3.md new file mode 100644 index 0000000..58988f8 --- /dev/null +++ b/_docs/03_implementation/deploy_cycle3.md @@ -0,0 +1,171 @@ +# Deploy Report — Cycle 3 (AZ-491 / AZ-492 / AZ-493 / AZ-494 / AZ-495 / AZ-496) + +**Date**: 2026-05-12 +**Cycle**: 3 +**Scope**: Test-infrastructure + dependency + security hardening from the cycle-2 retrospective backlog: +- **AZ-491** — consolidate JWT test-mint helpers into a single `SatelliteProvider.TestSupport` source of truth (replaces cycle-2 duplicate factories). +- **AZ-492** — wire perf harness PT-01..PT-06 JWT-attach + implement PT-07 (region cold/warm) and PT-08 (UAV batch upload) scenarios. +- **AZ-493** — integration test DB reset hook with two-guard safety (env + Host allowlist); replaces the AZ-488 wall-clock coordinate workaround. +- **AZ-494** — JWT `iss` / `aud` validation enabled (Option B per user decision); fail-fast at startup if env vars missing. +- **AZ-495** — formalise doc folder convention for new tasks. +- **AZ-496** — bump `Microsoft.AspNetCore.OpenApi` + `Microsoft.AspNetCore.Authentication.JwtBearer` 8.0.21 → 8.0.25 (closes cycle-1 D1 + cycle-2 D3 supply-chain findings). + +## What is shipping + +### Code changes (committed to `dev`, pushed) + +| Commit | Subject | +|--------|---------| +| `76076cb` | `[AZ-491] [AZ-492] [AZ-493] [AZ-494] [AZ-495] [AZ-496] Cycle 3 Step 9: 6 task specs` | +| `9cfd80b` | `[AZ-495] [AZ-496] Cycle 3 batch 1: doc convention + AspNetCore 8.0.25` | +| `c396740` | `[AZ-491] Cycle 3 batch 2: consolidate JWT test-mint helpers into TestSupport` | +| `745f484` | `[AZ-493] Cycle 3 batch 3: integration test DB-reset hook` | +| `080441d` | `[AZ-492] Cycle 3 batch 4: perf harness PT-07 + PT-08 + JWT-attach` | +| `f979e18` | `[AZ-494] Enable JWT iss/aud validation with fail-fast startup` | +| `495605f` | `[AZ-494] [AZ-492] Cycle 3 Step 16: full test suite green; close batches` | +| `e42bf62` | `[AZ-491] [AZ-492] [AZ-493] [AZ-494] [AZ-495] [AZ-496] Cycle 3 Steps 11-13: test-spec sync + ripple log` | +| `314d1de` | `[AZ-491] [AZ-492] [AZ-493] [AZ-494] [AZ-496] Cycle 3 Step 14: security audit refresh` | + +All 9 commits on `dev`, pushed to `origin/dev` as of this report. + +### Database migration + +**None this cycle.** All cycle-3 work is at the application / test-infrastructure / config layer. The `tiles` table schema is unchanged. + +### Configuration changes (operator must verify before promoting) + +| Setting | Was | Now | Source | +|---------|-----|-----|--------| +| `JWT_ISSUER` (env var) | unset | **MUST be set to admin-team-confirmed value** | AZ-494 — required for API to start. App throws `InvalidOperationException` at startup if missing or whitespace. | +| `JWT_AUDIENCE` (env var) | unset | **MUST be set to admin-team-confirmed value** | AZ-494 — same fail-fast contract as `JWT_ISSUER` / `JWT_SECRET`. | +| `Jwt:Issuer` (appsettings) | n/a | empty in `appsettings.json`; `DEV-ONLY-iss-admin-azaion-local` in `appsettings.Development.json` | AZ-494. Env var overrides config. | +| `Jwt:Audience` (appsettings) | n/a | empty in `appsettings.json`; `DEV-ONLY-aud-satellite-provider` in `appsettings.Development.json` | AZ-494. Env var overrides config. | +| `appsettings.json` → `Jwt.Issuer` / `Jwt.Audience` | absent | empty string | AZ-494 — empty prod values guarantee the fail-fast contract triggers. | +| `Microsoft.AspNetCore.OpenApi` PackageReference | 8.0.21 | **8.0.25** | AZ-496 — closes cycle-1 D1 (CVE-2026-26130 SignalR DoS; not reachable in this app but the bump is hygiene). | +| `Microsoft.AspNetCore.Authentication.JwtBearer` PackageReference | 8.0.21 | **8.0.25** | AZ-496 — closes cycle-2 D3 (same 8.0.21 patch line). | +| `docker-compose.yml` → `api.environment` | (already had `JWT_SECRET`) | adds `JWT_ISSUER=${JWT_ISSUER}` + `JWT_AUDIENCE=${JWT_AUDIENCE}` | AZ-494 | +| `docker-compose.tests.yml` → `integration-tests.environment` | (already had `JWT_SECRET`) | adds same `JWT_ISSUER` + `JWT_AUDIENCE` pass-through + `ASPNETCORE_ENVIRONMENT=Testing` + `INTEGRATION_KEEP_STATE=${…:-}` | AZ-493 + AZ-494 | +| `.env.example` | (no iss/aud lines) | adds `JWT_ISSUER` + `JWT_AUDIENCE` placeholder lines + documents fail-fast contract | AZ-494 | +| `scripts/run-tests.sh` | (loaded `JWT_SECRET`) | also loads + fail-fasts on `JWT_ISSUER` + `JWT_AUDIENCE`; `--keep-state` flag added | AZ-493 + AZ-494 | +| `scripts/run-performance-tests.sh` | (script-rot: no JWT attach, perf-test stubs) | full rewrite: JWT attach across PT-01..PT-06, PT-07 cold/warm distribution, PT-08 batch distribution, AZ-494 iss/aud handling | AZ-492 + AZ-494 | +| `SatelliteProvider.TestSupport` project (new) | n/a | added by AZ-491; consumed by `SatelliteProvider.Tests` + `SatelliteProvider.IntegrationTests`. `IsPackable=false`; **never ships in a production container image**. | AZ-491 | + +### Documentation, test-spec, audit, leftover artifacts (all committed in the commits above) + +- `_docs/02_document/architecture.md`, `module-layout.md`, `modules/api_program.md`, `modules/tests_unit.md`, `modules/tests_integration.md` — Step 13 (Update Docs). +- `_docs/02_document/tests/{traceability-matrix,security-tests,environment,performance-tests}.md` — Step 12 (Test-Spec Sync) + AZ-492's PT-07/PT-08 entries. +- `_docs/02_document/ripple_log_cycle3.md` — Step 13 ripple log (new). +- `_docs/05_security/{dependency_scan,static_analysis,owasp_review,infrastructure_review,security_report}.md` — Step 14 (Security Audit), cycle-3 deltas appended. **PASS_WITH_WARNINGS**. +- `_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md` (new) — Step 15 deferral. +- `_docs/03_implementation/batch_0{1,2,3,4,5}_cycle3_report.md` + matching `reviews/batch_0{1,2,3,4,5}_cycle3_review.md` — Step 10 per-batch + review reports. +- `_docs/03_implementation/cumulative_review_batches_0{1-3,4-5}_cycle3_report.md` — Step 10 cumulative reviews (every 3 batches). +- `_docs/02_tasks/done/AZ-49{1,2,3,4,5,6}_*.md` (moved from todo/). +- `_docs/02_tasks/_dependencies_table.md` (statuses updated to `In Testing`). +- `_docs/_process_leftovers/2026-05-11_perf-pt07-harness.md` — deleted (resolved by AZ-492). + +## Pre-deploy gate recap + +| Gate | Outcome | +|------|---------| +| Step 11 — Run Tests | **PASS** — `scripts/run-tests.sh --full` green against the post-cycle-3 build (log at `/tmp/run-tests-cycle3-step16.log`, 8025 lines, "All tests passed (mode=full)"). 4 new AZ-494 fail-fast unit tests + 2 new integration scenarios (`JwtIntegrationTests.WrongIssuer_Returns401` line 650, `WrongAudience_Returns401` line 653) PASS. Full integration suite (incl. all SEC-, BT-, RL- scenarios from cycle 1+2) green. Test-run skill execution skipped per its § Functional Mode (same runner, immediately preceding invocation as Implement skill's Step-16 internal gate). | +| Step 12 — Test-Spec Sync | **PASS** — cycle-update mode appended traceability rows for AZ-491 AC-1..AC-6, AZ-493 AC-1..AC-6, AZ-494 AC-1..AC-7 (AC-7 ◐ deferred), AZ-495, AZ-496; SEC-12 (wrong iss → 401) + SEC-13 (wrong aud → 401) added to `security-tests.md`; `environment.md` env-var table extended with `JWT_SECRET` / `JWT_ISSUER` / `JWT_AUDIENCE` / `INTEGRATION_TEST_DB_RESET` (closes a cycle-2 oversight where `JWT_SECRET` was never recorded). | +| Step 13 — Update Docs | **PASS** — most module / architecture / security docs were already updated inline during batches; `tests_unit.md` had a duplicate `AuthenticationServiceCollectionExtensionsTests` entry consolidated; `ripple_log_cycle3.md` generated. | +| Step 14 — Security Audit | **PASS_WITH_WARNINGS** — 0 Critical, 0 High, 0 new Medium. F-AUTH-2 (cycle-2 Medium) RESOLVED by AZ-494; D1 (cycle-1) + D3 (cycle-2) supply-chain RESOLVED by AZ-496. New cycle-3 findings: D4 (Low, test-only NU1902 7.0.3 < 7.1.2), F-AUTH-3 (Informational, test-runner-only iss/aud log), F-AUTH-4 (Informational, by-design DEV-ONLY placeholders), F-DBR-1 (false positive, hard-coded TRUNCATE table list), F-DBR-2 (Low, operator-bypassable two-guard model), F-PERF-1 (Low, 4-hour GPS token on stdout). | +| Step 15 — Performance Test | **SKIPPED** (user skipped the optional gate question). The cycle-3 perf-harness work (AZ-492 PT-07/PT-08) is runnable but was not executed. Tracked in `_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md`. | +| `dotnet format whitespace --verify-no-changes` | Implicitly passed via Step 11 (first line of the test-run log: "Step 0: dotnet format whitespace --verify-no-changes"). | + +## Cycle-3-specific operational risks (the deploy operator must act on these) + +### **R1 — `JWT_ISSUER` + `JWT_AUDIENCE` must be set to admin-team-confirmed prod values** + +The API throws `InvalidOperationException` at startup if either is missing or whitespace (caught by 4 new unit tests `AddSatelliteJwt_ThrowsOn{Missing,Empty}{Issuer,Audience}` and verified by SEC-12 / SEC-13 integration scenarios). HOWEVER: an operator who copies `appsettings.Development.json` verbatim into prod, or who sets `JWT_ISSUER` / `JWT_AUDIENCE` to the literal `DEV-ONLY-iss-admin-azaion-local` / `DEV-ONLY-aud-satellite-provider` placeholders, would pass the non-empty check. The placeholders are deliberately published in this repo (Option B forcing function). + +**Operator action**: +1. Confirm with the admin team the exact `iss` value the admin API stamps into prod tokens. +2. Confirm the `aud` value satellite-provider should require. +3. Set both as environment variables in the deploy environment (NOT in committed config). +4. Grep the rendered deploy environment for `DEV-ONLY-` and confirm zero hits before promoting beyond `dev`. + +### **R2 — Cross-repo doc update (AZ-494 AC-7) still pending** + +The cycle-3 work did NOT touch `suite/_docs/10_auth.md` in the parent monorepo. AZ-494 AC-7 says the suite-level auth contract should either document the iss/aud values or note that satellite-provider validates them locally. This write is outside satellite-provider's autodev workspace boundary. + +**Operator action**: ask the suite repo's owner to add a paragraph to `suite/_docs/10_auth.md` noting that satellite-provider validates `iss` + `aud` against `JWT_ISSUER` / `JWT_AUDIENCE` env vars (admin-team-supplied) with fail-fast at startup. + +### **R3 — Cycle-2 R1 / R3 follow-ups remain operationally relevant** + +The cycle-2 R1 (`gps-denied-onboard` must attach Bearer tokens) and R3 (UAV consumers need `GPS` permission) are unchanged from cycle 2. **Now additionally**: those tokens must also have `iss` + `aud` matching the operator-set prod values, or they 401 (post-AZ-494). The admin team is the single source of truth for both the secret-distribution and the iss/aud values. + +### **R4 — DEV-ONLY iss/aud values are NOT secrets but are still operator-visible in test logs** + +The integration-test runner's bootstrap line (`Auth : JWT_SECRET resolved (… bytes); iss=...; aud=...`) prints the resolved values at startup. In local / CI runs against the DEV-ONLY placeholders this is harmless; in any production-pointing test run (NOT recommended) it would echo prod values into the test log. The production API itself does NOT log iss/aud — verified by repo grep. + +**Operator action**: do not point the integration-test runner at production env vars. If a smoke test against a live prod API is needed, mint a token manually with the prod values and run a curl probe — do not run the full test-runner image against prod. + +## Rollback plan + +This deploy ships zero schema changes. Rollback is purely an image-version flip plus a config rollback: + +1. Re-deploy the pre-cycle-3 image (last cycle-2 deploy commit was `b69cf56`). +2. Optionally remove the `JWT_ISSUER=${JWT_ISSUER}` + `JWT_AUDIENCE=${JWT_AUDIENCE}` lines from the deployed `docker-compose.yml` (the previous image does NOT validate them, so leaving them is harmless). +3. No DB rollback needed — `tiles` table is identical before and after cycle 3. +4. Inform consumers (if any AZ-494-specific debugging is needed) that iss/aud claim values are no longer enforced; they may continue to attach matching claims (harmless) or stop. +5. The 8.0.25 → 8.0.21 ASP.NET Core downgrade reverts the AZ-496 hygiene bump; this is acceptable for an emergency rollback (the CVE-2026-26130 path is not reachable in this app). + +## Post-deploy verification + +After the cycle-3 image is deployed and the API is bound: + +1. **JWT iss/aud smoke** (NEW, AZ-494): + ```bash + # Token with WRONG issuer must be rejected: + curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer $TOKEN_WITH_WRONG_ISS" \ + "$API_URL/api/satellite/tiles/latlon?Latitude=47.461747&Longitude=37.647063&ZoomLevel=18" + # Expected: 401 + + # Token with WRONG audience must be rejected: + curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer $TOKEN_WITH_WRONG_AUD" \ + "$API_URL/api/satellite/tiles/latlon?Latitude=47.461747&Longitude=37.647063&ZoomLevel=18" + # Expected: 401 + + # Token with MATCHING iss + aud must be accepted (cycle-2 JWT smoke still works): + curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer $VALID_TOKEN" \ + "$API_URL/api/satellite/tiles/latlon?Latitude=47.461747&Longitude=37.647063&ZoomLevel=18" + # Expected: 200 + ``` + +2. **Fail-fast smoke**: deliberately deploy ONCE with `JWT_ISSUER` unset to confirm the API refuses to start. Then set the env var and confirm the API binds. + +3. **Cycle-2 verifications still apply**: JWT smoke (anonymous → 401, valid token → 200), UAV upload smoke (`GPS` permission → 200; no permission → 403), Swagger Bearer button visible. + +4. **No regression in tile reads**: same as cycle-2 R4 verification — `GET /api/satellite/tiles/latlon?...` against a multi-source cell. + +5. **Tail Serilog** for the first hour after promote: alert on `InvalidOperationException` (would indicate fail-fast triggered after a successful first bind, which means env vars went away mid-run — unusual but worth knowing) and on any spike in 401s (would indicate the prod iss/aud value drifted from what admin-API is stamping). + +## CI/CD path + +`.woodpecker/02-build-push.yml` (unchanged this cycle) builds and pushes on push to `dev`, `stage`, `main`. All 9 cycle-3 commits are on `dev` and pushed to `origin/dev`, so the dev-tier image is being built / has built automatically. + +Promote to `stage` / `main` only after R1 (admin-team-confirmed iss/aud values) is satisfied AND the R3 cycle-2 consumer coordination is still in place. + +**Push policy**: per `git-workflow.mdc`, this autodev pushed only to `dev`. Manual operator action required for `stage` / `main` promotion. + +## Resolved-this-cycle backlog items + +- **F-AUTH-2** (cycle-2 Medium) — `iss`/`aud` not validated. **RESOLVED in AZ-494**. Operational gate replaces the code-level gate. +- **D1** (cycle-1 Medium) — `Microsoft.AspNetCore.OpenApi` on the 8.0.21 patch line. **RESOLVED in AZ-496** — bumped to 8.0.25. +- **D3** (cycle-2 Low) — `Microsoft.AspNetCore.Authentication.JwtBearer` on the same 8.0.21 line. **RESOLVED in AZ-496**. +- **PT-07 / PT-08 leftover** (cycle-2 leftover) — perf-harness work for `Authorization: Bearer` attach + PT-07 + PT-08 scenarios. **RESOLVED in AZ-492** — harness now runnable; only execution itself is now-deferred (separate cycle-3 leftover). + +Cycle-1 carry-overs (S1, S2, S4, D1 closed, D2, I1, I3, I5) — S1, S2, S4, I1, I3, I5 unchanged; still flagged in `_docs/05_security/security_report.md` as the pre-public-network hardening backlog. + +## Cycle-3 follow-ups (NOT cycle-blocking; tracked for future PBIs) + +- **D4** (cycle-3 Low, test-only) — bump `System.IdentityModel.Tokens.Jwt 7.0.3` + `Microsoft.IdentityModel.Tokens 7.0.3` to ≥ 7.1.2 (or align to the 8.0.x family) to eliminate the NU1902 warning noise. Test-only, no production reachability. +- **F-DBR-2** (cycle-3 Low, test-only) — consider adding a third guard to `IntegrationTestResetGuard` requiring explicit `INTEGRATION_TEST_DB_RESET_CONFIRM=I-UNDERSTAND-THIS-TRUNCATES` env var when running against non-`postgres` hosts. +- **F-PERF-1** (cycle-3 Low, operator-CLI) — pipe `--mint-only` token via process substitution / `xargs` so it never lands in shell history. +- **AZ-492 image-fixture consolidation** — `UavUploadTests.CreateValidJpeg` + `PerfBootstrap.GenerateUavFixture` + `UavTileImageFactory` are 3 sites that all produce conceptually-similar JPEGs; consolidate into one factory in TestSupport. +- **AZ-492 server-side per-item timing** — PT-08 currently reports per-item gate cost as a derived proxy (`batch_p95 / batch_size`); true per-call `UavTileQualityGate.Validate` timing requires server-side instrumentation. +- **AZ-493 DB-name alignment** — optional rename `satelliteprovider` → `satelliteprovider_test` in `docker-compose.tests.yml` would let `IntegrationTestResetGuard` adopt the spec's original DB-name pattern as a second guard alongside Host allowlist. +- **AZ-494 AC-7 cross-repo doc** — write paragraph in `suite/_docs/10_auth.md` (parent monorepo). +- **No token revocation list** (residual A07 after AZ-494) — Low; re-evaluate if user-revocable tokens or session management is ever required. diff --git a/_docs/_autodev_state.md b/_docs/_autodev_state.md index 34d0bc7..8e9fed8 100644 --- a/_docs/_autodev_state.md +++ b/_docs/_autodev_state.md @@ -2,13 +2,13 @@ ## Current Step flow: existing-code -step: 14 -name: Security Audit +step: 16 +name: Deploy status: completed sub_step: - phase: 5 - name: PASS_WITH_WARNINGS - detail: "Step 14 PASS_WITH_WARNINGS — all 5 phases (Dependency Scan, Static Analysis, OWASP Top 10, Infrastructure, Security Report) refreshed for cycle 3. Resolved: F-AUTH-2 (Medium, AZ-494), D1 (Medium, AZ-496), D3 (Low, AZ-496). New cycle-3 findings: D4 (Low, test-only NU1902), F-AUTH-3 (Info, test-runner), F-AUTH-4 (Info, by design), F-DBR-1 (false positive), F-DBR-2 (Low, test-only TRUNCATE guard), F-PERF-1 (Low, operator-CLI). 0 Critical, 0 High, 0 new Medium. Next: Step 15 (Performance Test, optional gate)." + phase: 7 + name: deploy-report-written + detail: "Step 16 COMPLETED — deploy report written to _docs/03_implementation/deploy_cycle3.md (mirrors cycle-2 shape; project does not use _docs/04_deploy/ layout). 9 commits pushed to origin/dev. No DB migration. 4 cycle-3-specific operational risks recorded (R1 JWT_ISSUER/JWT_AUDIENCE prod values, R2 cross-repo doc deferral, R3 cycle-2 carry-overs still apply, R4 test-runner log line). Rollback plan + post-deploy verification steps captured. Push policy honoured (dev only). Next: Step 17 (Retrospective)." retry_count: 0 cycle: 3 tracker: jira diff --git a/_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md b/_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md new file mode 100644 index 0000000..30a1237 --- /dev/null +++ b/_docs/_process_leftovers/2026-05-12_perf-cycle3-harness-execution.md @@ -0,0 +1,44 @@ +# 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. + +## What was deferred + +Execution of `scripts/run-performance-tests.sh` against the cycle-3 build. The harness was made runnable by AZ-492 and would exercise: + +- **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) + +## Pre-requisites for replay + +The harness needs the same env vars that the cycle-3 test run consumes: + +- `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` + +Optionally: +- `PERF_REPEAT_COUNT` (default 20) +- `PERF_UAV_BATCH_SIZE` (default 10) + +## How to replay + +```bash +./scripts/run-performance-tests.sh +``` + +(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). +- 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. + +## 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.