# Deploy Report — Cycle 1 (AZ-484) **Date**: 2026-05-11 **Cycle**: 1 **Scope**: Multi-source tile storage schema (AZ-484) and supporting documentation/security artifacts. ## What is shipping ### Code changes (already committed) | Commit | Subject | |--------|---------| | `5ba58b6` | `[AZ-484] [AZ-483] Add task spec + tile-storage v1.0.0 contract draft` | | `687d6bd` | `[AZ-484] Multi-source tile storage: source + captured_at` | | `e9d6db0` | `[AZ-484] Fix multi-source tile reads: drop Dapper enum handler` | ### Database migration - **`013_AddTileSourceAndCapturedAt.sql`** — runs on first startup of the new image. Transactional (`BEGIN; … COMMIT;`), idempotent against partial replays. Adds `source` (VARCHAR(32) NOT NULL DEFAULT `'google_maps'`) + `captured_at` (TIMESTAMP NOT NULL); backfills both for every pre-existing row; replaces 4-col `idx_tiles_unique_location` with 5-col `idx_tiles_unique_location_source`. ### Documentation, test-spec, audit artifacts (uncommitted in working tree, scoped to this deploy commit below) - `_docs/02_document/data_model.md`, `module-layout.md`, 6 module / component docs — Step 13 (Update Docs). - `_docs/02_document/ripple_log_cycle1.md` — Step 13 ripple analysis. - `_docs/02_document/tests/performance-tests.md`, `traceability-matrix.md` — Step 12 (Test-Spec Sync) — added AZ-484 ACs and PT-07. - `_docs/05_security/` (5 files) — Step 14 (Security Audit) — verdict PASS_WITH_WARNINGS. - `_docs/06_metrics/perf_2026-05-11_cycle1_az484.md` — Step 15 (Performance Test) — all scenarios Unverified, non-blocking. - `_docs/_process_leftovers/2026-05-11_perf-pt07-harness.md` — deferred PT-07 harness work. - `_docs/_autodev_state.md` — autodev pointer. ## Pre-deploy checks (recap) | Gate | Outcome | |------|---------| | Step 11 — Run Tests | All unit + integration tests pass against the post-AZ-484 build (213 unit + 5 new AZ-484 integration). | | Step 14 — Security Audit | **PASS_WITH_WARNINGS** — 0 Critical, 0 High, 5 Medium, 5 Low. AZ-484 itself added zero new findings. Top hardening items recorded in `_docs/05_security/security_report.md`. | | Step 15 — Performance Test | All scenarios `Unverified` (PT-07 harness deferred to next cycle). Non-blocking per test-run perf-mode rules. | | `dotnet format whitespace --verify-no-changes` | Passed (part of `scripts/run-tests.sh`). | ## Migration safety - **Idempotent**: re-running migration 013 against an already-migrated database is a no-op (`ADD COLUMN IF NOT EXISTS`, `DROP INDEX IF EXISTS`, `CREATE UNIQUE INDEX IF NOT EXISTS` patterns). - **Backwards compatible**: pre-AZ-484 application code that reads `tiles` still works — `source` defaults to `'google_maps'`, `captured_at` is backfilled to `created_at`. Vestigial columns `version` / `maps_version` are preserved nullable. - **No data loss**: the only DDL change is dropping `idx_tiles_unique_location` (replaced by `idx_tiles_unique_location_source`); rows are not touched except by the backfill UPDATE. - **Lock duration**: ALTER TABLE on a tiles table with O(100K-1M) rows takes a few seconds for ADD COLUMN (metadata-only in PG 11+) and a few seconds for the UPDATE backfill on existing data. Acceptable for a maintenance window-free deploy assuming current row counts (`_docs/02_document/data_model.md` storage estimate: 100K-1M tiles). ## Rollback plan The migration is forward-only by DbUp design (no automatic down migrations). In the unlikely event AZ-484 needs to be reverted in production: 1. Re-deploy the previous image (`registry.../azaion/satellite-provider:`). The pre-AZ-484 code reads `*` from `tiles` and ignores the new columns — it will function correctly against the migrated schema. 2. The 5-col `idx_tiles_unique_location_source` is a strict superset of the pre-AZ-484 4-col index for the only producer pre-AZ-484 sees (`source = 'google_maps'`); pre-AZ-484 UPSERT on `(lat, lon, tile_zoom, tile_size_meters, version)` would fail conflict detection against the new index — so do **not** roll the schema back, only roll the application back. The schema is forward-compatible with the old code. 3. If a true schema rollback is ever needed, write a follow-up migration `014_RevertAz484.sql` rather than editing `013` in place. ## Post-deploy verification After the new image is deployed and migrations have run: 1. Hit `GET /api/satellite/region/{any-existing-region-id}` — confirm 200 + status JSON (smoke test that the `tiles` reads still parse with the new entity shape). 2. Hit `GET /api/satellite/tiles/latlon?Latitude=...&Longitude=...&ZoomLevel=18` for a known cell — confirm 200 + tile bytes. 3. Run a single `psql` query: `SELECT count(*) FROM tiles WHERE source IS NULL OR captured_at IS NULL;` — expected `0`. 4. Tail Serilog logs for `Slow GetTilesByRegionAsync` warnings (>500ms) for the first hour. A spike here would be the earliest signal of a perf regression vs. the (unmeasured) pre-AZ-484 baseline. ## CI/CD path The project's `.woodpecker/02-build-push.yml` builds and pushes the image on push to `dev`, `stage`, `main`. The `dev` branch is the standard target per `.cursor/rules/git-workflow.mdc`. Pushing the new commit to `origin/dev` triggers the pipeline; no manual deploy steps are needed beyond the `git push`. **Push policy**: per `git-workflow.mdc`, the autodev does NOT push without explicit user request. The next user prompt will offer the push as an explicit option. ## Security caveats carried into this deploy The Security Audit (Step 14) flagged 5 Medium findings that should be tracked as separate hardening tasks before public-network exposure but do NOT block this internal deploy: - **S1+S2+I5** — Postgres default credentials in `appsettings.json` + `docker-compose.yml`; `.env` not in `.dockerignore`. - **S4** — Google Maps API key needs rotation + `.env.example`. - **D1** — Bump `Microsoft.AspNetCore.OpenApi 8.0.21 → 8.0.25` (CVE-2026-26130 SignalR DoS — not reachable in this app). - **I3** — Wire `Microsoft.AspNetCore.RateLimiting`. None of these are AZ-484 regressions; they are pre-existing items that today's audit surfaced. Tracking them as separate Jira tickets is recommended but not enforced by this gate.