Implements the cycle-3-deferred AZ-512 task under the user-authorized
Option B path (MSW-stubbed; live deploy gates at Step 16 on AZ-513).
Code:
- src/features/admin/AdminPage.tsx — inline edit affordance:
editingId/editForm state, handleStartEdit/Cancel/Update, Enter+Escape
keyboard handling, colspan row swap when editing, pencil (✎) button
per row. Full-body PATCH (Risk 2). Single editingId enforces the
one-row-at-a-time invariant (Risk 3). Disabled buttons during the
in-flight PATCH (Risk 4). Inline role="alert" on validation/server
errors (no alert() per Finding B4 anti-pattern).
- src/i18n/{en,ua}.json — `admin.classes` flat → nested with `title`
+ 6 new keys (edit, save, cancel, nameRequired,
maxSizeMustBePositive, updateFailed). Parity gate FT-P-22 PASS.
Test infrastructure:
- tests/msw/handlers/admin.ts — PATCH /api/admin/classes/:id
partial-merge handler.
- tests/admin_class_edit.test.tsx — 12 tests covering AC-1..AC-6
+ AC-8 (AC-7 satisfied by static FT-P-22 gate).
- tests/destructive_ux.test.tsx — adjacent-hygiene selector fix
at 3 call sites: the new ✎ button moved the first-button
position; targeting × explicitly preserves the existing
it.fails()/control semantics.
Docs:
- _docs/02_document/components/08_admin/description.md — recorded
edit affordance + PATCH wiring + AZ-513 cross-workspace note.
- _docs/03_implementation/batch_16_cycle4_report.md
- _docs/03_implementation/implementation_report_admin_class_edit_cycle4.md
- _docs/02_tasks/todo → done — AZ-512 archived.
Quality gates: 32 files / 243 tests / 13 quarantined skips PASS;
all 35 static checks PASS (FT-P-22/23, STC-ARCH-01/02, STC-SEC*,
banned-deps incl. SEC1B/C/D).
Cross-workspace dependency: admin/ AZ-513 (POST + PATCH + DELETE
/classes routes) NOT yet shipped. Step 11 (Run Tests) passes on
stubs; Step 16 (Deploy) holds until AZ-513 lands live. Leftover
record at _docs/_process_leftovers/2026-05-13_az-512-admin-
classes-prereq.md stays open.
Discovered pre-existing bug (NOT bundled): tests/msw/handlers/
admin.ts returns paginate(seedUsers) for GET /api/admin/users,
but AdminPage consumes as flat User[] → users.map crash. Test
files use the same flat-array workaround
destructive_ux.test.tsx documented. Flagged in batch + impl
reports for separate triage.
Co-authored-by: Cursor <cursoragent@cursor.com>
8.0 KiB
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-59already overrides/api/admin/userswithjsonResponse([])and documents the drift with the same comment shape; my newtests/admin_class_edit.test.tsxadds 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.usersconsumption 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.mdremains 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.