[AZ-512] Cycle 4 closure: deploy + retro + lessons + state

Closes cycle 4 (AZ-512 admin class inline edit).

Steps 16-17 artifacts:
- deploy_cycle4_report.md: ui/ dev pushed (09449bd..8737491, 4 commits,
  fast-forward); stage/main and admin/ dev deferred at the push-scope
  gate (option A; same as cycle 3). AZ-513 admin/ implementation +
  deploy gate stays open as the cross-workspace prerequisite.
- retro_2026-05-13_cycle4.md: PASS_WITH_WARNINGS verdict carries;
  243 PASS / 13 SKIP / 0 FAIL; bundle 291 332 B (+757 B / +0.26%);
  net architecture delta 0; user-action backlog 7 -> 9 (rate
  decelerating from +4 to +2); first cycle where the user explicitly
  overrode a spec-conservative default (AZ-512 Option B).
- structure_2026-05-13_cycle4.md: identity-copy snapshot; no new
  components, no new gates, no new barrels, no new wire-contract
  assertions, no new architecture findings.
- LESSONS.md: top-3 cycle-4 lessons appended (testing/testing/process),
  ring buffer at 12 of 15.
- _autodev_state.md: cycle 4 closed, cycle 5 entered awaiting New Task.

Jira AZ-512: In Testing -> Done with cycle-4 closing comment.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-13 04:56:42 +03:00
parent 873749197a
commit eb1e8a8581
5 changed files with 409 additions and 7 deletions
@@ -0,0 +1,74 @@
# Cycle 4 Step 16 — Deploy Report
**Date**: 2026-05-13
**Cycle**: 4 (autodev existing-code Step 16)
**Mode chosen**: real cutover (option A in the cycle-4 deploy gate — "Push to ui/ dev only")
**Outcome**: ui/ dev pushed; stage/prod cutover deferred to a later turn; admin/ dev NOT pushed; cross-workspace AZ-513 still un-implemented.
## What was actually deployed
| Repo | Branch | Commits pushed | Pipeline triggered |
|------|--------|----------------|--------------------|
| `ui/` | `dev` (`09449bd..8737491`) | 4 | Woodpecker dev build for `ui/` |
| `admin/` | — | 0 | none |
### Commits pushed to `ui/` `origin/dev`
```
8737491 [AZ-512] Cycle 4 Steps 12-15: test-spec sync + docs + sec + perf
ecacfa8 [AZ-512] Admin class inline edit form + PATCH wiring (cy4 batch 16)
ef56d9c [AZ-512] chore: reactivate for cycle 4 (Option B path)
eef3bdf [AZ-509][AZ-510][AZ-511] Cycle 3 closure: deploy + retro + state
```
The cycle-3 closure commit `eef3bdf` was locally ahead since cycle 3's deploy step (deferred at the cycle-3 push-scope gate), and gets pushed now alongside cycle-4's three commits as a single fast-forward.
## What was NOT done (deferred / pending)
| ID | Item | Reason | Owner |
|----|------|--------|-------|
| D-CY4-STAGE | `ui/` `dev → stage → push origin/stage` | User chose option A (dev-only) at the cycle-4 deploy gate. Stage cutover deferred. **Will compound with cycle-3 stage deferral** — when stage cutover lands, it will ship cycles 3 + 4 simultaneously. | User |
| D-CY4-MAIN | `ui/` `stage → main → push origin/main` (prod cutover) | Same reason. Devices will not auto-pull cycle-3 + cycle-4 changes until this completes. | User |
| D-CY4-AZ513-IMPL | Implementation of AZ-513 (admin/ POST + PATCH + DELETE /classes routes) | Cross-workspace dependency: `admin/` workspace must implement before AZ-512 is functionally usable in any environment. Filed in Jira (AZ-513, parent epic AZ-509, Blocks AZ-512). UI ships with MSW-stubbed tests under user-authorized Option B — the live PATCH endpoint does not exist server-side yet, so the deployed `ui/` dev build will surface `admin.classes.updateFailed` on real edits. | admin/ team |
| D-CY4-ADMIN-PUSH | `admin/` `dev push origin/dev` | User did not select option C at the cycle-4 deploy gate. The AZ-513 task spec sits locally on `admin/` `dev` (since cycle 3). | User |
## Carry-forward from cycles 2 and 3
Cycle 2's `deploy_planning_sync_cycle2.md` deferred 3 items to leftovers in `_docs/_process_leftovers/2026-05-12_az-498-deploy-and-key-revocations.md`. Cycle 3 did not close any of them. Cycle 4 also did not close them:
| ID (origin) | Item | Status as of 2026-05-13 (cycle 4 close) |
|----|------|-------|
| L-AZ-498-DEPLOY | UI tile-swap prod cutover | Still deferred — cross-workspace satellite-provider gate unchanged. **Will compound with cycle-3 + cycle-4 stage/prod deferrals** when finally promoted. |
| L-AZ-499-OWM-REVOKE | OpenWeatherMap key revocation at owm dashboard | Still pending — manual third-party action; owner: user. |
| L-AZ-501-GOOGLE-REVOKE | Google Geocode key revocation at Google Cloud Console | Still pending — manual third-party action; owner: user. |
| L-AZ-512-ADMIN-PREREQ | AZ-513 implementation + ship in `admin/` workspace | Re-opened cycle 4 under user-authorized Option B. UI implementation now landed; gate stays open until admin/ AZ-513 ships AND deploys. |
These leftovers need a status sweep at the start of the next `/autodev` invocation per `tracker.mdc` Leftovers Mechanism.
## Cycle-4 deployment-doc deltas (NOT written this cycle)
In strict autodev terms, Step 16 in this cycle was a real cutover (option A), not a planning sync. The cycle-2 pattern of patching `_docs/02_document/deployment/*` was therefore skipped here because:
- AZ-512 introduced **no** changes to Dockerfile, `.woodpecker/`, env vars, or `nginx.conf` (verified inline during Step 14 security audit; the cycle-4 delta `security_report_cycle4_delta.md` enumerates the changed files).
- AZ-512's only wire-shape change is one new HTTP method on an existing URL (`PATCH /api/admin/classes/{id}` — already routed to `admin/` by `nginx.conf` since cycle 2 because `DELETE /api/admin/classes/{id}` was already proxied through the same route block).
- No new env vars, no new container, no new exposed port.
If a future cycle adds env vars, infra changes, or new services, the cycle-2 planning-sync pattern (update `environment_strategy.md`, `ci_cd_pipeline.md`, `containerization.md`, `observability.md`) should be applied.
## Verification
- `git push origin dev` for `ui/` returned `09449bd..8737491 dev -> dev` (4 commits, fast-forward).
- `git status -sb` for `ui/` confirms `dev` and `origin/dev` are synced post-push (no `[ahead N]`).
- Functional test suite green pre-push (243 passed, 13 quarantined skips, 0 failed — see `test-output/summary.csv` and `test-output/fast-report.xml`). Up +12 vs cycle 3 from the new `tests/admin_class_edit.test.tsx` suite.
- Static perf NFT-PERF-01 green pre-push (291 332 B gzipped vs ≤ 2 097 152 B threshold — see `test-output/performance-summary.txt` and `_docs/06_metrics/perf_2026-05-13_cycle4.md`).
- Security cycle-4 delta verdict PASS_WITH_WARNINGS pre-push (see `_docs/05_security/security_report_cycle4_delta.md`).
- No nginx/Docker/CI config changes in cycle 4.
- Cross-workspace deploy gate (AZ-513) explicitly acknowledged and re-recorded in this report and in the leftover entry. The deployed UI on `ui/` dev will return `admin.classes.updateFailed` on real PATCH attempts until `admin/` AZ-513 ships — by design under user-authorized Option B.
## Cycle-3 → cycle-4 push-scope progression
Cycle 3 deploy gate: user picked option A (ui/ dev only). Cycle 4 deploy gate: user picked option A again (ui/ dev only). The same trade-off applies — stage/prod cutover is being collected for a single later promotion. Two consecutive cycles of dev-only pushes means the eventual stage promotion will batch AZ-510 + AZ-511 + AZ-512 deltas into one stage build, with the additional gate that AZ-513 must have shipped in admin/ by that time (otherwise the AZ-512 edit feature renders but cannot complete saves).
## Auto-chain
→ Step 17 (Retrospective) for cycle 4.
+192
View File
@@ -0,0 +1,192 @@
# Retrospective — 2026-05-13 (Phase B Cycle 4)
**Mode**: cycle-end (autodev existing-code Step 17)
**Scope**: Phase B, cycle 4 (`state.cycle = 4`)
**Epic / theme**: AZ-509 (UI workspace cycle 3 epic, continued) — single carry-over task AZ-512 reactivated under user-authorized Option B after cycle 3's Cross-Workspace Verification BLOCKING gate
**Cycle duration**: 1 batch (batch 16) over 1 working day (2026-05-13)
**Previous retro**: `_docs/06_metrics/retro_2026-05-13_cycle3.md` (cycle 3, same calendar day)
## Implementation Summary
| Metric | Value | Δ vs cycle 3 |
|--------|-------|--------------|
| Tasks attempted | 1 (AZ-512) | 2 |
| Tasks delivered | 1 (AZ-512) | 1 (cycle 3 shipped 2, deferred 1) |
| Tasks deferred at spec gate | 0 (the only deferral was the cycle-3 carry, already reactivated this cycle) | 1 |
| Total batches | 1 (batch 16) | 2 |
| Total complexity points planned | 3 | 6 |
| Total complexity points delivered | 3 | 3 |
| Avg tasks per batch | 1 | 0 |
| Avg complexity per (completed) batch | 3 | 0 |
| Source files mutated | 5 production + test + 1 component-doc + 5 cross-cutting docs | n/a (different shape from cycle 3) |
| Cycle shape | Single-task reactivation cycle — user explicitly overrode the cycle-3 conservative-path default | new pattern |
Sources: `batch_16_cycle4_report.md`, `implementation_report_admin_class_edit_cycle4.md`, `deploy_cycle4_report.md`, `security_report_cycle4_delta.md`, `perf_2026-05-13_cycle4.md`, `structure_2026-05-13_cycle4.md`.
## Quality Metrics
### Code Review Results
| Verdict | Count | Percentage | Δ vs cycle 3 |
|---------|-------|-----------|--------------|
| PASS (inline self-review per batch report) | 1 (batch 16) | 100 % | +1 (cycle 3 had 2 PASS) |
| PASS_WITH_WARNINGS | 0 | 0 % | 0 |
| FAIL | 0 | 0 % | 0 |
| (no review — deferred at gate) | 0 | 0 % | 1 |
Note: batch 16 used inline self-review (3-point single-task batch). A formal `/code-review` skill run is scheduled for batch 18 (cumulative-review cadence is every K=3 batches).
### Findings by Severity (code review only)
| Severity | Count | Δ vs cycle 3 |
|----------|-------|--------------|
| Critical | 0 | 0 |
| High | 0 | 0 |
| Medium | 0 | 0 |
| Low | 0 | 0 |
### Findings by Category (code review)
All zero (cycle 3 was also all-zero). No new pattern.
### Security-Audit Findings (Step 14 — cycle 4 delta against cycle 3)
| Status change | Count | Notable IDs |
|---------------|-------|-------------|
| Closed | 0 | — |
| Newly introduced (LOW) | 1 | F-SAST-CY4-1 — lost-update / mid-air-collision on `PATCH /api/admin/classes/{id}` (by design per AZ-512 spec; promotes to a UI ticket only when AZ-513 lands and the backend's concurrency model is known) |
| Carried forward unchanged | 12 | F-SAST-1 (HIGH, git-history), F-SAST-CY3-1 (LOW, test-only barrel export), F-SAST-2/3/4, F-INF-1..5 |
**Security verdict trajectory**: cycle 3 PASS_WITH_WARNINGS → cycle 4 **PASS_WITH_WARNINGS** (unchanged). `bun audit` re-run clean. No OWASP category status flipped.
## Structural Metrics
Source: `_docs/06_metrics/structure_2026-05-13_cycle4.md` (this cycle), compared against `structure_2026-05-13.md` (cycle 3 close).
| Metric | Cycle 2 close | Cycle 3 close | Cycle 4 close | Δ vs cycle 3 |
|--------|---------------|---------------|---------------|--------------|
| Component count | 12 | 12 | 12 | 0 |
| Public-API barrels | 11 / 11 | 11 / 11 | 11 / 11 | 0 |
| STC-ARCH-01 carve-out exemptions | 1 | 0 | 0 | 0 (held at zero) |
| Commit-time static gates | 33 / 33 PASS | 33 / 33 PASS | 33 / 33 PASS | 0 |
| Architecture cycles | 0 | 0 | 0 | 0 |
| Architecture findings open (baseline F1F9) | 7 of 9 | 6 of 9 | 6 of 9 | 0 |
| Newly introduced architecture violations | 0 | 0 | 0 | 0 |
| Net architecture delta this cycle | 0 | 1 | **0** | reverted to net-zero |
| Wire-contract assertions (`endpoints.test.ts`) | 36 | 37 | 37 | 0 (AZ-512 reused `endpoints.admin.class(id)`) |
| Fast-profile suite | 229 PASS / 13 SKIP / 0 FAIL | 231 PASS / 13 SKIP / 0 FAIL | **243 PASS / 13 SKIP / 0 FAIL** | **+12 PASS**, 0 SKIP |
| Bundle (gzipped initial JS) | 290 465 B | 290 575 B | **291 332 B** | +757 B (+0.26 %; ~13.89 % budget) |
### Auto-lesson triggers (per skill Step 1)
- Net Architecture delta > 0? **No** — delta is 0.
- Structural metric regression > 20 %? **No** — every structural metric held; test count +5.2% (improvement); bundle +0.26% (well within budget).
- Contract coverage % decreased? **No** — wire-contract assertions held at 37.
- New finding category emerged? **No** — security audit ran in delta mode; categories stable.
**Zero auto-lesson triggers fired.** Manual lessons (3 picked) appear in §LESSONS Append below.
## Efficiency
| Metric | Value | Δ vs cycle 3 |
|--------|-------|--------------|
| Blocked tasks (cycle-internal) | 0 | 0 |
| Tasks deferred to backlog at spec gate | 0 | 1 (the cycle-3 deferral was the one reactivated here) |
| Cross-workspace prerequisite tickets filed | 0 | 1 (AZ-513 already filed in cycle 3) |
| Pre-existing bugs surfaced as side observations | 1 (MSW `/api/admin/users` paginated vs `AdminPage.tsx` flat-array expectation) | 0 |
| Tasks pending external user action (cycle-4 close) | **9** | +2 vs cycle 3's 7 |
| Tasks requiring fixes after review | 0 | 0 |
| Batch with most findings | none — 0 findings cycle-wide | n/a |
| Auto-fix loops invoked | 0 | 0 |
| Stuck-agent incidents | 0 | 0 |
| Unplanned implementation-time test stabilization loops | 1 — selector-target fix in `destructive_ux.test.tsx` after the ✎ button was inserted before `×` | 3 (cycle 3 had 4 for AZ-510's module-scoped state ripple) |
### Blocker Analysis
| Blocker Type | Count | Prevention |
|--------------|-------|-----------|
| Pre-existing bug surfaced during test writing | 1 | New cycle-4 lesson: when a new test mounts the full container component, run it once *without* defensive fixture overrides and let the natural crashes surface latent fixture-vs-source drift, then either fix or document — never silently work around. See Improvement Action #3. |
| Selector regression in adjacent test from new affordance | 1 | New cycle-4 lesson: adding a new control to a DOM row that already holds existing controls requires auditing the test corpus for selectors like `querySelector('button')` or `getByRole('button')` without disambiguation. See Improvement Action #2. |
| Cycle-3 deferred deploy items (carry) | 3 (D-CY3-STAGE/MAIN/ADMIN-PUSH) | Still not actioned. Cycle 4 added 3 more deploy-deferred items (D-CY4-STAGE/MAIN/ADMIN-PUSH). Compounding. |
| Cross-workspace deploy gate (carry from cycles 2 and 3) | 4 (L-AZ-498-DEPLOY, L-AZ-499-OWM-REVOKE, L-AZ-501-GOOGLE-REVOKE, L-AZ-512-ADMIN-PREREQ — last one re-opened cycle 4) | Same as cycle-3 retro Action #3 — drain mechanism still not implemented. |
### User-action backlog at cycle close
| Category | Count | Items |
|----------|-------|-------|
| Manual third-party console action | 2 | L-AZ-499-OWM-REVOKE, L-AZ-501-GOOGLE-REVOKE (carry from cycle 2) |
| Cross-workspace deploy gate (satellite-provider) | 1 | L-AZ-498-DEPLOY (carry from cycle 2) |
| Cross-workspace prerequisite ticket awaiting sibling-team work | 1 | AZ-513 implementation on `admin/` (re-opened cycle 4 under user-authorized Option B) |
| Cycle deploy-push pending | 5 | D-CY3-STAGE, D-CY3-MAIN, D-CY3-ADMIN-PUSH (carry); D-CY4-STAGE, D-CY4-MAIN, D-CY4-AZ513-IMPL — note the cycle-4 AZ-513 deploy gate is the same item as the cross-workspace prereq above when counted only once (de-dup) |
| **Total (de-duplicated)** | **9** | (cycle 1 close: 0 → cycle 2 close: 3 → cycle 3 close: 7 → cycle 4 close: **9**) |
> Trajectory continues: 0 → 3 → 7 → **9**. Net growth +2 this cycle (cycle 4 added D-CY4-STAGE + D-CY4-MAIN; AZ-513 re-opened as `cross-workspace prerequisite`; D-CY4-ADMIN-PUSH was carried-not-added because the user kept the same dev-only push scope as cycle 3). Cycle-3 retro Improvement Action #3 (track backlog as first-class metric) is now being applied — but the drain mechanism (step-0 sweep that closes items, not just notices them) is still pending. **Backlog growth is decelerating** (+3, +4, **+2**); even so, the gap between accumulated and drained remains the dominant signal.
### User-decision points (cycle 4 only)
- Cycle-4 entry: user **explicitly overrode** the cycle-3 conservative-path default for AZ-512 ("implement 512, 513 would be implemented in minutes. You can write mocks for backend data anyway for testing."). The spec was updated to record this as user-authorized Option B; the leftover entry was re-opened with the Option-B rationale. This is the first cross-cycle override of a spec-conservative default in cycles 1-4.
- Step 13 → Steps 14+15 gate: user chose **D** (run both Security Audit AND Performance Test) — first time across cycles 1-4 that BOTH optional gates ran inline. Cycle 3 also ran both via auto-chain but Step 15 emitted no separate report; cycle 4 produced standalone `perf_2026-05-13_cycle4.md` for the first time.
- Cycle-4 Step 16 deploy gate: user chose **A** (push to ui/ dev only) — same option as cycle 3. Stage / main / admin/ dev push deferred.
## Trend Comparison
| Trend | Cycle 1 | Cycle 2 | Cycle 3 | Cycle 4 | Direction |
|-------|---------|---------|---------|---------|-----------|
| Code review pass rate (formally-reviewed batches) | 100 % | 50 % | 100 % | 100 % (self-review) | held |
| Test count (cumulative this cycle delta) | +46 | +20 | +2 | **+12** | rebounded from cycle-3 low |
| Static gate count | +2 | +2 | 0 | **0** | held |
| Architecture findings open (baseline) | 7 (2) | 7 (0) | 6 (1) | **6 (0)** | held flat |
| STC-ARCH-01 exemptions | 1 | 1 | 0 | **0** | held at zero |
| Wire-contract assertions | 36 | 36 | 37 (+1) | **37 (0)** | held |
| Pending USER actions at cycle close | 0 | 3 | 7 | **9** | ⬆ still growing (rate decelerating) |
| Tasks deferred to backlog at spec gate | 0 | 0 | 1 | **0** | reverted (the cycle-3 deferral was the one reactivated) |
| Cycles where user overrode a spec-conservative default | 0 | 0 | 0 | **1** (AZ-512 Option B) | new pattern |
| Bundle (gzipped initial JS, B) | — | 290 465 | 290 575 (+110) | **291 332 (+757)** | growing in line with feature delta; far within budget |
Cycle 4 is the first single-task reactivation cycle (vs cycle 3's three-task fresh cycle). The cycle-3 retro called out that the AZ-512 gate worked as designed; cycle 4 confirms the *other half* of the design: a user-authorized override path can flow through the entire 9→17 step sequence without regressions, while preserving the deploy gate. Both halves of the gate design are now field-validated.
## Top 3 Improvement Actions
1. **Codify a "pre-existing-bug surface lifting" routine — observe-then-document, never silently work-around.**
While writing `tests/admin_class_edit.test.tsx`, I discovered the `/api/admin/users` MSW handler's paginated response vs `AdminPage.tsx`'s flat-array expectation by hitting a `users.map is not a function` render crash. The route taken was: document in batch_16_cycle4_report.md "Pre-existing bug noted", apply a local workaround (`stubUsersAsPlainArray()` in `beforeEach`), and recommend filing a separate UI-workspace ticket. This was the right tactical move, but the **systematic routine** is missing — there's no checklist anywhere that says "when a new test mounts a container component, run it once with default fixtures only, name any crashes, and decide explicitly fix-now vs document-and-defer." Without that routine, future cycles will keep accumulating quiet local workarounds and the side-observed bug list grows without a tracking artifact.
- Impact: medium — the failure mode (silent test-fixture overrides masking real source bugs) is the test-side analog of "client-side validation only" — looks green, but tested against a fake. Two distinct cycles (3 and 4) already surfaced one bug each through this route.
- Effort: low — add a section "Pre-existing-bug surfacing during test writing" to `_docs/02_tasks/_templates/module_scoped_state_introduction.md` (created in cycle 3) and to the implementation skill's batch-report template; require the batch report to either list "Pre-existing bug noted" entries or affirm "None observed; ran with default fixtures only".
2. **Audit test selectors that pick "the button" / "the link" / "the input" without disambiguation, before adding a new affordance to an existing DOM region.**
The cycle-4 ✎ edit button was inserted into the same `<td>` that holds the `×` delete button. `tests/destructive_ux.test.tsx` had three call sites using `firstRow.querySelector('button')` — each silently rebound to the new ✎ button instead of the old `×` button, and the tests would have shipped green-but-meaningless if not caught by the test run. The fix was 1-line per site (`Array.from(...).find(b => b.textContent === '×')`). The deeper lesson is that the failure mode is **invisible at code-review time** — a code reviewer reading the source diff has no view into which test selectors will resolve to which DOM element, only the test run reveals it. The cheap structural prevention: before adding a new control to a DOM region, grep the test corpus for `querySelector('button|input|a')` / `getByRole('button')` without name/text disambiguation, in the same file / sibling files, and add disambiguating selectors *in the affordance batch*.
- Impact: medium — saves one stabilization loop per affordance addition; the cost of NOT catching it is silent test-meaning-drift in destructive-UX assertions, which is exactly the kind of bug Finding B4 (cycle 1) was filed for.
- Effort: low — add a 3-bullet checklist to the implement skill's "Adjacent hygiene" rules: (a) before inserting a new button/input into an existing row/region, grep for non-disambiguated selectors targeting that region; (b) update them in the same commit; (c) if you can't make them disambiguated without changing the source DOM, prefer giving the new control a stable `data-testid` over rewriting test selectors.
3. **Add a "user-action backlog drain rate" to the retrospective metric set.**
Cycle 3 retro added "user-action backlog at cycle close" as an absolute count. Cycle 4 now has two consecutive data points (7 → 9). The signal in absolute count is being applied — but the signal that matters for process-shape is the **drain rate**: how many items got closed *this* cycle vs how many got added? Cycle 4: 1 item state-transitioned (L-AZ-512-ADMIN-PREREQ moved from "deferred awaiting AZ-513" to "re-opened under Option B"; technically still open), +2 net new (D-CY4-STAGE, D-CY4-MAIN). So drain = 0, add = 2, net = +2. Tracking drain explicitly will make the drain-mechanism conversation concrete — today the retro just says "backlog is +2, drain mechanism still pending" with no metric to optimize.
- Impact: medium — operationalizes cycle-3 Action #3. Makes the drain-mechanism design (which is presumably a step-0 sweep that closes items, not just notices them) measurable from the first cycle it runs.
- Effort: low — extend `.cursor/skills/retrospective/SKILL.md` Step 1 metric collection with a "User-action backlog drain rate" subsection (count of items added this cycle vs items closed this cycle vs net change vs absolute close-count), and add to the retrospective-report template.
## Suggested Rule / Skill Updates
| File | Change | Rationale |
|------|--------|-----------|
| `.cursor/skills/implement/SKILL.md` (Adjacent Hygiene section) | Add the 3-bullet "test selector audit" checklist for inserting a new control into an existing DOM region. | §Top 3 Improvement Action #2. |
| `.cursor/skills/implement/templates/batch_report.md` (if it exists) or `_docs/02_tasks/_templates/module_scoped_state_introduction.md` | Add "Pre-existing-bug surfacing during test writing" subsection requirement: batch report must explicitly list observed pre-existing bugs OR affirm "None observed; ran with default fixtures only". | §Top 3 Improvement Action #1. |
| `.cursor/skills/retrospective/SKILL.md` (Step 1 metrics) | Add **"User-action backlog drain rate"** metric: items added this cycle / items closed this cycle / net delta / absolute close-count; track alongside the absolute count introduced in cycle 3. | §Top 3 Improvement Action #3. |
| `.cursor/skills/retrospective/templates/retrospective-report.md` | Add a "User-action backlog drain rate" sub-table alongside the absolute table under Efficiency. | §Top 3 Improvement Action #3. |
| `_docs/LESSONS.md` (top) | Append the 3 lessons in §LESSONS Append below; trim to ≤ 15 entries. | Skill Step 4. |
## Notes — Step 16 outcome
Step 16 (Deploy) ran in **real-cutover mode (option A)** for the second consecutive cycle. Push scope was ui/ `dev` only (4 commits, fast-forward `09449bd..8737491`). The cycle-3 closure commit `eef3bdf` (which had been locally ahead since cycle 3's push) shipped this cycle alongside cycle-4's three commits. Stage / main / admin/ `dev` pushes were deferred at the push-scope sub-gate (user chose option A — ui/ dev only).
- Devices will not auto-pull cycle-3 + cycle-4 changes until `dev → stage → main` completes (D-CY4-STAGE, D-CY4-MAIN).
- AZ-513 task spec still sits locally on `admin/` `dev` — admin/ team cannot pick it up until D-CY3-ADMIN-PUSH lands (now carried into cycle 5).
- No Dockerfile / `.woodpecker/` / nginx / env changes in cycle 4 — verified inline by the security audit (Step 14 enumerated the changed-file set as 6 source/test + 5 doc files only).
- The deployed ui/ dev build will surface `admin.classes.updateFailed` on real edits until AZ-513 ships in admin/ — by design under the user-authorized Option B path.
These items add to the user-action backlog; see §Efficiency → User-action backlog table.
## LESSONS Append (top 3, single-sentence, tagged)
1. **[testing]** When inserting a new control (button, input, link) into an existing DOM row or region that already holds other controls, audit the test corpus *before* the commit for non-disambiguated selectors targeting that region (`querySelector('button')`, `getByRole('button')` without `name`/`text`, indexed `querySelectorAll('button')[0]`) and either update them with disambiguating text/role/name in the same affordance commit or give the new control a stable `data-testid` — otherwise the new control silently rebinds existing assertions to the wrong element and the tests ship green-but-meaningless, exactly as cycle 4's `destructive_ux.test.tsx` did when the AZ-512 ✎ button became the new first button in the class-row action cell.
2. **[testing]** When a new test mounts a container component end-to-end, run it once with the project's default test fixtures only (no per-test override) and explicitly name any natural crashes ("`users.map is not a function`") in the batch report as "Pre-existing bug noted" — never silently apply a local fixture workaround without recording the latent drift, because each silent workaround hides a source-vs-fixture mismatch that future authors will re-encounter as a "mysterious test setup", and cycle 4's `tests/admin_class_edit.test.tsx` was the second cycle to surface one through this route.
3. **[process]** When the user explicitly overrides a spec-conservative cycle-defer decision (the AZ-512 Option B authorization: "implement now, write mocks for backend"), the autodev MUST preserve every downstream gate that the conservative path would have enforced — re-record the override rationale in the leftover entry, keep the cross-workspace deploy gate visible at Step 16, mark the carried tickets distinctly from cycle-internal carries, and surface the override as a first-class retrospective trend ("Cycles where user overrode a spec-conservative default") — so the operating cost of the override stays measurable and the user's downstream visibility is unchanged from the conservative path.
@@ -0,0 +1,95 @@
# Structural Snapshot — 2026-05-13 (Phase B Cycle 4 close)
**Cycle**: Phase B, cycle 4 (`state.cycle = 4`)
**Source-of-truth files**: `_docs/02_document/module-layout.md`, `_docs/02_document/architecture_compliance_baseline.md`, `scripts/check-arch-imports.mjs`, `scripts/run-tests.sh`, `src/api/endpoints.test.ts`.
**Previous snapshot**: `_docs/06_metrics/structure_2026-05-13.md` (Phase B cycle 3 close).
> Cycle 4 was a single-task, contained UI-feature cycle (AZ-512 admin class inline edit). It introduced **no new components**, **no new gates**, **no new barrels**, **no new wire-contract assertions**, and **no new architecture findings**. The structural snapshot is therefore a near-identity copy of cycle 3 close with two non-structural deltas: test count (+12) and bundle size (+757 B).
## Component Inventory
| Metric | Cycle 1 close | Cycle 3 close | Cycle 4 close | Δ vs cycle 3 |
|--------|---------------|---------------|---------------|--------------|
| Component count | 12 | 12 | 12 | 0 |
| Components with Public API barrels | 11 | 11 | 11 | 0 |
| Barrel coverage (eligible components) | 100 % | 100 % | 100 % | 0 |
| Documented feature→feature edges (grandfathered) | 1 | 1 | 1 | 0 |
| Documented STC-ARCH-01 carve-out exemptions | 1 | 0 | 0 | 0 (held at zero) |
| Cycles in component import graph | 0 | 0 | 0 | 0 |
## Architecture Gates (cycle 4 close)
| Gate | Added in | Enforces | Status (cycle 4 close) |
|------|----------|----------|------------------------|
| `STC-ARCH-01` | Cycle 1 / AZ-485 | No cross-component deep imports; barrels are the Public API | PASS (zero exemptions — held since cycle 3) |
| `STC-ARCH-02` | Cycle 1 / AZ-486 | No hardcoded `/api/<service>/...` literals in production source | PASS |
| `STC-SEC1C` | Cycle 2 / AZ-499 | Banned literal: OpenWeatherMap key | PASS |
| `STC-SEC1D` | Cycle 2 / AZ-501 | Banned literal: Google Geocode key | PASS |
| `FT-P-22` (key parity) | (i18n coverage gate) | `en.json``ua.json` key parity | PASS (extended cycle 4: covers `admin.classes.{title,edit,save,cancel,nameRequired,maxSizeMustBePositive,updateFailed}`) |
| `FT-P-23` (no hardcoded strings) | (i18n coverage gate) | No raw English strings outside i18n bundles | PASS (the aria-label-as-hardcoded-English failure during cycle-4 implementation was caught by this gate and fixed before commit — see batch_16_cycle4_report.md "Pre-existing bug noted") |
Total commit-time static gates: **33** (cycle 3 close = 33; cycle 4 close = 33 — no new gates this cycle, **all existing gates green**).
## Architecture Baseline Delta vs `architecture_compliance_baseline.md`
| Finding | Category | Cycle 1 close | Cycle 2 close | Cycle 3 close | Cycle 4 close |
|---------|----------|---------------|---------------|---------------|---------------|
| F1 — mission-planner vs flights duplication | Architecture | Open | Open | Open | Open |
| F2 — cross-feature edge `07_dataset → 06_annotations` | Architecture | Open (grandfathered) | Open | Open | Open |
| F3 — classColors physical/logical owner split | Architecture | Open | Open | RESOLVED (AZ-511) | RESOLVED |
| F4 — No Public API barrels | Architecture | RESOLVED (AZ-485) | RESOLVED | RESOLVED | RESOLVED |
| F5 — Pre-existing cycle inside `mission-planner` | Architecture | Open | Open | Open | Open |
| F6 — No `src/shared/` | Architecture | Open | Open | Open | Open |
| F7 — Hardcoded `/api/<service>/` literals | Architecture | RESOLVED (AZ-486) | RESOLVED | RESOLVED | RESOLVED |
| F8 — Layering-table inconsistency | Architecture | Open | Open | Open | Open |
| F9 — Inert second Vite entry tree | Architecture | Open | Open | Open | Open |
- **Resolved this cycle**: 0
- **Newly introduced this cycle**: 0
- **Architecture findings open at cycle 4 close**: 6 of 9 baseline (F1, F2, F5, F6, F8, F9) — unchanged
- **Net architecture delta cycle 4**: 0 (no movement)
## Contract Coverage
- `_docs/02_document/contracts/` does NOT exist; project uses **code-derived contracts pattern** via `src/api/endpoints.test.ts`.
- Wire-contract assertions count: cycle 1 = 36, cycle 2 = 36, cycle 3 = 37, cycle 4 = **37** (no change — AZ-512 reused the existing `endpoints.admin.class(id)` builder for PATCH; no new builder introduced per task constraint).
## Test Suite Snapshot
| Profile | Cycle 1 close | Cycle 2 close | Cycle 3 close | Cycle 4 close | Δ vs cycle 3 |
|---------|---------------|---------------|---------------|---------------|--------------|
| Fast (count) | 209 PASS / 13 SKIP / 0 FAIL | 229 PASS / 13 SKIP / 0 FAIL | 231 PASS / 13 SKIP / 0 FAIL | **243 PASS / 13 SKIP / 0 FAIL** | **+12 PASS**, 0 SKIP |
| Static (gates) | 31 / 31 PASS | 33 / 33 PASS | 33 / 33 PASS | 33 / 33 PASS | 0 |
| Build | green | green | green | green | 0 |
| Bundle (gzipped initial JS) | not measured | 290 465 B | 290 575 B | **291 332 B** | **+757 B** (+0.26 %) |
The +12 PASS comes from `tests/admin_class_edit.test.tsx` (the entire AZ-512 suite). No other test files changed counts; `tests/destructive_ux.test.tsx`'s selector fix kept its existing 6 cases (2 fixed, 4 carried).
The +757 B bundle delta is explained at byte-level in `_docs/06_metrics/perf_2026-05-13_cycle4.md` (~500600 B from the new `AdminPage` handlers + JSX, ~150200 B from 7 i18n keys × 2 locales).
## Cycle 4 Source-of-Truth Mutations
| File / area | Mutation | Driver |
|-------------|----------|--------|
| `src/features/admin/AdminPage.tsx` | Inline edit state (4 hooks), 4 new handlers, conditional colspan row JSX, pencil affordance, `t('admin.classes')``t('admin.classes.title')` | AZ-512 |
| `src/i18n/en.json`, `src/i18n/ua.json` | `admin.classes` flat string → nested object (`title` + 6 keys for edit UI in both locales) | AZ-512 |
| `tests/msw/handlers/admin.ts` | New `http.patch('/api/admin/classes/:id', ...)` partial-merge handler | AZ-512 (test infra) |
| `tests/admin_class_edit.test.tsx` | NEW — 12 tests covering AC-1..AC-6, AC-8 | AZ-512 |
| `tests/destructive_ux.test.tsx` | Selector fix at 3 call sites (`querySelector('button')``Array.from(...).find(b => b.textContent === '×')`) | Adjacent hygiene from AZ-512 |
| `_docs/02_document/components/08_admin/description.md` | Edit affordance + PATCH wiring recorded | AZ-512 (spec-authorized) |
| `_docs/02_document/architecture.md` (row 272) | `08_admin/AdminPage` row gains PATCH /api/admin/classes/{id} with AZ-513 deploy-gate caveat | Step 13 (Update Docs) |
| `_docs/02_document/modules/src__features__admin__AdminPage.md` | Header cycle-4 banner; new state slots, four new handlers, layout note, PATCH integrations row, expanded i18n key list, Tests section | Step 13 (Update Docs) |
| `_docs/02_document/tests/blackbox-tests.md` | Added FT-P-62, FT-N-18 | Step 12 (Test-Spec Sync) |
| `_docs/02_document/tests/traceability-matrix.md` | O9 → Covered; references FT-P-62 + FT-N-18 + AZ-513 deploy gate | Step 12 (Test-Spec Sync) |
| `_docs/05_security/security_report_cycle4_delta.md` | NEW — cycle-4 delta; verdict PASS_WITH_WARNINGS; one new LOW finding (F-SAST-CY4-1) | Step 14 (Security Audit) |
| `_docs/05_security/security_report.md` | Cycle-4 amendment banner | Step 14 (Security Audit) |
| `_docs/06_metrics/perf_2026-05-13_cycle4.md` | NEW — NFT-PERF-01 PASS at 291 332 B | Step 15 (Performance Test) |
## Auto-lesson triggers (per skill Step 1)
- Net Architecture delta > 0? **No** — delta is 0. No `architecture` lesson trigger.
- Structural metric regression > 20%? **No** — every structural metric held at its cycle-3 value, except test count (+5.2%) and bundle (+0.26%), both improvements / within-budget.
- Contract coverage % decreased? **No** — same 37 assertions (no builder added, no builder removed).
- New finding category emerged? **No** — security audit ran in delta mode; categories are stable.
No auto-lesson triggers fired in cycle 4. Manual lessons (3 picked) appear in the retro report.
+36
View File
@@ -8,6 +8,42 @@ Categories: estimation · architecture · testing · dependencies · tooling ·
--- ---
- [2026-05-13] [testing] When inserting a new control (button, input, link)
into an existing DOM row or region that already holds other controls, audit
the test corpus *before* the commit for non-disambiguated selectors
targeting that region (`querySelector('button')`, `getByRole('button')`
without `name`/`text`, indexed `querySelectorAll('button')[0]`) and either
update them with disambiguating text/role/name in the same affordance
commit or give the new control a stable `data-testid` — otherwise the new
control silently rebinds existing assertions to the wrong element and the
tests ship green-but-meaningless, as cycle 4's `destructive_ux.test.tsx`
did when the AZ-512 ✎ button became the new first button in the class-row
action cell.
Source: _docs/06_metrics/retro_2026-05-13_cycle4.md
- [2026-05-13] [testing] When a new test mounts a container component
end-to-end, run it once with the project's default test fixtures only (no
per-test override) and explicitly name any natural crashes ("`users.map is
not a function`") in the batch report as "Pre-existing bug noted" — never
silently apply a local fixture workaround without recording the latent
drift, because each silent workaround hides a source-vs-fixture mismatch
that future authors will re-encounter as a "mysterious test setup", and
cycle 4's `tests/admin_class_edit.test.tsx` was the second cycle to
surface one through this route.
Source: _docs/06_metrics/retro_2026-05-13_cycle4.md
- [2026-05-13] [process] When the user explicitly overrides a
spec-conservative cycle-defer decision (the AZ-512 Option B authorization:
"implement now, write mocks for backend"), the autodev MUST preserve every
downstream gate that the conservative path would have enforced — re-record
the override rationale in the leftover entry, keep the cross-workspace
deploy gate visible at Step 16, mark the carried tickets distinctly from
cycle-internal carries, and surface the override as a first-class
retrospective trend ("Cycles where user overrode a spec-conservative
default") — so the operating cost of the override stays measurable and
the user's downstream visibility is unchanged from the conservative path.
Source: _docs/06_metrics/retro_2026-05-13_cycle4.md
- [2026-05-13] [process] When a task spec defines a Cross-Workspace Verification - [2026-05-13] [process] When a task spec defines a Cross-Workspace Verification
BLOCKING gate and the user skips the choice prompt, the autodev MUST default BLOCKING gate and the user skips the choice prompt, the autodev MUST default
to the most conservative spec-aligned option (Option A: file prerequisite to the most conservative spec-aligned option (Option A: file prerequisite
+12 -7
View File
@@ -2,19 +2,24 @@
## Current Step ## Current Step
flow: existing-code flow: existing-code
step: 16 step: 9
name: Deploy name: New Task
status: not_started status: not_started
sub_step: sub_step:
phase: 0 phase: 0
name: awaiting-invocation name: awaiting-invocation
detail: "" detail: ""
retry_count: 0 retry_count: 0
cycle: 4 cycle: 5
tracker: jira tracker: jira
## Notes ## Notes
- Cycle 4 batch 16 shipped (commit ecacfa8): AZ-512 — 3/3 pts. Jira: To Do → In Testing. - Cycle 4 CLOSED: AZ-512 — 3/3 pts. Jira: In Testing → Done. Retro `retro_2026-05-13_cycle4.md` + deploy report `deploy_cycle4_report.md` + perf `perf_2026-05-13_cycle4.md` + security delta `security_report_cycle4_delta.md` + structure snapshot `structure_2026-05-13_cycle4.md` written. Cycle-4 push: `09449bd..8737491` (4 commits) → origin/dev.
- Cross-workspace: AZ-513 on admin/ NOT shipped. Step 16 (Deploy) gates on it. - Cycle 5 awaiting next `/autodev` New Task invocation.
- Leftovers: `2026-05-12_az-498-deploy-and-key-revocations.md` (manual), `2026-05-13_az-512-admin-classes-prereq.md` (re-opened). - Leftovers carried into cycle 5 (replay at Step 0):
- Pre-existing bug surfaced during AZ-512: `/api/admin/users` MSW shape (paginated) vs `AdminPage` consumption (flat `User[]`) mismatch. Flagged in batch + impl reports; needs separate UI ticket triage. - `2026-05-12_az-498-deploy-and-key-revocations.md` — 3 manual third-party items (UI satellite-provider deploy gate; OWM revoke; Google Geocode revoke).
- `2026-05-13_az-512-admin-classes-prereq.md` — re-opened under user-authorized Option B; closes when AZ-513 ships AND deploys in admin/.
- Cross-workspace status: AZ-513 (admin/) still not implemented. UI's PATCH /api/admin/classes/{id} returns 404 in any env until admin/ ships AZ-513.
- User-action backlog at cycle-4 close (per retro): 9 items (de-duplicated). +2 vs cycle 3.
- Pre-existing bug surfaced during AZ-512: `/api/admin/users` MSW shape (paginated) vs `AdminPage` consumption (flat `User[]`). Awaiting separate UI-workspace ticket triage; pre-existing, not introduced by AZ-512.
- Cycle-3 deferred deploy items still carry: D-CY3-STAGE, D-CY3-MAIN, D-CY3-ADMIN-PUSH. Cycle 4 added: D-CY4-STAGE, D-CY4-MAIN (D-CY4-ADMIN-PUSH not added — user kept same ui/-dev-only scope).