11 KiB
Housekeeping Content Events Vertical Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Deliver a complete ASE event and event-type operator workflow backed by real database data and existing audited mutations.
Architecture: Keep the generic Content query/command foundation, add strict route-specific event payloads, and render them through a focused ContentEventWorkflow. Use the existing event tables and mutation runtime; introduce dedicated delete command IDs only to enforce reason confirmation.
Tech Stack: Next.js 16, React 19, TypeScript, Drizzle/MySQL, Zod, Vitest, React server actions
Spec: docs/superpowers/specs/2026-09-01-housekeeping-content-events-vertical-design.md
Global Constraints
- Work directly on
codex/housekeeping-rebuild-stepwise; do not create a worktree. - Preserve
/admin/eventsand all unrelated local/untracked files. - Do not add dependencies or change the database schema.
- Keep Polls and Prefixes behavior unchanged.
- Write and run a failing test before each production behavior change.
- Keep every destructive event or event-type action reason-protected and auditable.
Task 1: Typed event query payloads and production loading
Files:
- Modify:
src/features/housekeeping/domains/content/queries/content-queries.ts - Modify:
src/features/housekeeping/domains/content/queries/content-queries-production.ts - Test:
src/features/housekeeping/domains/content/queries/content-queries.test.ts
Interfaces:
-
Produces:
ContentEventTypePayload,ContentEventSummaryPayload,ContentEventDetailPayload, and their public type guards. -
Produces: validated private payloads for list, create, detail, and type routes.
-
Consumes:
ContentQueryItem.privatePayload, normalizedparams.id,list.search,list.status,list.pageSize, andlist.offset. -
Step 1: Write failing contract tests
Add literal fixtures proving that malformed event payloads fail closed, event list input normalizes a bounded status, and a detail adapter returning more than one item is rejected.
- Step 2: Run the query test and confirm RED
Run: yarn.cmd vitest run src/features/housekeeping/domains/content/queries/content-queries.test.ts
Expected: FAIL because event payload guards and status normalization do not exist.
- Step 3: Implement the minimal event contracts
Add route-specific interfaces with JSON-safe primitive fields and type guards. Extend list input with status, normalized to lowercase and at most 32 characters. Extend isValidData so each event route accepts only its corresponding payload and detail accepts at most one selected item.
- Step 4: Run the query test and confirm GREEN
Run the command from Step 2. Expected: PASS.
- Step 5: Write failing production-loader tests
Add tests proving:
expect(list.items[0]?.privatePayload).toMatchObject({
typeName: "Tournament",
registrationCount: 12,
});
expect(detail.items).toHaveLength(1);
expect(detail.items[0]?.privatePayload).toMatchObject({
description: "Complete event",
eventTypes: [{ id: "2", name: "Tournament" }],
prizes: [{ id: "4", prizeType: "badge" }],
winners: [{ userId: "9", username: "Alice" }],
registrations: [{ userId: "10", username: "Bob" }],
});
Also assert that the serialized detail SQL binds the requested ID and that status filtering is bound, not interpolated.
- Step 6: Run the loader tests and confirm RED
Run the command from Step 2. Expected: FAIL because production definitions only expose generic summaries.
- Step 7: Implement production event loaders
Update event list and type definitions to project full safe summaries. Load event-create options from active event types. Add a dedicated detail loader that performs bounded, parameterized reads for the selected event, event types, prizes, winners with usernames, and registrations with usernames. Return not-found as an empty successful result.
- Step 8: Run the loader tests and confirm GREEN
Run the command from Step 2. Expected: PASS.
Task 2: Event command safety and form field semantics
Files:
- Modify:
src/features/housekeeping/domains/content/commands/content-commands.ts - Modify:
src/features/housekeeping/domains/content/pages/content-command-form.tsx - Test:
src/features/housekeeping/domains/content/commands/content-commands.test.ts - Test:
src/features/housekeeping/domains/content/pages/content-pages.test.tsx
Interfaces:
-
Produces:
content.engagement.event.deleteandcontent.engagement.event-type.delete, both mapped to existing mutations withrequiresReason: true. -
Produces:
ContentCommandField.type === "datetime-local", submitted as the normalized browser value. -
Step 1: Write failing command-policy tests
Assert the two delete command IDs exist, use event.change and event-type.change, retain PERMS.EVENTS_EDIT, and require reasons while non-destructive change commands do not.
- Step 2: Run command tests and confirm RED
Run: yarn.cmd vitest run src/features/housekeeping/domains/content/commands/content-commands.test.ts
Expected: FAIL because the dedicated delete commands are absent.
- Step 3: Implement command metadata
Extend the command definition tuple with an optional requiresReason flag and register both dedicated delete command IDs without adding mutation operations.
- Step 4: Run command tests and confirm GREEN
Run the command from Step 2. Expected: PASS.
- Step 5: Write a failing date/time form test
Render a field with type: "datetime-local" and assert the real input type and default value. Submit it and assert the existing server action receives the exact normalized date/time string.
- Step 6: Run page tests and confirm RED
Run: yarn.cmd vitest run src/features/housekeeping/domains/content/pages/content-pages.test.tsx
Expected: FAIL because datetime-local is not supported.
- Step 7: Implement the minimal field support
Add datetime-local to the field union and map it to <input type="datetime-local">; keep the existing bounded string parser.
- Step 8: Run page tests and confirm GREEN
Run the command from Step 6. Expected: PASS.
Task 3: Complete Event workflow UI
Files:
- Create:
src/features/housekeeping/domains/content/pages/event-workflow.tsx - Modify:
src/features/housekeeping/domains/content/pages/engagement.tsx - Create:
src/features/housekeeping/domains/content/pages/event-workflow.test.tsx - Modify:
src/features/housekeeping/domains/content/pages/content-pages.test.tsx
Interfaces:
-
Produces:
ContentEventWorkflow(props)for the four event routes. -
Consumes: typed payload guards from Task 1 and command IDs/field semantics from Task 2.
-
Step 1: Write failing list and state tests
Render real HousekeepingResult fixtures and assert loading, forbidden, dependency-error, empty, and ready states. The ready list must expose search, status filtering, result count, type, schedule, capacity, registrations, create/type links, and bounded previous/next links.
- Step 2: Run workflow tests and confirm RED
Run: yarn.cmd vitest run src/features/housekeeping/domains/content/pages/event-workflow.test.tsx
Expected: FAIL because the component does not exist.
- Step 3: Implement the list/state slice
Create the focused workflow component and route the four event route IDs to it from ContentEngagementPage, leaving Polls and Prefixes on the existing generic implementation.
- Step 4: Run workflow tests and confirm GREEN
Run the command from Step 2. Expected: PASS for list/state tests.
- Step 5: Write failing create/detail tests
Assert create uses real type options and date/time inputs. Assert detail prepopulates title, description, selected type, schedule, capacity, room, status, recurrence, and image; automatically binds event IDs for prizes/winners; renders usernames for registrations; and exposes a reason-required delete form using content.engagement.event.delete.
- Step 6: Run workflow tests and confirm RED
Run the command from Step 2. Expected: FAIL because create/detail workflow sections are incomplete.
- Step 7: Implement create/detail
Build field factories from the validated payload. Render not-found separately from dependency failure. Keep related forms and lists inside the selected event detail; never expose manual event-ID inputs.
- Step 8: Run workflow tests and confirm GREEN
Run the command from Step 2. Expected: PASS for create/detail tests.
- Step 9: Write failing event-type tests
Assert a create form and one prefilled update form per type, plus a reason-required delete form using content.engagement.event-type.delete; no operator-entered type ID field is allowed.
- Step 10: Run workflow tests and confirm RED
Run the command from Step 2. Expected: FAIL until type management is implemented.
- Step 11: Implement event-type management and refactor
Add create/update/delete sections using typed payloads. Extract small field and formatting helpers while all tests stay green.
- Step 12: Run focused Content tests and confirm GREEN
Run:
yarn.cmd vitest run src/features/housekeeping/domains/content/queries/content-queries.test.ts src/features/housekeeping/domains/content/commands/content-commands.test.ts src/features/housekeeping/domains/content/pages/content-pages.test.tsx src/features/housekeeping/domains/content/pages/event-workflow.test.tsx
Expected: all focused tests PASS.
Task 4: Full verification and draft PR update
Files:
- Modify:
docs/superpowers/plans/2026-09-01-housekeeping-content-events-vertical.md - Modify: draft PR 53 body in English and Dutch
Interfaces:
-
Consumes: all deliverables from Tasks 1–3.
-
Produces: verified commit(s), pushed branch, and current bilingual PR evidence.
-
Step 1: Run static and targeted checks
Run the repository TypeScript, Biome, Knip, focused test, and Housekeeping matrix commands from package.json and the existing Housekeeping evidence workflow. Fix only failures caused by this vertical.
- Step 2: Run the full test suite with coverage
Run: yarn.cmd test
Expected: zero failing test files and zero failing tests.
- Step 3: Run the production build
Run: yarn.cmd build
Expected: exit code 0 with canonical /ase-next routes generated.
- Step 4: Review the exact diff
Run: git diff --check, git status --short, and git diff --stat origin/main...HEAD after committing. Confirm .remember/ and .superpowers/brainstorm/ remain untouched and untracked.
- Step 5: Commit and push exact paths
Commit query/command/UI/test/plan files with a scoped message, then push codex/housekeeping-rebuild-stepwise.
- Step 6: Update and verify the draft PR
Add the Events vertical and fresh verification counts to PR 53 in English and Dutch. Confirm the remote head matches local HEAD and inspect CI status without claiming deployment.