# Deploy Report — Cycle 10 (AZ-1113)

**Date**: 2026-06-25
**Cycle**: 10
**Scope**: REST 400 error message sanitization (AZ-1113).

## What is shipping

### Code changes

| Area | Change |
|------|--------|
| `GlobalExceptionHandler.cs` | Static `JsonException` / `BadHttpRequestException` client messages |
| `UavUploadValidationFilter.cs` | Static metadata parse error string |
| `UavTileUploadHandler.cs` | Defense-in-depth metadata parse sanitization |
| Unit + integration tests | Assertions for static strings; no `System.` in UAV 400 bodies |
| `error-shape.md` | v1.0.0 → **v1.0.1** (Information Disclosure section) |
| `docker-compose.perf.yml` | **New** — unsets postgres host port for perf/tests when 5433 is occupied |
| `scripts/run-performance-tests.sh` | PT-07: 15s queue drain; pass on warm p95 < cold p95 **or** warm p50 < cold p50 |
| `performance-tests.md` | PT-07 pass criterion aligned with AZ-492 AC-2 |

### Database migrations

**None.**

### Configuration changes

| Setting | Change |
|---------|--------|
| New env vars | **None** |
| Container image | Rebuild only — same `aspnet:10.0` base; no Dockerfile changes |
| Consumer contracts | `error-shape.md` patch bump — field paths unchanged; message content only |

### Contract changes (consumer-visible)

| Contract | Change | Consumer action |
|----------|--------|-----------------|
| `error-shape.md` v1.0.1 | 400 deserializer/binding messages are static strings | Clients must not parse error message text (already documented in Inv-5 / Non-Goals); field paths unchanged |
| REST / gRPC wire shapes | Unchanged | No action |

## Verification gates passed in this cycle

| Gate | Result | Evidence |
|------|--------|----------|
| Step 11 — Functional tests | **PASS** | 450/450 unit + integration smoke EXIT:0 (~2.5m) |
| Step 12 — Test-Spec Sync | **PASS** | BT-33, SEC-14..16, traceability AZ-1113 rows |
| Step 13 — Update Docs | **PASS** | `api_program.md`, test module docs, ripple log |
| Step 14 — Security Audit | **PASS** (delta) | `security_report_cycle10.md`; F-AZ795-1/2, F-AZ810-1 **resolved** |
| Step 15 — Performance Test | **PASS** | `perf_2026-06-25_cycle10.md` — 8/8 after PT-07 harness fix |

## Security carry-overs (post-cycle-10)

| ID | Status |
|----|--------|
| F-AZ795-1, F-AZ795-2, F-AZ810-1 | **Resolved** (AZ-1113) |
| F-AZ810-2 | Open — `DateTime` vs `DateTimeOffset` on `capturedAt` |
| D-AZ795-1 | Open — FluentValidation 12.0.0 → 12.1.1 |
| D2-cy4 | Open — test SDK JWT advisory (test-runtime only) |

## Operator runbook

1. **Commit and push** cycle-10 changes to `origin/dev`; confirm CI green.
2. **No migration** — deploy new API image only.
3. **Smoke-test** after deploy:
   - POST malformed JSON to any validated endpoint → 400 with static message, no `System.` substring
   - POST `/api/satellite/upload` with bad `metadata` → `errors["metadata"]` static string
   - Existing happy paths unchanged (region, route, inventory, UAV upload)
4. **Perf harness** (optional): use `docker compose -f docker-compose.yml -f docker-compose.perf.yml up -d` when host port 5433 is taken by a sibling Postgres.

## Release note

`/release` prerequisites (`scripts/deploy.sh`, `_docs/04_release/`) are **not present** in this repo — production promotion remains operator-driven (image build + compose on target host). Step 16.5 should be **skipped** unless release infrastructure is onboarded.

**Verdict**: Cleared for retrospective (Step 17). Release (16.5) skipped — no release execution harness.
