diff --git a/docs/architecture/key-files.md b/docs/architecture/key-files.md
index e52dc6e..0a2c4d3 100644
--- a/docs/architecture/key-files.md
+++ b/docs/architecture/key-files.md
@@ -3,7 +3,7 @@ type: architecture
title: Key Files Index
description: One-line responsibility for important source and contract-test files, including strict event-window validation and the public bootstrap SSE listener.
tags: [architecture, key-files, event-window, validation, default-challenge-ip, sse, bootstrap, migrations]
-timestamp: 2026-07-22T16:44:54Z
+timestamp: 2026-07-22T18:37:00Z
---
# Backend
@@ -52,8 +52,10 @@ timestamp: 2026-07-22T16:44:54Z
| `frontend/src/app/features/shell/tabs/quick-tabs.component.ts` | Main shell navigation tabs. |
| `frontend/src/app/features/shell/change-password/change-password-modal.component.ts` | Change-password form modal. |
| `frontend/src/app/features/admin/general.component.ts` | `AdminGeneralComponent` reactive form for `/admin/general` — per-field inline error rendering (page-title + event-start + event-end), logo upload wiring, welcome Markdown preview, event-state derivation, and SSE `general` event handling. |
+| `frontend/src/app/features/admin/categories/category-form-modal.component.ts` | Standalone OnPush modal for create + edit; owns the `CategoryFormGroup`, exposes the pure `syncCategoryForm` helper, and reacts to `open` / `mode` / `category` signal inputs via a `markForCheck` effect so edit prefill reaches the DOM. |
| `frontend/src/app/features/admin/general.pure.ts` | Pure General Settings helpers, including required datetime validation, field messages, UTC conversion, end-after-start validation, and the default-challenge-address IPv4/hostname validator/normalizer/message trio. |
| `tests/frontend/admin-general-pure.spec.ts` | Pure client-contract tests for required event timestamps, datetime messaging, UTC conversion, event-window ordering, and default-challenge-address validation, error mapping, and normalization. |
+| `tests/frontend/admin-categories-form-modal.spec.ts` | Tests the pure `syncCategoryForm` helper that drives the edit/create prefill in `CategoryFormModalComponent`: system-row abbreviation lock, user-row unlock, re-population on second invocation, clearing on create, and iconPreview passthrough. |
| `tests/frontend/authenticated-event-source.spec.ts` | Tests SSE authorization, frame transport behavior, and the `401`/`403` unauthorized path. |
| `tests/frontend/auth-session-events.spec.ts` | Pure tests for cross-tab invalidation message encoding, payload validation, and `storage`-event filtering. |
diff --git a/docs/guides/admin-categories.md b/docs/guides/admin-categories.md
index 4f0bc45..e9780f2 100644
--- a/docs/guides/admin-categories.md
+++ b/docs/guides/admin-categories.md
@@ -3,7 +3,7 @@ type: guide
title: Admin — Categories
description: How an admin lists, creates, edits, and deletes challenge categories from the /admin/categories page, including system-row protection and challenge-attached protection.
tags: [guide, admin, categories, tester]
-timestamp: 2026-07-22T12:00:00Z
+timestamp: 2026-07-22T18:37:00Z
---
# When this view is available
@@ -89,6 +89,29 @@ side-nav (categories are also embedded inside the General settings page).
file is selected, the new icon is uploaded first and the returned
`publicUrl` replaces `iconPath` in the same update.
+### Edit prefill mechanics
+
+When the parent sets `open=true` + `mode='edit'` + a `category`, a
+constructor `effect()` runs `syncCategoryForm(form, mode, category)`
+which:
+
+* writes `name`, `abbreviation`, `description` via per-control
+ `setValue(..., { emitEvent: false })`,
+* marks every control pristine + untouched so validators do not flash
+ errors on a freshly opened modal,
+* returns `{ abbreviationReadonly: category.isSystem === true,
+ iconPreview: category.iconPath || null }` which the effect pushes
+ into `abbreviationReadonly` and `iconPreview` signals and clears
+ `iconFile`,
+* and triggers `ChangeDetectorRef.markForCheck()` so the OnPush view
+ repaints the freshly-patched `` / `