# Retrospective — Cycle 8 (2026-05-23)

**Tasks**: AZ-812 (Region API field rename `Latitude/Longitude → Lat/Lon`, OSM convention, 3 SP), AZ-808 (Region POST strict validation, 3 SP), AZ-811 (lat/lon GET strict validation, 3 SP), AZ-809 (Route POST strict validation + nested DTO chain, 5 SP), AZ-810 (UAV upload metadata strict validation, multipart envelope, 3 SP). **5 tasks, 17 SP, 4 batches.**
**Mode**: cycle-end (autodev Step 17). Step 16 (Deploy) and Step 16.5 (Release) skipped per user choice (A at both gates) — matches the cycle-1-to-7 historical pattern (`_docs/04_deploy/` empty across all 8 cycles; `_docs/04_release/` likewise). The pushed commits on `origin/dev` (tip `32bc5c1`) are the cycle's release record.
**Previous retro**: `retro_2026-05-22_cycle7.md`
**Cycle shape**: explicit follow-through on cycle 7's Action 3 (AZ-795 child-task sweep across the remaining public endpoints) — 4 of the 4 endpoints cycle 7 named (`POST request`, `POST route`, `POST upload`, `GET tiles/latlon`) shipped a strict-validation surface in cycle 8. First time the project has shipped a **directly-traceable cross-cycle improvement action** end-to-end (cycle-7 retro recommendation → cycle-8 task slate → cycle-8 closure).

## 1. Implementation Metrics

| Metric | Cycle 8 | Δ vs cycle 7 |
|--------|---------|--------------|
| Tasks implemented | **5** (AZ-808, AZ-809, AZ-810, AZ-811, AZ-812) | +2 |
| Batches executed | **4** | +3 |
| Avg tasks / batch | **1.25** | -1.75 |
| Total complexity delivered | **17 SP** (3+3+3+5+3) | +9 SP |
| Avg complexity / batch | **~4.25 SP** | -3.75 SP |
| Tasks at-or-below 5 SP cap | **5 of 5 (100%)** | unchanged |
| Tasks split mid-cycle | **0** | unchanged |
| Tasks above cap | 0 | unchanged |
| Cumulative reviews | **1** (`cumulative_review_batches_01-04_cycle8_report.md` — trigger fired at batch 3 per the "every 3 batches" rule, executed after batch 4 closure) | +1 |
| Cross-cycle leftovers carried in | **0** | unchanged |
| Cross-cycle leftovers carried out | **0** | unchanged |
| Implementation report artifact written | **YES** (`implementation_report_strict_validation_cycle8.md`) **+ completeness report + cumulative review** | **PATTERN 1 RESOLVED** (cycle 7 had none) |

**Sequencing rationale**: 4 batches sized 1-2 tasks each (vs cycle 7's single 3-task batch). The split reflects the spec structure: AZ-812 (rename) was a prerequisite for the validator wave because it changed the wire format the validators would enforce; AZ-808 + AZ-811 paired because they shared the same wiring pattern (`.WithValidation<T>()` JSON-body + a query-param filter); AZ-809 stood alone because its 3-DTO-deep nested validator chain (route + per-point + per-polygon-corner) was meaningfully more complex than batches 2/4; AZ-810 stood alone because its multipart-envelope filter was a different shape from the JSON-body endpoints. The 4-batch slicing kept each batch under the "5 SP per batch" implicit complexity ceiling and made the per-batch reviews tractable.

Cycle 8 completed in **8 dev commits** on `dev` (and 1 commit on a pre-cycle prep): the four implementation commits (one per batch — batches 1-3 were committed in their own autodev runs, batch 4 in the closing run), then `bbe8783` (Cycle 8 close + AZ-810 fix-test-coords adjacent), `b763da3` (AZ-810 clamp UAV test fixture coords), then the post-implementation sync chain: `ec0eb90` (test-spec sync, Step 12), `6207ab7` (docs sync, Step 13), `ac40a8b` (security audit, Step 14), `8fca6e0` (F-AZ809-1 fix, Step 14 follow-up), `32bc5c1` (perf run, Step 15). All five trailing commits pushed together at the end of Step 15 per the user's Step 16-gate choice. **+1 commit vs cycle 7** (8 vs 7) — accounted for by the F-AZ809-1 in-cycle resolution commit (cycle 7 had no in-cycle security-finding resolution).

## 2. Quality Metrics

### Code Review Results

| Verdict | Count | Percentage |
|---------|-------|-----------|
| PASS | **0** | 0% |
| PASS_WITH_NOTES | **2** (batches 02 + 03) | 50% |
| PASS_WITH_WARNINGS | **2** (batches 01 + 04) | 50% |
| FAIL | 0 | — |
| **Cumulative review** | **PASS_WITH_WARNINGS** (cycle-8 implementation phase approved for closure) | n/a |

First cycle with a per-batch review file written for every batch (cycle 7 had no per-batch review files; cycle 6 had one per batch but only one batch). The cumulative cross-batch review surfaced no new finding categories beyond the per-batch reviews — i.e., the per-batch reviews caught everything.

### Findings by Severity (cycle-8 review surface)

| Severity | Cycle 8 (code review) | Cycle 8 (security audit) | Combined | Δ vs cycle 7 |
|----------|----------------------|---------------------------|----------|--------------|
| Critical | 0 | 0 | 0 | unchanged |
| High | 0 | 0 | 0 | unchanged |
| Medium | 0 | **1 resolved in-cycle** (F-AZ809-1) + 1 carry-over (D2-cy4) | 1 (resolved) + 1 carry-over | new resolved-in-cycle pattern |
| Low | **4** (1 per batch — all DRY-in-test-helpers or design-decision documented) | 2 new (F-AZ810-1 + F-AZ810-2) + 3 carry-over (F-AZ795-1 + F-AZ795-2 + D-AZ795-1) | 6 new + 3 carry-over | +1 vs cycle 7's 3 |
| Info | **2** (batches 02 + 04 — both documented and accepted) | 0 | 2 | new pattern (cycle 7 had no Info findings) |
| Remaining post-cycle | **6 Low** all open (4 code-review test-DRY items + 2 security Lows from F-AZ810-1/2); 1 Medium resolved (F-AZ809-1); 1 Medium carry-over (D2-cy4) unchanged; 3 Low carry-over (F-AZ795-1/2 + D-AZ795-1) unchanged | | | +3 Lows vs cycle 7's 3 |

### Findings by Category (cycle-8 review surface — code-review side only)

| Category | Count | Top Files |
|----------|-------|-----------|
| Maintainability | **3 Low** (test-DRY) | `SatelliteProvider.IntegrationTests/ProblemDetailsAssertions.cs` (batch-1 promotion → resolved batch 2); `SatelliteProvider.Tests/Validators/UavTileMetadataValidatorTests.cs` (`FixedTimeProvider` duplicated 4×); `SatelliteProvider.IntegrationTests/UavUploadValidationTests.cs` (`PostBatch` duplicated 2×) |
| Bug | **0** | — |
| Spec-Gap | **0** | — |
| Security | **0** (code-review side) — security audit findings counted separately | — |
| Performance | **0** | — |
| Style | **0** | — |
| Scope | **0** | unchanged |
| Design-Decision Documented | **1 Low (batch 03)** | `SatelliteProvider.Api/Validators/RoutePointValidator.cs` — `OverridePropertyName` chain ordering documented inline to prevent future "simplification" |
| Info (rationale documented) | **2** | `SatelliteProvider.Api/DTOs/GetTileByLatLonQuery.cs` (nullable types rationale, batch 02); `SatelliteProvider.Api/Validators/UavTileMetadataValidator.cs` metadata-key wire shape (batch 04) |

### Security audit (cycle 8)

| Metric | Value | Δ vs cycle 7 |
|--------|-------|--------------|
| Verdict | **PASS_WITH_WARNINGS** (after in-cycle F-AZ809-1 resolution) | unchanged (cycle 7 also PASS_WITH_WARNINGS) |
| Reason | F-AZ809-1 Medium (DoS via unbounded `geofences.polygons`) resolved in-cycle via `MaxPolygons = 50` cap + unit + integration test + `route-creation.md` v1.0.1 patch bump (commit `8fca6e0`). F-AZ810-1 + F-AZ810-2 (Lows) carried forward as cycle-9 candidates. | new resolved-in-cycle pattern |
| New Critical / High | **0** | unchanged |
| New Medium | **1 found, 1 resolved in-cycle** (F-AZ809-1) | first time a security-audit finding was resolved within the cycle it was discovered |
| New Low | **2** (F-AZ810-1 `JsonException.Message` echo in `UavUploadValidationFilter`; F-AZ810-2 `DateTime` vs `DateTimeOffset` for `CapturedAt`) | -1 (cycle 7 had 3 new Lows) |
| Carry-overs (still OPEN, unchanged from cycle 7) | **4** (F-AZ795-1 + F-AZ795-2 + D-AZ795-1 + D2-cy4) | +1 (D2-cy4 explicitly re-flagged as carry-over in this cycle's dependency scan) |

**Audit shape**: cycle 8 ran the full 5-phase security skill (Dependency Scan, Static Analysis, OWASP Review, Infrastructure Review, Security Report) — second consecutive cycle to run all 5 phases (cycle 7 was the first). Audit was justified by 4 new validator files + 4 new endpoint contracts + 1 new endpoint filter + 1 new multipart envelope filter. **Cycle 8 is the first cycle where a security-audit Medium finding (F-AZ809-1) was resolved within the same cycle it was discovered** (commit `8fca6e0`, ~30 minutes from discovery to fix landed). The user-driven A/B/C gate at Step 14 explicitly enabled this; the autodev orchestrator now treats in-cycle finding resolution as a first-class option rather than always deferring to cycle N+1.

### Performance gate (cycle 8)

| Metric | Value |
|--------|-------|
| Verdict | **PASS** (after in-cycle perf-script contract fix; see §4 Pattern 3) |
| Scenarios | **8 Pass · 0 Warn · 0 Fail · 0 Unverified** — PT-01..PT-08 |
| Script exit code | **0** (third consecutive clean exit-0 — cycle 6, 7, 8) |
| AZ-808..AZ-812 contract regression check | **PASS** — every cycle-8 validator surface measured below percentile resolution; PT-06 (route create) initially failed with HTTP 400 because the perf script omitted the cycle-8 newly-required `requestMaps` and `createTilesZip` fields (one-line script fix landed in the same commit as the perf report — `32bc5c1`). The miss was a perf-script contract sync gap — the script had been updated for AZ-812's `lat`/`lon` rename but not for AZ-809's new required fields. |
| Cycle-8 validator overhead | **invisible** at percentile resolution. PT-03/04/05/07 (region POST) measurements all within noise band vs cycle 7. PT-06 (route POST) at 83 ms (post-fix) vs cycle 7's 161 ms — both in the noise band. PT-08 (UAV upload) at 379 ms batch p95 vs cycle 7's 284 ms — within historical band (cycle 5-7 range: 284-544 ms). |
| F-AZ809-1 polygon-cap fix overhead | **invisible** — the new `MaxPolygons = 50` check is a single `Count <= 50` comparison; the cap's intent is allocation-bounding under adversarial load, not steady-state latency, and a dedicated adversarial scenario is recommended for cycle 9 (recorded in `perf_2026-05-23_cycle8.md` § Follow-ups). |
| Cross-cycle leftover handling | **0 new leftovers** in `_docs/_process_leftovers/`. The PT-06 sync-miss was fixed inline (Action 3-style mechanical fix), not deferred. |

## 3. Structural Metrics (cycle-8 delta — first snapshot since cycle 5)

| Metric | Cycle 8 | Δ vs cycle 5 (last snapshot) |
|--------|---------|-------------------------------|
| Project count | **9** | unchanged |
| Cross-project ProjectReference edges | **21** | unchanged (3 consecutive cycles with zero new edges) |
| Cycles in project import graph | **0** | unchanged (5 consecutive cycles clean DAG) |
| Max in-degree | Common at **7** | unchanged |
| Max out-degree | Tests at **7** | unchanged |
| Migrations | 15 (cycle 5: 14) | +1 (added by cycle 6 — AZ-505 leaflet index; no migrations in cycles 7 or 8) |
| Contracts in `_docs/02_document/contracts/api/` | **6** (error-shape + region-request + route-creation + tile-inventory + tile-latlon + uav-tile-upload) | +5 vs cycle 5 (cycle 5 had only `uav-tile-upload`; cycles 6/7/8 added the rest) |
| **Cycle 8 alone**: new contracts | **3** (region-request v1.0.0 + route-creation v1.0.0→v1.0.1 + tile-latlon v1.0.0) | new pattern: cycle 8 more than doubled the cycle-5 contract count |
| **Cycle 8 alone**: contract MINOR bumps | **1** (uav-tile-upload v1.1.0 → v1.2.0) | — |
| **Cycle 8 alone**: contract PATCH bumps | **1** (route-creation v1.0.0 → v1.0.1, F-AZ809-1 follow-up) | new pattern: first patch bump on a same-cycle contract |
| **Cycle 8 alone**: contract MAJOR bumps | 0 | — |
| Contract coverage of documented endpoints | **5 / 7** = **71%** | up from cycle 7's 2 / 7 = 29% (cycle 8 closed the documented-but-uncontract gap for region + route + lat/lon) |
| New NuGet packages | **0** | unchanged (3 consecutive zero-bump cycles) |
| `SatelliteProvider.Api/Validators/` file count | **13** | +9 (cycle 8 added 9 new validator files; cycle 7 had added the 4 foundation files) |
| New unit-test methods in cycle 8 | **63** (across 8 new test files in `SatelliteProvider.Tests/Validators/`) | — |
| New integration-test methods in cycle 8 | **52** (across 5 new test files in `SatelliteProvider.IntegrationTests/*ValidationTests.cs`) | — |
| New probe scripts in cycle 8 | **4** (probe_region_validation.sh + probe_latlon_validation.sh + probe_route_validation.sh + probe_upload_validation.sh) | — |
| Net Architecture delta | **-1** | first negative delta since cycle 4 (Pattern 1 from cycle 7 closed; F-AZ809-1 Medium resolved in-cycle so net contribution 0; only Lows carry forward) |

Detail: `_docs/06_metrics/structure_2026-05-23_cycle8.md`.

**Cycle 6 + 7 structure snapshot gap**: cycles 6 and 7 did not write a `structure_*.md`. The cycle-8 snapshot is computed vs cycle 5 (the most recent), so the cycle-6 + cycle-7 deltas are folded into the cycle-8 row. **Process recommendation**: codify the structure-snapshot write as a Step-17 hard requirement in `.cursor/skills/retrospective/SKILL.md` Step 1 self-verification (currently soft-checked).

## 4. Trend Comparison (cycle 7 → cycle 8)

| Trend | Direction | Notes |
|-------|-----------|-------|
| Tasks per cycle | **+2** (3 → 5) | Cycle 8 was the AZ-795 child-task sweep — 4 endpoint validators + 1 prerequisite rename. The increased task count is by design. |
| Complexity per cycle | **+9 SP** (8 → 17) | Largest single-cycle SP delivery in project history. |
| Batches per cycle | **+3** (1 → 4) | Per-batch slicing kept each batch ≤ 5 SP and made per-batch reviews tractable (cycle 7 was 1 batch × 8 SP — heavier review per artifact). |
| Findings volume (post-review) | **+3 Low** (3 → 6) | Code-review Lows are all test-helper DRY (cycle-9 follow-up: promote `FixedTimeProvider` + `PostBatch` to `SatelliteProvider.TestSupport`). Security audit added 2 new Lows + 1 Medium **resolved in-cycle**. |
| Code-review pass rate | **n/a → 100% PASS_WITH_*** (4 / 4 batches reviewed; 0 FAILs) | First cycle with a per-batch review file for every batch; cycle 7 had none. |
| Leftovers carried out | **unchanged** (0 → 0) | Third consecutive cycle with zero new process leftovers (cycle 6 was the first). |
| Architectural cycles introduced | **0** (unchanged) | 5 consecutive cycles with a clean DAG. |
| Contract artifacts produced | **+3** (1 → 4: 3 new + 1 minor bump + 1 patch) | Cycle 8 published 3 new contracts (region-request + route-creation + tile-latlon) and bumped `uav-tile-upload` v1.1.0 → v1.2.0 + `route-creation` v1.0.0 → v1.0.1 (in-cycle F-AZ809-1 follow-up). |
| Migrations | **0** (unchanged 0 → 0) | Second consecutive migration-free cycle. |
| Step 14 (Security Audit) outcome | **PASS_WITH_WARNINGS** (unchanged — cycle 7 was also PASS_WITH_WARNINGS) | First cycle where a Medium finding was resolved in-cycle (F-AZ809-1 → commit `8fca6e0`). |
| Step 15 (Performance Test) script exit | **0** (unchanged) | Third consecutive clean exit-0; required one in-cycle perf-script fix for the AZ-809 contract sync miss. |
| Cross-cycle bug-introduction pattern | **0 new** | Nothing newly broken this cycle. The strict-validation surface continued to surface latent bugs (`UavUploadTests.NextTestCoordinate` lat-out-of-range — see Pattern 2 below — same family as cycle 7's `IdempotentPostTests` discovery), but they're pre-existing bugs surfaced by the new strict layer, not cycle-8 regressions. |
| Mid-cycle scope decisions (split / defer / re-spec) | **0** | No mid-cycle pivots; all 5 tasks shipped as planned across 4 batches. |
| Post-merge correction commits | **+1** (0 → 1) | Cycle 8 had one in-cycle fix commit (F-AZ809-1) AND one perf-script fix folded into the perf-report commit. Both are policy-driven (security-audit and perf-gate findings respectively), not error-driven (vs cycle 6's AC-5 TLS pivot which was a planning miss). |
| Implementation report artifact | **PRESENT** | Closes cycle-7 Pattern 1. Implementation report + completeness report + cumulative cross-batch review all written. |
| Per-batch review files | **4** (one per batch) | Closes cycle-7 process gap on per-batch reviews. Cycle 7 had 0 review files; cycle 8 has 4. |
| In-cycle security-finding resolution | **NEW** (F-AZ809-1) | First cycle to resolve a security-audit Medium within the cycle it was discovered. |

**Cumulative LESSONS reuse**: 5 lessons from previous cycles were directly applicable this cycle:

- **2026-05-22 process lesson on implement-skill artifact contract (cycle 7)** — directly closed by cycle 8: every cycle-8 batch wrote a per-batch report AND a per-batch review file, the cumulative cross-batch review fired (`cumulative_review_batches_01-04_cycle8_report.md`), the implementation phase ended with a consolidated `implementation_report_strict_validation_cycle8.md` AND a `implementation_completeness_cycle8_report.md`. Four artifact types where cycle 7 had two. The lesson's recommendation B (codify the fallback) was not formally written into the skill files — instead, the implement skill self-corrected by producing the artifacts. Either approach satisfies the underlying need.
- **2026-05-22 testing lesson on strict-validation surfaces latent bugs (cycle 7)** — directly applied this cycle: `UavUploadTests.NextTestCoordinate` had been seeding `(Ticks/TicksPerSecond) % 1_000_000` as the latitude offset since cycle 2, which produced values up to 160° (far above 90°). AZ-488's happy-path test never noticed because there was no lat/lon validator at the upload endpoint. AZ-810 added the validator and the cycle-8 batch-4 smoke run caught the bug. Fix was a 2-file clamp (UavUploadTests + UavUploadValidationTests use non-overlapping clamped ranges to preserve per-source UNIQUE-index safety). Same family as cycle 7's `IdempotentPostTests` discovery — the strict-validation surface continues to surface latent test-fixture bugs that the prior lenient defaults masked.
- **2026-05-23 process lesson on no-regression AC verification (cycle 8 batch 4)** — added inline by the implement skill during the AZ-810 batch-4 batch report (already in the ring buffer from a prior commit, dated 2026-05-23). Directly invoked the next day during the cycle-8 retro Step 1 self-verification.
- **2026-05-12 process lesson on Deferred-NFR ring-buffer (cycle 2)** — cycle 8 reused: the F-AZ809-1 finding was resolved in-cycle rather than deferred to cycle 9, preventing it from becoming a multi-cycle item.
- **2026-05-12 dependencies lesson on Major-version bumps (cycle 4)** — cycle 8 reused (negatively): cycle 8 declined to take the cycle-7 D-AZ795-1 FluentValidation 12.0.0 → 12.1.1 bump per scope discipline. The lesson's "verify transitive drift" precondition was satisfied (D-AZ795-1's static analysis re-confirmed no behavioural change in 12.1.1).

## 5. Patterns and Insights

### Pattern 1 — Cycle 7 Action 3 (AZ-795 child-task sweep) ships end-to-end in cycle 8 (NEW positive pattern)

Cycle 7's retro § 5 Action 3 recommended a per-endpoint child-task sweep under AZ-795 to add strict validation to `POST /api/satellite/request`, `POST /api/satellite/route`, `POST /api/satellite/upload`, and `GET /api/satellite/tiles/latlon`. **Cycle 8 shipped all four** — AZ-808 + AZ-809 + AZ-810 + AZ-811 — plus the prerequisite AZ-812 OSM rename that was a coordination-clause dependency.

**Why this matters**: this is the first cycle in the project's history where a cycle-N retrospective recommendation was both (a) tracker-captured as new PBIs in cycle N+1's spec slate and (b) shipped end-to-end in cycle N+1. Prior cycles had carry-overs (e.g., the cycle-3 perf-harness leftover that lingered until cycle 6), but those were single-task or single-fix items. Cycle 7's recommendation was a 4-task sweep + 1 coordination prerequisite, and cycle 8 delivered all 5 as a coherent cycle theme.

**Insight**: retrospective recommendations have measurable impact when (a) the recommendation explicitly names concrete tracker tickets / files / endpoints (cycle 7's Action 3 named the 3-4 endpoints + the SP sizing), (b) the next cycle's planning phase pulls the recommendation directly into the task slate without re-deriving scope, and (c) the recommendation is sized as a coherent cycle theme rather than a one-off fix. Cycle 7's Action 3 satisfied all three. Cycle 8's retrospective should treat this as the gold standard for recommendation phrasing.

### Pattern 2 — Strict-validation surface surfaced one more latent test-fixture bug (continuing positive pattern from cycle 7)

`UavUploadTests.NextTestCoordinate` had a latent bug since cycle 2: the seed `lat = (Ticks/TicksPerSecond) % 1_000_000` produced lat values far above 90° (at iteration n=200_000 → lat=160°). The bug never fired because (a) AZ-488 had no validator at the upload endpoint, (b) no DB constraint on lat range, (c) the test only checked HTTP 200 (which the unvalidated endpoint returned). AZ-810 added the validator and the cycle-8 smoke run caught it. The 2-file clamp fix landed in batch 4 alongside AZ-810 (`UavUploadTests` to [50,70) lat × [10,40) lon; `UavUploadValidationTests` to [-70,-50) × [-40,-10) for per-source UNIQUE-index safety).

**Why this matters**: cycle 7's identical pattern was `IdempotentPostTests` posting `{"Lat":..., "Lon":...}` against a `[JsonPropertyName("lat")]` DTO. Cycle 8's `NextTestCoordinate` bug is the second instance in two consecutive cycles. **The strict-validation rollout is a multi-cycle latent-bug excavation** — every new validator surface uncovers another bug that lenient defaults had masked.

**Insight**: when a strict-validation child task lands (e.g., one of the AZ-795 child tasks), the test-fixture inspection scope should include not just the new validator's tests but also the older happy-path tests of the same endpoint. A 30-minute "what test data does the prior happy-path test send, and does it satisfy the new validator?" check at spec-write time would have caught the `NextTestCoordinate` bug before the implementation phase began. **Add this to cycle 9's task-spec quality gate.**

### Pattern 3 — Perf-script contract sync is a partial-update class of bug (NEW pattern)

The cycle-8 batch-1 AZ-812 work updated `scripts/run-performance-tests.sh` PT-03/04/05/07 JSON bodies from `latitude/longitude` to `lat/lon`. The cycle-8 batch-3 AZ-809 work added new required fields (`requestMaps`, `createTilesZip`) to the route-creation contract. **The perf script's PT-06 body was NOT updated with the new required fields during batch 3.** The omission was caught at Step 15 (Performance Test gate) when PT-06 returned HTTP 400; a 1-line script fix landed alongside the perf report in commit `32bc5c1`.

**Why this matters**: cross-script wire-format sync is a recurring class of bug. Cycle 7 had the same family of issue when migrating `tile-inventory.md` 1.0.0 → 2.0.0 (`tileZoom/tileX/tileY → z/x/y`) — the perf script was updated, the integration tests were updated, but the cycle-7 deploy-report's example curl was a leftover sync miss that cycle 7's review caught manually. Cycle 8 has the same flavor (perf-script PT-06 partial sync).

**Insight**: there is no single owner for "every place that constructs the wire format outside of the production DTOs." The contract docs are the source of truth for the format, but the consumers (perf script, probe scripts, README example URLs, deploy-report example bodies, blackbox-tests.md trigger excerpts, OpenAPI examples) are scattered across the repo. **Recommendation**: add a `scripts/check_wire_format_consumers.sh` (cycle 9 candidate, ~2 SP) that ripgrep's the canonical contract-doc paths and reports any file path that has fewer wire-key matches than expected. This is a static-check level of defense — not a full schema verifier, just a "the field name is mentioned in N consumers, here are the N consumers, did you update them all?" surface.

### Pattern 4 — In-cycle security-finding resolution is a new tool in the autodev toolbox (NEW positive pattern)

Cycle 8 is the first cycle where a Step-14 security-audit Medium finding (F-AZ809-1) was resolved within the same autodev invocation: discovered in the audit (commit `ac40a8b`), fixed in commit `8fca6e0` (`MaxPolygons = 50` cap + unit test + integration test + `route-creation.md` v1.0.1 patch bump + traceability row AZ-809 AC-1b), all in ~30 minutes. The user-driven A/B/C gate at Step 14 explicitly enabled this: option A was "fix F-AZ809-1 NOW" rather than the historical default "defer to cycle N+1."

**Why this matters**: every prior cycle's security audit deferred all findings to a follow-up cycle (or the deploy-report's "Recommended follow-ups" section). The deferral pattern is correct for Low / informational findings and for any finding whose fix is non-trivial, but for a Medium finding whose fix is a one-line cap + ~5 lines of tests + a contract patch bump, the in-cycle resolution is meaningfully better: (a) the fix ships with the same commit chain that introduced the surface (one history rather than "introduced in cycle N, fixed in cycle N+1"), (b) the contract version reflects the fix immediately (route-creation.md v1.0.1 not v1.0.0), (c) the traceability matrix and blackbox-tests.md sub-cases are written at the time the finding is fresh, not from a stale finding description three weeks later.

**Insight**: the autodev orchestrator's Step-14 Optional Skill Gate should explicitly enumerate "fix-in-cycle" as a first-class A/B/C option for Medium findings whose fix is small (≤2 SP, ≤2 files, ≤1 contract bump). The current cycle-8 prompt did this ad-hoc; cycle 9's autodev flow should codify the threshold. **Recommendation**: amend `.cursor/skills/autodev/flows/existing-code.md` Step-14 action with a sub-clause: "If Step 14 surfaces a Medium finding whose remediation fits the small-fix threshold (≤2 SP, ≤2 files, ≤1 contract bump), present a 3-way gate: A) fix in-cycle then proceed | B) defer to cycle N+1 | C) skip Step 15+16+16.5 and end cycle. Default recommend A."

### Pattern 5 — Cycle 8 sustained the "zero new architecture-layer findings" streak across the project's largest delivery cycle (NEW positive pattern)

Cycle 8 delivered the most code volume in any single cycle of the project's history (5 tasks, 17 SP, 9 new validator files, 63 new unit tests, 52 new integration tests, 3 new contracts, 4 new probe scripts) AND held every structural metric stable:

- 0 new cross-project edges (3 consecutive cycles).
- 0 new csproj (5 consecutive cycles).
- 0 import-graph cycles (5 consecutive cycles).
- 0 new NuGet packages (3 consecutive cycles).
- 0 schema migrations (2 consecutive cycles).
- 0 new architecture-layer findings (after F-AZ809-1 resolution).

**Why this matters**: large-volume cycles historically correlate with new architecture deltas (cycle 1 added 4 csproj; cycle 4 added FluentValidation; cycle 5 added the Uuidv5 cross-cutting + 1 new import edge). Cycle 8 broke that correlation — the entire delivery stayed inside the established layer boundaries (`Api/Validators/` + `Common/DTO` annotations + `Tests/Validators/` + `IntegrationTests/*ValidationTests` + probe scripts + contract docs).

**Insight**: when a cycle's theme can be expressed as "add files in one well-defined directory + extend one existing API contract", the architecture-stability tax is near-zero even at high task volume. Cycles 9+ should explicitly favor task slates that share a single architectural surface (vs cross-cutting slates that touch multiple layers). **Recommendation**: when sizing a cycle's task slate, ask "are all tasks landing in ≤3 new files in the same namespace + ≤2 existing contract bumps?" — if yes, the cycle can safely deliver 4-5 tasks; if no, cap at 2-3 tasks.

## 6. Top 3 Improvement Actions

### Action 1 — Promote `FixedTimeProvider` and `PostBatch` test helpers to `SatelliteProvider.TestSupport`

**Why**: Pattern 1 from § 4 surfaced two DRY follow-ups during the cumulative review: `FixedTimeProvider` is duplicated across 4 test files (cycle-2 advisory crossed by cycle-8 batch 4 — "if a 3rd consumer appears, promote"); `PostBatch` is duplicated between `UavUploadTests.cs` and `UavUploadValidationTests.cs`. Cycle 8 explicitly recorded both as follow-up PBI candidates (`implementation_report_strict_validation_cycle8.md` § Follow-up PBIs).

**Action**: create AZ-NNN-promote-time-and-postbatch (2 SP — promote `FixedTimeProvider` to `SatelliteProvider.TestSupport.Time` + promote `PostBatch` to `SatelliteProvider.TestSupport.UavUploadMultipartFixture` + update 4 + 2 consuming test files). Sized as 2 SP because it's mechanical move + adjust imports + verify suite stays green. **Cycle 9 candidate, single batch.**

**Cost**: ~1 hour of dev work. Counted as 2 SP because it touches 6 files across 2 test projects.

### Action 2 — Add the strict-validation-fixture cross-check to task-spec quality gate

**Why**: Pattern 2 from § 4. The `NextTestCoordinate` latent-bug discovery cost the cycle-8 batch-4 implementation phase an extra ~30 minutes of investigation + a 2-file clamp fix. A 30-minute spec-write-time inspection ("does the prior happy-path test's data satisfy the new validator?") would have caught it before the implementation phase began. This is a process-level change to the task-spec quality gate, not a one-off fix.

**Action**: amend `.cursor/skills/new-task/SKILL.md` (or `.cursor/skills/plan/SKILL.md` if that's where strict-validation child specs are written) with a new spec-quality checklist item: "**Fixture pre-check**: for every existing test file that exercises the endpoint being newly validated, identify the test-data generator (helper, fixture, constant), trace one representative value end-to-end through the new validator rules, and explicitly confirm in the spec that the prior data satisfies the new rules — or document the required test-data update." ~1 SP.

**Cost**: ~30 minutes of skill-author work. Counted as 1 SP because it's a single checklist item added to one (or two) skill files.

### Action 3 — Build a `scripts/check_wire_format_consumers.sh` static-check probe

**Why**: Pattern 3 from § 4. Perf-script PT-06 partial sync was a known class of bug (cycle 7 had a similar instance with `tile-inventory.md` 1.0.0 → 2.0.0). There is no single owner for "every place that constructs the wire format outside the production DTOs," and the consumers are scattered (perf script, probe scripts, README example URLs, deploy-report example bodies, blackbox-tests.md trigger excerpts, OpenAPI examples). A static-check probe at the cycle-8 closure level would have caught the PT-06 omission before Step 15.

**Action**: create AZ-NNN-wire-format-consumer-check (2 SP — write `scripts/check_wire_format_consumers.sh` that ripgrep's the canonical contract-doc paths under `_docs/02_document/contracts/api/` and reports any consumer file path that mentions an old wire-key the contract has retired; integrate into the test-spec sync skill's quality gate). Initial implementation can be a simple "for each contract doc, extract the JSON field names from the contract's example body; for each consumer path, count matches; flag any consumer with fewer matches than expected after a known rename." ~2 SP.

**Cost**: ~1 hour of skill + script work. Counted as 2 SP because it requires (a) the bash script, (b) integration into the test-spec skill's quality gate, and (c) one validation run against cycle 8's actual surface to confirm the cycle-8 PT-06 miss would have been caught.

## 7. Carry-overs (status this cycle)

| Item | Status | Notes |
|------|--------|-------|
| `_docs/_process_leftovers/` folder state | **EMPTY** as of cycle 8 entry and exit | Third consecutive cycle with zero process leftovers. |
| Microsoft.NET.Test.Sdk 17.8.0 transitive `NuGet.Frameworks` NU1902 (D2-cy4, **Medium**) | OPEN (carried from cycles 4 + 5 + 6 + 7 + 8) | No cycle-8 touch. Re-flagged in `dependency_scan_cycle8.md` carry-over table. |
| Microsoft.IdentityModel.Tokens / System.IdentityModel.Tokens.Jwt 7.0.3 NU1902 (Low) | OPEN (carried from cycles 3 + 4 + 5 + 6 + 7 + 8) | No cycle-8 touch. Re-flagged. |
| Serilog.AspNetCore 8.0.3 → 10.x recheck (Low) | OPEN (carried from cycles 4 + 5 + 6 + 7 + 8) | No cycle-8 touch. Re-flagged. |
| ASPDEPR002 `WithOpenApi(...)` deprecation | OPEN (carried from cycles 4 + 5 + 6 + 7 + 8) | No cycle-8 touch. |
| Admin team `iss/aud` confirmation (carried from cycle 3) | OPEN (still required before promoting beyond `dev`) | Re-listed. |
| `metadata.flightId` authenticated provenance (F1-cy5) | OPEN (long-term, not actionable until flight registry exists) | Re-listed. |
| `pgcrypto` ops gap (F2-cy5) | OPEN (doc-only fix, ~30 min) | Re-listed. |
| Deployment runbook: ingress TLS termination + HTTP/2 forwarding (cy6 follow-up) | OPEN (no cycle-7 or cycle-8 touch) | Re-listed. |
| `tile-storage.md` consumer audit post v2.0.0 (cy6 follow-up) | OPEN | Re-listed. |
| Inventory endpoint `estimatedBytes` field (AZ-505 deferral) | OPEN | Re-listed. |
| HTTP/3 / QUIC dev listener (AZ-505 deferral) | OPEN | Re-listed. |
| Cycle-7 D-AZ795-1: FluentValidation 12.0.0 → 12.1.1 bump | OPEN (carried from cycle 7) | Cycle 8 explicitly excluded NuGet hygiene per scope discipline. |
| Cycle-7 F-AZ795-1 + F-AZ795-2: sanitize `JsonException.Message` + `BadHttpRequestException.Message` in 400 detail | OPEN (carried from cycle 7) | Cycle 8 added a third instance of this class (F-AZ810-1 in `UavUploadValidationFilter`) — see new cycle-8 entry below. |
| Cycle-7 implementation-report exit-gate / fallback formalisation | **CLOSED in-cycle by cycle 8** | Cycle 8 produced both per-batch reports AND a consolidated implementation report AND a completeness report AND a cumulative cross-batch review — four artifact types where cycle 7 had two. The implement skill self-corrected without a rule-file change. |
| Cycle-7 AZ-795 child-task sweep across remaining public endpoints | **CLOSED in-cycle by cycle 8** | Cycle 8 shipped all four AZ-795 child tasks (AZ-808 + AZ-809 + AZ-810 + AZ-811) + the prerequisite AZ-812 rename. See § 4 Pattern 1. |
| **NEW (cycle 8)** F-AZ809-1: unbounded `geofences.polygons` DoS | **CLOSED in-cycle** (commit `8fca6e0`) | First cycle-N security-audit Medium resolved within cycle N. |
| **NEW (cycle 8)** F-AZ810-1: `JsonException.Message` echo in `UavUploadValidationFilter` | OPEN — recommended | New instance of the cycle-7 F-AZ795-1 pattern in a second code path. Roll into the cycle-7 F-AZ795-1/2 sanitization PBI. |
| **NEW (cycle 8)** F-AZ810-2: `UavTileMetadata.CapturedAt` typed `DateTime` not `DateTimeOffset` | OPEN — informational | Freshness window drifts in non-UTC dev environments. Zero impact in UTC-deployed prod. Cycle 9 candidate, low priority. |
| **NEW (cycle 8)** Promote `FixedTimeProvider` + `PostBatch` test helpers to `SatelliteProvider.TestSupport` | OPEN — recommended | Action 1 above. ~2 SP. |
| **NEW (cycle 8)** Strict-validation-fixture cross-check in task-spec quality gate | OPEN — recommended | Action 2 above. ~1 SP. |
| **NEW (cycle 8)** `scripts/check_wire_format_consumers.sh` static probe | OPEN — recommended | Action 3 above. ~2 SP. |
| **NEW (cycle 8)** PT-07 perf-harness cross-run cache pollution (hard-coded base coords) | OPEN — perf harness cleanup | Documented in `perf_2026-05-23_cycle8.md` § Known harness quirks. Pre-existing issue surfaced by cycle-8 back-to-back perf runs. ~2 SP cycle-9 candidate. |
| **NEW (cycle 8)** F-AZ809-1 adversarial perf scenario (PT-NN — 50 + 51 polygons → measure validator latency and 400-response time) | OPEN — perf harness extension | Converts F-AZ809-1 cap intent (DoS-bound) into a measurable regression gate. ~3 SP cycle-9 candidate. |
| **NEW (cycle 8)** Structure-snapshot retro hard-gate | OPEN — process | Cycles 6 + 7 didn't write `structure_*.md`; codify as a Step-17 hard requirement in retrospective Step 1 self-verification. ~1 SP. |
| **NEW (cycle 8)** Autodev Step-14 in-cycle-fix Optional Skill Gate codification | OPEN — process | § 4 Pattern 4 — amend `.cursor/skills/autodev/flows/existing-code.md` Step-14 to add "fix-in-cycle" as a first-class A/B/C option for Medium findings whose fix is small (≤2 SP, ≤2 files, ≤1 contract bump). ~1 SP. |

**New leftovers carried out of cycle 8**: **0** (process leftovers folder remains empty). All cycle-8 follow-ups are tracked as recommended PBIs in this retro § 6 + § 7, not as process leftovers.

## 8. Suggested Rule / Skill Updates

| Target file | Change | Rationale |
|-------------|--------|-----------|
| `.cursor/skills/retrospective/SKILL.md` Step 1 self-verification | Promote `Structural snapshot written` from soft check to hard requirement (BLOCKING gate) when `module-layout.md` exists. Add: "If the most recent `structure_*.md` is more than 1 cycle old, compute the snapshot vs the most recent one and fold the intermediate cycles' deltas into the new snapshot's notes." | Pattern: cycles 6 + 7 skipped the snapshot; cycle 8 caught up but had to compute the delta vs cycle 5 directly. The hard gate prevents the gap from compounding. |
| `.cursor/skills/autodev/flows/existing-code.md` Step 14 action | Add: "If Step 14 surfaces a Medium finding whose remediation fits the small-fix threshold (≤2 SP, ≤2 files, ≤1 contract bump), present a 3-way gate: A) fix in-cycle then proceed | B) defer to cycle N+1 | C) skip Step 15+16+16.5 and end cycle. Default recommend A." | § 4 Pattern 4. Codifies the cycle-8 F-AZ809-1 resolution pattern as a first-class option. |
| `.cursor/skills/new-task/SKILL.md` (or `.cursor/skills/plan/SKILL.md` if strict-validation child specs are written there) — spec quality checklist | Add a "Fixture pre-check" item: "For every existing test file that exercises the endpoint being newly validated, identify the test-data generator (helper, fixture, constant), trace one representative value end-to-end through the new validator rules, and explicitly confirm in the spec that the prior data satisfies the new rules — or document the required test-data update." | § 6 Action 2. Would have caught the cycle-8 `NextTestCoordinate` latent bug at spec-write time. |
| `.cursor/skills/test-spec/SKILL.md` cycle-update mode | Add a "wire-format consumer cross-check" step: after any contract `[JsonPropertyName]` rename or new required-field addition, ripgrep the consumer paths (`scripts/run-performance-tests.sh`, `scripts/probe_*.sh`, `README.md` example URLs, `_docs/04_deploy/*.md` example bodies, `_docs/02_document/tests/blackbox-tests.md` trigger excerpts) and verify they reference the new field names. | § 6 Action 3, simplified — instead of a separate script, fold the check into the existing test-spec sync skill that already runs on contract changes. |
| `.cursor/skills/security/SKILL.md` Phase 5 (Security Report) | Add a "Resolved In-Cycle" row category to the consolidated security-report template, distinct from "Carried Forward". Document the cycle-8 F-AZ809-1 resolution as the reference example. | The current security-report template implicitly assumes all findings carry forward; cycle 8 demonstrated that in-cycle resolution is a viable third state. Codifying it in the template makes future reports clearer about what "PASS_WITH_WARNINGS" actually contains. |

## 9. Validations and Sources

- **Cycle-8 implementation artifacts parsed**: 4 batch reports (`batch_01..04_cycle8_report.md`), 4 review files (referenced from each batch report), 1 cumulative review (`cumulative_review_batches_01-04_cycle8_report.md`), 1 consolidated implementation report (`implementation_report_strict_validation_cycle8.md`), 1 completeness report (`implementation_completeness_cycle8_report.md`), 5 task specs in `_docs/02_tasks/done/`, 9 dev commits on the cycle-8 chain (full subject + body parsed for each).
- **Cycle-7 retro compared explicitly**: see § 4 trend table + § 5 Pattern 1.
- **Cycle-5 structure snapshot compared**: see § 3 + `structure_2026-05-23_cycle8.md` (cycles 6 + 7 deltas folded into the cycle-8 row).
- **Cross-cycle dependency tracking**: AZ-808 + AZ-809 + AZ-810 + AZ-811 all carry `Relates AZ-795` (the AZ-795 epic parent) in Jira. AZ-812 carries `blocks AZ-808` (the OSM rename must land before the region-POST validator's contract publishes with `lat`/`lon`). The cycle-5/6/7 lessons about explicit dependency-link choice were applied.
- **No skill-level escalations encountered**: no `retry_count: 3` failures in any sub-skill; no FAIL verdicts in any per-batch review or cumulative review; no FAIL verdict in security audit (after in-cycle F-AZ809-1 resolution); no FAIL verdict in perf gate (after in-cycle perf-script fix). Two in-cycle process-driven fixes (F-AZ809-1 + perf-script PT-06) executed cleanly via user A/B/C gates.

## 10. Self-Verification

- [x] All cycle-8 implementation artifacts parsed (4 batch reports, 4 review files, 1 cumulative review, 1 implementation report, 1 completeness report, 5 task specs, 9 commit bodies, perf report, all 5 security audit reports, structure snapshot).
- [x] Comparison with cycle-7 retro performed (§ 4 trend table).
- [x] Top 3 improvement actions concrete and actionable (§ 6).
- [x] Suggested rule/skill updates specific and tied to a target file (§ 8).
- [x] New cycle-8 follow-up PBIs (Action 1 + 2 + 3 + F-AZ810-1/2 + perf-harness cleanups + structure-snapshot hard-gate + Step-14 in-cycle-fix codification) cross-referenced between § 6, § 7, and the security/perf reports.
- [x] LESSONS.md ring buffer to be appended with top 3 cycle-8 lessons (§ 5 Patterns 1, 2, 4 distilled to single-sentence form) — applied in next step.
- [x] Structural snapshot written (`structure_2026-05-23_cycle8.md`); cycle-6 + cycle-7 deltas folded; gap noted as cycle-9 follow-up (Suggested Rule Update 1).
