# Batch Report

**Batch**: 16
**Cycle**: 4 (autodev existing-code Step 10)
**Tasks**: [AZ-512]
**Date**: 2026-05-13
**Reactivation context**: AZ-512 was deferred to backlog at the end of cycle 3 (Cross-Workspace Verification BLOCKING gate failed — `admin/` service does not expose `/classes` write routes). User authorized **Option B** (MSW-stubbed UI ahead of admin/ AZ-513 shipping) at cycle 4 entry. Task moved `backlog/` → `todo/` in commit `ef56d9c`.

## Task Results

| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|------|--------|---------------|-------|-------------|--------|
| AZ-512_admin_edit_detection_class | Done | 5 production + test + 1 doc | 12 passed | 8/8 ACs covered | 1 noted (pre-existing) |

### Files modified

| Path | Type | Change |
|------|------|--------|
| `src/features/admin/AdminPage.tsx` | OWNED (08_admin) | Added inline edit affordance: `editingId` / `editForm` / `editError` / `editSaving` state; handlers (`handleStartEdit`, `handleCancelEdit`, `handleUpdateClass`, `handleEditKeyDown`); colspan row swap when editing; pencil (✎) button on read-only rows. Updated `t('admin.classes')` → `t('admin.classes.title')`. |
| `src/i18n/en.json` | spec-authorized (00_foundation) | Restructured `admin.classes` from flat string to nested object (`title` + 6 new keys: `edit`, `save`, `cancel`, `nameRequired`, `maxSizeMustBePositive`, `updateFailed`). |
| `src/i18n/ua.json` | spec-authorized (00_foundation) | Ukrainian mirror of the same 7 keys (FT-P-22 parity gate PASS). |
| `tests/msw/handlers/admin.ts` | test-infra | Added `http.patch('/api/admin/classes/:id', ...)` partial-merge handler; existing PUT handler retained (dead code, not introduced by this task). |
| `tests/admin_class_edit.test.tsx` | new | 12 tests covering AC-1..AC-6, AC-8 (AC-7 covered by static FT-P-22 gate). |
| `tests/destructive_ux.test.tsx` | adjacent hygiene | Fixed `firstRow.querySelector('button')` selector at 3 call sites — my ✎ button became the first button in the row; replaced with `Array.from(querySelectorAll('button')).find(b => b.textContent === '×')` to deliberately target the delete (×) button. Pre-existing `it.fails()` semantics preserved. |
| `_docs/02_document/components/08_admin/description.md` | spec-authorized (per task Scope.Included) | Recorded edit affordance + PATCH wiring in Internal Interfaces table and External API table; cross-referenced AZ-513 prerequisite. |

### Files NOT modified (scope discipline)

| Path | Reason |
|------|--------|
| `src/api/endpoints.ts` | Task constraint: reuse existing `endpoints.admin.class(id)` builder; no new endpoint helper for PATCH (same URL as DELETE). |
| `src/api/client.ts` | `api.patch()` helper already exists. |
| `_docs/02_document/architecture.md` | Architecture-level wire-shape table update belongs in Step 13 (Update Docs), not Step 10. |
| AdminPage delete-confirm wiring | Out of scope (Finding B4 — explicitly excluded per task spec Scope.Excluded). |
| Settings/Users sections | Out of scope (separate concerns per task spec Scope.Excluded). |

## AC Test Coverage: All covered (8 of 8)

| AC | Test name | Notes |
|----|-----------|-------|
| AC-1 | `renders a pencil button per row` | One edit affordance per class row |
| AC-2 | `row 1 enters edit mode with name="class-a"; other rows stay read-only` + `single-row invariant` | Seeded values + Risk 3 mitigation |
| AC-3 | `Save button → one PATCH with full body, row re-renders, form closes` + `Enter key inside form behaves like Save` | Risk 2 mitigation: full-body always |
| AC-4 | `Cancel button → no PATCH; row reverts` + `Escape key inside form behaves like Cancel` | No network in either path |
| AC-5 | `empty name → no PATCH; nameRequired error visible` + `non-positive maxSizeM → no PATCH; maxSizeMustBePositive error visible` | Validation-before-submit |
| AC-6 | `PATCH 500 → form stays open; updateFailed error visible; no alert() called` | Risk 4 mitigation: disabled buttons during PATCH; spy on `window.alert` |
| AC-7 | (static) `FT-P-22 (key parity): PASS` | `scripts/check-i18n-coverage.mjs --parity-only` |
| AC-8 | `Add posts to /api/admin/classes and refetches the list` + `Delete sends DELETE and removes the row optimistically` | Regression guards |

## Code Review Verdict: PASS (inline self-review)

A formal `/code-review` skill run was not invoked for this single-task batch (3 pts, tight scope, all spec ACs verified). The self-review checked: file ownership respected, no silent error swallowing, no `alert()` usage (STC-SEC7 confirms), no banned-deps literals (STC-SEC1B/C/D confirm), i18n parity + coverage (FT-P-22/23 confirm), architecture compliance (STC-ARCH-01/02 confirm), single-responsibility handlers, no spec drift, no dependencies on un-shipped admin/ work in the test layer.

If a cumulative review is required at Step 14.5 (every K=3 batches), this is the 1st batch of cycle 4 — cumulative review fires at batch 18.

## Auto-Fix Attempts: 0

No PASS-with-warnings or FAIL findings during self-review.

## Stuck Agents: None

Single task, ~7 file edits, no rewrites without progress. The one i18n-coverage failure (3 raw English aria-labels) was fixed in a single targeted swap (aria-label → data-field) without regressing the spec's aria-label-on-edit-button NFR.

## Test Suite Result

| Suite | Result |
|-------|--------|
| `bun run test` (full vitest) | **32 files passed, 243 tests passed, 13 quarantined skips** (cycle 3 baseline preserved) |
| `bash scripts/run-tests.sh --static-only` | **All 35 static checks PASS** including FT-P-22, FT-P-23, STC-ARCH-01/02, STC-SEC1/2/3/4/7/8/13/14, STC-SEC1B/C/D, banned-deps, etc. |

## Pre-existing bug noted (NOT fixed this batch)

While writing the new test file, I discovered that `tests/msw/handlers/admin.ts` returns `paginate(seedUsers)` (= `{ items, totalCount, page, pageSize }`) for `GET /api/admin/users`, but `AdminPage.tsx:19` does `api.get<User[]>(...).then(setUsers)` expecting a flat array. The catch swallows fetch errors but NOT the subsequent `users.map is not a function` render error.

- **Impact in tests**: any test that mounts the full `<AdminPage />` without overriding the users handler crashes. Today, `destructive_ux.test.tsx:50-59` already overrides `/api/admin/users` with `jsonResponse([])` and documents the drift with the same comment shape; my new `tests/admin_class_edit.test.tsx` adds the same override (`stubUsersAsPlainArray()`).
- **Impact in production**: depends on what the live `admin/` service actually returns (flat or paginated). If paginated, the Users table is broken end-to-end against the live service — analogous to the pre-existing AZ-513 add/delete situation. If flat, only the test fixture is wrong.
- **Recommendation**: a separate UI-workspace ticket to either (a) align the MSW handler with the live admin/ shape (and fix `AdminPage.users` consumption if needed), or (b) introduce a paginated-response unwrap in the api client. NOT bundled with AZ-512 per scope discipline (`coderule.mdc`).

## Cross-workspace dependency reminder

AZ-512 ships in this batch but the **live admin/ service does not yet expose** `POST | PATCH | DELETE /api/admin/classes(/{id})` (verified 2026-05-13: zero `MapPost|MapPatch|MapDelete` against `classes` in `admin/Azaion.AdminApi/Program.cs`). Per the user-chosen Option B path:

- **Step 11 (Run Tests)** passes on MSW stubs.
- **Step 16 (Deploy)** gates on **AZ-513** landing on the admin/ workspace AND that build being deployed to whichever environment(s) the UI is promoted into. The leftover record at `_docs/_process_leftovers/2026-05-13_az-512-admin-classes-prereq.md` remains open until that point.
- The existing pre-existing-broken Add and Delete affordances on `AdminPage`'s class table also start working end-to-end the moment AZ-513 ships.

## Next Batch

None planned in this cycle (cycle 4 was entered for AZ-512 reactivation only). After Step 11 (Run Tests) confirms the test suite still passes, autodev auto-chains through Steps 12 → 13 → 14 → 15 → 16 → 17. The Deploy gate (Step 16) will surface the admin/ AZ-513 dependency before any prod cutover.
