From 74756dfed2c431778544c5afa56186caf139e86d Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Wed, 26 Aug 2026 20:26:19 +0200 Subject: [PATCH] docs: plan complete ase housekeeping cutover --- .../2026-08-26-housekeeping-completion.md | 1191 +++++++++++++++++ ...26-08-26-housekeeping-completion-design.md | 57 +- 2 files changed, 1225 insertions(+), 23 deletions(-) create mode 100644 docs/superpowers/plans/2026-08-26-housekeeping-completion.md diff --git a/docs/superpowers/plans/2026-08-26-housekeeping-completion.md b/docs/superpowers/plans/2026-08-26-housekeeping-completion.md new file mode 100644 index 00000000..414e7d2b --- /dev/null +++ b/docs/superpowers/plans/2026-08-26-housekeeping-completion.md @@ -0,0 +1,1191 @@ +# Housekeeping Completion Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` (current session) or `superpowers:executing-plans` (separate session) to execute this plan task-by-task. + +**Goal:** Complete all 137 Housekeeping migrations behind `/ase-next`, then atomically publish the capability-aware Command Deck at `/ase` and remove the legacy `/admin`, `/admin-next`, and `/mod` UI trees without redirects. + +**Architecture:** Keep `src/features/housekeeping/foundation` limited to cross-domain contracts, registry projection, provider orchestration, shell composition, preferences, commands, and audit envelopes. Each of the six domains owns its route catalog, server adapters, commands, search, inbox, widgets, and workflow components. App Router entrypoints dispatch canonical route IDs to domain-owned renderers; the preview maps canonical `/ase/*` destinations to `/ase-next/*` without changing domain manifests. + +**Tech Stack:** Node.js 26, Next.js 16 App Router, React 19, TypeScript 7, Drizzle ORM/MySQL, next-intl, Zod 4, Vitest 4, Biome 2, Tailwind CSS 4, cmdk, dnd-kit. + +**Spec:** `docs/superpowers/specs/2026-08-26-housekeeping-completion-design.md` + +**Global Constraints:** Work only on `codex/housekeeping-complete`; do not use worktrees; preserve `.remember/` and unrelated changes; keep `/admin` and `/mod` behavior unchanged until Task 25; keep `/ase-next` unavailable in production; retain `/api/admin/*` as internal transport contracts unless a task explicitly changes one; add only backward-compatible database changes; use the current ACL permission slugs and never introduce rank thresholds; stage exact files; run `git diff --check` before every commit; do not push or open a pull request until the entire plan and final review pass. + +## File structure and ownership + +```text +src/app/ase-next/ gated preview App Router surface + layout.tsx production-denying preview gate + page.tsx first visible domain redirect + [domain]/layout.tsx capability-projected Command Deck shell + [domain]/[[...segments]]/page.tsx route ID resolution and domain dispatch +src/app/ase/ created only by atomic cutover + +src/features/housekeeping/foundation/ + contracts/ stable capability, result, route, command, + search, inbox, widget, and preference types + routing/ canonical/preview href mapping and matcher + providers/ timeout, cap, partial-result orchestration + commands/ dispatcher, confirmation, audit envelope + preferences/ schema validation and reconciliation + recent/ audit-derived recent work + shell/ rail, contextual nav, command deck, inbox, + widgets, favorites, and responsive chrome + page/ shared page states and workflow primitives + +src/features/housekeeping/domains// + manifest.ts declarative registry entry + routes.ts canonical `/ase` routes and handler IDs + route-handlers.ts domain-owned renderer dispatch + queries/ server-only read adapters + commands/ typed mutations around existing services + search.ts entity-search providers + inbox.ts derived work sources + widgets.ts mandatory and optional loaders + pages/ workflow-focused React server/client views + +src/actions/housekeeping-*.ts narrow server-action boundaries +drizzle/migrations/0023_housekeeping.sql additive audit and preference migration +src/features/housekeeping/cutover/ 137-row parity and legacy-removal contracts +``` + +The migration files remain the authoritative inventory of legacy source pages and dependencies. Domain route catalogs are authoritative for canonical `/ase` destinations. A contract joins the two by canonical route ID; no legacy page component is imported into the new route tree. + +### Task 1: Lock the `/ase` namespace and preview mapping + +**Files:** + +- Modify: `src/features/housekeeping/foundation/contracts/domain.ts` +- Create: `src/features/housekeeping/foundation/routing/href.ts` +- Create: `src/features/housekeeping/foundation/routing/href.test.ts` +- Modify: `src/features/housekeeping/foundation/registry.ts` +- Modify: `src/features/housekeeping/foundation/registry.test.ts` +- Modify: `src/features/housekeeping/foundation/navigation.ts` +- Modify: `src/features/housekeeping/foundation/navigation.test.ts` +- Modify: `src/features/housekeeping/domains/*/manifest.ts` +- Modify: `src/features/housekeeping/migration/operations.ts` +- Modify: `src/features/housekeeping/migration/people.ts` +- Modify: `src/features/housekeeping/migration/content.ts` +- Modify: `src/features/housekeeping/migration/economy.ts` +- Modify: `src/features/housekeeping/migration/hotel.ts` +- Modify: `src/features/housekeeping/migration/system.ts` +- Modify: `src/features/housekeeping/migration/validate-matrix.ts` +- Modify: `src/features/housekeeping/migration/validate-matrix.test.ts` +- Move: `src/app/admin-next` to `src/app/ase-next` +- Modify: `src/features/housekeeping/foundation/preview-route-contract.test.ts` +- Modify: `src/features/housekeeping/foundation/foundation-source-contract.test.ts` +- Modify: `src/lib/admin-theme-source-audit.test.ts` + +**Interfaces:** + +- Consumes: canonical route strings beginning with `/ase`. +- Produces: + +```ts +export type HousekeepingSurface = "preview" | "canonical"; +export type CanonicalHousekeepingHref = `/ase${string}`; +export function toHousekeepingHref( + href: CanonicalHousekeepingHref, + surface: HousekeepingSurface, +): CanonicalHousekeepingHref | `/ase-next${string}`; +``` + +- `HousekeepingDomainManifest.canonicalHref` is a `CanonicalHousekeepingHref`; + Operations uses `/ase`, while the other domains use `/ase/`. Route + definitions store canonical `/ase` hrefs only. + +- [ ] Write failing href tests proving `/ase` stays `/ase`, `/ase/people/users` maps to `/ase-next/people/users` in preview, and `/admin` input is rejected by the type/runtime guard. +- [ ] Run `pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/foundation/routing/href.test.ts` and confirm RED. +- [ ] Implement `toHousekeepingHref`, replace `previewHref` with `canonicalHref`, and pass `surface` into `buildHousekeepingNavigation`. +- [ ] Change every non-null migration `targetPath` prefix from `/admin` to `/ase`; change validation to require `targetPath === "/ase" || targetPath.startsWith("/ase/")`. +- [ ] Move the preview route tree to `src/app/ase-next`, update source contracts/localized test fixtures, and prove production still calls `notFound()`. +- [ ] Run `pnpm hk:matrix:check` and the routing, registry, navigation, preview, matrix, and theme-source tests; confirm 137 discovered rows and zero issues. +- [ ] Run targeted Biome, `git diff --check`, stage only listed paths, and commit `refactor(housekeeping): adopt ase route namespace`. + +### Task 2: Stabilize result, error, correlation, and route contracts + +**Files:** + +- Modify: `src/features/housekeeping/foundation/contracts/result.ts` +- Modify: `src/features/housekeeping/foundation/contracts/query.ts` +- Modify: `src/features/housekeeping/foundation/contracts/command.ts` +- Modify: `src/features/housekeeping/foundation/contracts/search.ts` +- Modify: `src/features/housekeeping/foundation/contracts/inbox.ts` +- Modify: `src/features/housekeeping/foundation/contracts/widget.ts` +- Modify: `src/features/housekeeping/foundation/contracts/domain.ts` +- Modify: `src/features/housekeeping/foundation/contracts/index.ts` +- Modify: `src/features/housekeeping/foundation/contracts/contracts.test.ts` +- Create: `src/features/housekeeping/foundation/correlation.ts` +- Create: `src/features/housekeeping/foundation/correlation.test.ts` + +**Interfaces:** + +```ts +export type HousekeepingErrorCode = + | "UNAUTHENTICATED" | "FORBIDDEN" | "VALIDATION" | "NOT_FOUND" + | "CONFLICT" | "RATE_LIMITED" | "DEPENDENCY_UNAVAILABLE" + | "TIMEOUT" | "INTERNAL"; +export interface HousekeepingError { + code: HousekeepingErrorCode; + messageKey: string; + fieldErrors?: Readonly>; +} +export type HousekeepingResult = + | { ok: true; data: T; correlationId: string } + | { ok: false; error: HousekeepingError; correlationId: string }; +export function createCorrelationId(): string; +``` + +- [ ] Replace the obsolete error-code expectations with table-driven tests for all nine approved codes, field errors, success, and correlation preservation. +- [ ] Run the contracts and correlation tests and confirm RED on the renamed taxonomy. +- [ ] Implement the types, `ok`, `fail`, `mapUnknownError`, and a 64-character-safe correlation generator using `crypto.randomUUID()`. +- [ ] Extend search results with `type`, `description`, canonical href, and capability metadata; extend inbox items with priority, age/state, actions; allow widget loaders to accept an abort signal. +- [ ] Run the two tests plus `pnpm typecheck`; fix all foundation callers without weakening types. +- [ ] Run targeted Biome, `git diff --check`, stage exact contract files, and commit `refactor(housekeeping): stabilize service contracts`. + +### Task 3: Make capability context request-scoped and reusable + +**Files:** + +- Modify: `src/features/housekeeping/foundation/server-capability-context.ts` +- Modify: `src/features/housekeeping/foundation/server-capability-context.test.ts` +- Modify: `src/features/housekeeping/foundation/capability-context.ts` +- Modify: `src/features/housekeeping/foundation/capability-context.test.ts` +- Modify: `src/lib/admin/guard.ts` +- Modify: `src/lib/admin/guard.test.ts` +- Create: `src/features/housekeeping/foundation/authorization.ts` +- Create: `src/features/housekeeping/foundation/authorization.test.ts` + +**Interfaces:** + +```ts +export const getHousekeepingCapabilityContext: () => + Promise; +export function authorizeHousekeeping( + context: HousekeepingCapabilityContext, + requirement: CapabilityRequirement, +): HousekeepingResult; +export function requireHousekeepingCapability( + requirement: CapabilityRequirement, + context?: HousekeepingCapabilityContext, +): Promise; +``` + +- [ ] Write tests proving one request calls `getAdminContext()` once across shell, page, and command preflight; denied checks return `FORBIDDEN` without rank thresholds. +- [ ] Run the capability, authorization, and guard tests and confirm RED. +- [ ] Keep React `cache()` as the request boundary, add reusable authorization functions, and let HK guards accept an already-created context. +- [ ] Retain legacy `requireStaff`, `requirePermission`, and `/admin` fallbacks unchanged until cutover; remove only duplicate HK lookups. +- [ ] Run targeted tests and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `refactor(housekeeping): reuse request capability context`. + +### Task 4: Add the audit and preferences schema additively + +**Files:** + +- Create: `drizzle/migrations/0023_housekeeping.sql` +- Modify: `src/db/schema.ts` +- Create: `src/features/housekeeping/foundation/persistence-contract.test.ts` + +**Interfaces:** + +```ts +export type HousekeepingUserPreferenceRow = + typeof HousekeepingUserPreferences.$inferSelect; +// admin_audit_log adds correlationId, outcome, reason, and domain. +``` + +Migration shape: + +```sql +ALTER TABLE `admin_audit_log` + ADD COLUMN `correlation_id` VARCHAR(64) NULL, + ADD COLUMN `outcome` VARCHAR(32) NULL, + ADD COLUMN `reason` TEXT NULL, + ADD COLUMN `domain` VARCHAR(32) NULL, + ADD INDEX `admin_audit_log_correlation_id_idx` (`correlation_id`); +CREATE TABLE `housekeeping_user_preferences` ( + `user_id` INT NOT NULL, + `schema_version` INT NOT NULL DEFAULT 1, + `payload` LONGTEXT NOT NULL, + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`user_id`) +); +``` + +- [ ] Write a source-contract test asserting exact nullable audit columns/index, exact preference columns, one primary key, and no `DROP`, `RENAME`, or legacy-column mutation. +- [ ] Run the persistence contract and confirm RED. +- [ ] Add migration `0023` and matching Drizzle declarations using existing schema naming conventions. +- [ ] Run the contract, `pnpm typecheck`, and `pnpm db:migrate:status` against the configured non-production environment only when available; record an unavailable DB as unexecuted, not passed. +- [ ] Run targeted Biome, `git diff --check`, stage the migration/schema/test, and commit `feat(housekeeping): add audit and preference storage`. + +### Task 5: Extend audit writing and correlation evidence + +**Files:** + +- Modify: `src/lib/services/audit.ts` +- Modify: `src/lib/services/audit.test.ts` +- Create: `src/features/housekeeping/foundation/commands/audit-envelope.ts` +- Create: `src/features/housekeeping/foundation/commands/audit-envelope.test.ts` + +**Interfaces:** + +```ts +export interface AuditEntry { + userId: number; + action: string; + target: string; + targetId?: number; + before?: Record; + after?: Record; + correlationId?: string; + outcome?: "intent" | "success" | "failure" | "partial" | "denied"; + reason?: string; + domain?: HousekeepingDomainId; + ipAddress?: string; +} +export interface HousekeepingAuditWriter { + write(entry: AuditEntry, transaction?: HousekeepingAuditTransaction): Promise; +} +``` + +- [ ] Add tests for recursive secret redaction, correlation/domain/outcome persistence, denied/failure evidence, and transaction-injected writes. +- [ ] Run audit tests and confirm RED. +- [ ] Export the audit payload type, preserve existing call compatibility, and add a writer adapter that can use either `db` or the supplied transaction. +- [ ] Implement `writeIntent` and `writeOutcome`; block external/file execution if intent persistence fails. +- [ ] Run audit and envelope tests plus `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): correlate command audit evidence`. + +### Task 6: Implement preferences validation, repository, and reconciliation + +**Files:** + +- Create: `src/features/housekeeping/foundation/preferences/schema.ts` +- Create: `src/features/housekeeping/foundation/preferences/schema.test.ts` +- Create: `src/features/housekeeping/foundation/preferences/repository.ts` +- Create: `src/features/housekeeping/foundation/preferences/repository.test.ts` +- Create: `src/features/housekeeping/foundation/preferences/reconcile.ts` +- Create: `src/features/housekeeping/foundation/preferences/reconcile.test.ts` +- Create: `src/actions/housekeeping-preferences.ts` +- Create: `src/actions/housekeeping-preferences.test.ts` + +**Interfaces:** + +```ts +export const housekeepingPreferencesSchema: z.ZodType<{ + schemaVersion: 1; + pinnedRouteIds: string[]; + pinnedCommandIds: string[]; + shortcutOrder: string[]; + widgetOrder: string[]; + enabledOptionalWidgetIds: string[]; +}>; +export interface HousekeepingPreferencesRepository { + read(userId: number): Promise; + upsert(userId: number, value: HousekeepingPreferences): Promise; +} +export function reconcilePreferences( + stored: HousekeepingPreferences, + registry: HousekeepingRegistry, + context: HousekeepingCapabilityContext, +): HousekeepingPreferences; +``` + +- [ ] Write failing schema tests for malformed JSON, unknown keys, duplicate IDs, and schema version; write repository tests for absent/default and upsert behavior. +- [ ] Write reconciliation tests proving unauthorized/removed IDs are dropped, mandatory widgets remain, and relative order of valid IDs is stable. +- [ ] Run all four preference test files and confirm RED. +- [ ] Implement Zod validation, injected repository DB adapter, deterministic reconciliation, and capability-checked load/save server actions. +- [ ] Run preference tests, `pnpm typecheck`, and the Housekeeping suite. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): persist operator preferences`. + +### Task 7: Build bounded provider orchestration + +**Files:** + +- Create: `src/features/housekeeping/foundation/providers/run-provider.ts` +- Create: `src/features/housekeeping/foundation/providers/run-provider.test.ts` +- Create: `src/features/housekeeping/foundation/providers/orchestrate.ts` +- Create: `src/features/housekeeping/foundation/providers/orchestrate.test.ts` + +**Interfaces:** + +```ts +export interface ProviderPolicy { + timeoutMs: number; + perProviderLimit: number; + combinedLimit: number; + sort(items: readonly T[]): readonly T[]; + dedupeKey(item: T): string; +} +export interface ProviderBatchResult { + items: readonly T[]; + errors: readonly { providerId: string; code: HousekeepingErrorCode }[]; + correlationId: string; +} +``` + +- [ ] Use fake timers to test a 2,000 ms abort, capability-skipped providers, thrown errors mapped once, stable dedupe, per-provider cap, combined cap, and deterministic ordering. +- [ ] Run both provider tests and confirm RED. +- [ ] Implement orchestration without importing domain modules or database clients; accept providers and policy through arguments. +- [ ] Prove one timeout returns successful sibling results with a typed partial error. +- [ ] Run provider tests and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): orchestrate partial providers`. + +### Task 8: Implement the typed command dispatcher + +**Files:** + +- Create: `src/features/housekeeping/foundation/commands/registry.ts` +- Create: `src/features/housekeeping/foundation/commands/registry.test.ts` +- Create: `src/features/housekeeping/foundation/commands/dispatcher.ts` +- Create: `src/features/housekeeping/foundation/commands/dispatcher.test.ts` +- Create: `src/features/housekeeping/foundation/commands/confirmation.ts` +- Create: `src/features/housekeeping/foundation/commands/confirmation.test.ts` +- Create: `src/actions/housekeeping-command.ts` +- Create: `src/actions/housekeeping-command.test.ts` + +**Interfaces:** + +```ts +export interface HousekeepingCommand { + id: string; + owner: HousekeepingDomainId; + risk: "safe" | "sensitive"; + capability: CapabilityRequirement; + input: z.ZodType; + requiresReason: boolean; + rateLimit: { attempts: number; windowMs: number }; + execute(ctx: HousekeepingCommandContext, input: I): Promise>; +} +export interface HousekeepingCommandContext { + capability: HousekeepingCapabilityContext; + correlationId: string; + ipAddress: string; +} +export interface HousekeepingCommandDependencies { + context: HousekeepingCapabilityContext; + audit: HousekeepingAuditWriter; + rateLimit(key: string, attempts: number, windowMs: number): Promise; +} +export async function dispatchHousekeepingCommand( + request: { commandId: string; input: unknown; reason?: string }, + dependencies: HousekeepingCommandDependencies, +): Promise>; +``` + +- [ ] Write tests for unknown command, denied capability, invalid input, missing reason, rate limit, safe success, sensitive intent/success, sensitive intent/failure, and sanitized unknown exception. +- [ ] Run dispatcher/action tests and confirm RED. +- [ ] Implement global ID/owner validation, server-side capability recheck, Zod parsing, per-actor/IP limit, confirmation metadata, and audit envelope usage. +- [ ] Ensure server actions accept plain serializable values and never trust client risk/capability metadata. +- [ ] Run all command tests, audit tests, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): dispatch audited commands`. + +### Task 9: Resolve canonical routes and dispatch domain pages + +**Files:** + +- Create: `src/features/housekeeping/foundation/routing/match-route.ts` +- Create: `src/features/housekeeping/foundation/routing/match-route.test.ts` +- Create: `src/features/housekeeping/route-handlers.ts` +- Create: `src/features/housekeeping/route-handlers.test.ts` +- Modify: `src/app/ase-next/page.tsx` +- Modify: `src/app/ase-next/[domain]/layout.tsx` +- Create: `src/app/ase-next/[domain]/[[...segments]]/page.tsx` +- Delete: `src/app/ase-next/[domain]/page.tsx` + +**Interfaces:** + +```ts +export interface HousekeepingRouteMatch { + routeId: string; + domain: HousekeepingDomainId; + params: Readonly>; + canonicalHref: CanonicalHousekeepingHref; +} +export interface HousekeepingRouteHandler { + routeId: string; + render(input: HousekeepingPageInput): Promise; +} +export function matchHousekeepingRoute( + registry: HousekeepingRegistry, + canonicalPath: string, +): HousekeepingRouteMatch | null; +``` + +- [ ] Write matcher tests for static, `:id`, nested, unknown, malformed, and cross-domain paths; test one-to-one equality between registered route IDs and handler IDs. +- [ ] Run matcher/handler tests and confirm RED. +- [ ] Implement segment-safe matching without regular-expression injection; reject duplicate dynamic shapes during registry creation. +- [ ] Update preview pages to map `/ase-next//` to canonical `/ase//`, reauthorize the matched route, and call its handler. +- [ ] Test inaccessible routes as `notFound()` and permitted routes with one request-scoped context. +- [ ] Run routing, registry, preview-route tests and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact paths, and commit `feat(housekeeping): dispatch canonical domain routes`. + +### Task 10: Deliver System access, configuration, observability, and operations + +**Files:** + +- Create: `src/features/housekeeping/domains/system/routes.ts` +- Create: `src/features/housekeeping/domains/system/routes.test.ts` +- Create: `src/features/housekeeping/domains/system/queries/access.ts` +- Create: `src/features/housekeeping/domains/system/queries/configuration.ts` +- Create: `src/features/housekeeping/domains/system/queries/observability.ts` +- Create: `src/features/housekeeping/domains/system/queries/operations.ts` +- Create: `src/features/housekeeping/domains/system/queries/system-queries.test.ts` +- Create: `src/features/housekeeping/domains/system/commands/system-commands.ts` +- Create: `src/features/housekeeping/domains/system/commands/system-commands.test.ts` +- Create: `src/features/housekeeping/domains/system/pages/access.tsx` +- Create: `src/features/housekeeping/domains/system/pages/configuration.tsx` +- Create: `src/features/housekeeping/domains/system/pages/observability.tsx` +- Create: `src/features/housekeeping/domains/system/pages/operations.tsx` +- Create: `src/features/housekeeping/domains/system/pages/system-pages.test.tsx` +- Create: `src/features/housekeeping/domains/system/route-handlers.ts` +- Modify: `src/features/housekeeping/domains/system/manifest.ts` +- Modify: `src/actions/admin-alerts.ts` +- Modify: `src/actions/admin-emulator.ts` +- Modify: `src/actions/admin-maintenance.ts` +- Modify: `src/actions/admin-settings.ts` +- Modify: `src/actions/commandocentrum.ts` +- Modify: `src/actions/permissions.ts` +- Modify: `src/lib/admin/ops-health.ts` +- Modify: `src/lib/admin/ops-online-users.ts` + +**Interfaces:** + +```ts +export const SYSTEM_ROUTE_IDS = [ + "system.access.permissions", "system.access.permission-detail", + "system.configuration.settings", "system.configuration.emulator", + "system.observability.analytics", "system.observability.analytics-activity", + "system.observability.analytics-economy", "system.observability.devops", + "system.observability.devops-errors", "system.observability.logs-staff", + "system.observability.logs-audit", "system.observability.logs-chat", + "system.observability.logs-commands", "system.observability.logs-trades", + "system.operations.alerts", "system.operations.command-center", + "system.operations.maintenance", +] as const; +``` + +- [ ] Write route tests asserting the exact route IDs above, canonical `/ase/system/*` destinations, matrix coverage, labels, and read capabilities. +- [ ] Write query tests using injected adapters for ACL/ranks, settings, emulator data, analytics/logs, health, errors, alerts, and maintenance; include dependency-unavailable mapping. +- [ ] Write command tests for rank/ACL writes, settings/emulator updates, alerts, RCON commands, and maintenance; require reasons for permission, RCON, and global-availability mutations. +- [ ] Run System tests and confirm RED before each production module is added. +- [ ] Extract redirect-free mutation functions from the listed legacy actions; keep legacy action wrappers and `/admin` redirects working until cutover. +- [ ] Build the four workflow pages with loading/empty/partial/error/forbidden states, then register real System routes and commands; Task 19 registers the System search, inbox, and widget providers. +- [ ] Run System, legacy action, audit, authorization, full HK tests, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact System/action files, and commit `feat(housekeeping): deliver system vertical`. + +### Task 11: Build People read adapters and canonical data models + +**Files:** + +- Create: `src/features/housekeeping/domains/people/routes.ts` +- Create: `src/features/housekeeping/domains/people/routes.test.ts` +- Create: `src/features/housekeeping/domains/people/queries/users.ts` +- Create: `src/features/housekeeping/domains/people/queries/community.ts` +- Create: `src/features/housekeeping/domains/people/queries/staff.ts` +- Create: `src/features/housekeeping/domains/people/queries/support.ts` +- Create: `src/features/housekeeping/domains/people/queries/moderation.ts` +- Create: `src/features/housekeeping/domains/people/queries/people-queries.test.ts` +- Create: `src/features/housekeeping/domains/people/models.ts` +- Create: `src/features/housekeeping/domains/people/models.test.ts` + +**Interfaces:** + +```ts +export interface PeopleUserSummary { + id: number; username: string; rank: number; online: boolean; + mail: string | null; ipCurrent: string | null; bannedUntil: number | null; +} +export interface PeopleQueueSnapshot { + tickets: number; helpTickets: number; cfh: number; activeBans: number; +} +export interface PeopleQueries { + users(input: ListInput): Promise>>; + user(id: number): Promise>; + queue(): Promise>; +} +``` + +- [ ] Write exact route-catalog tests for users, multi-accounts, online, guilds, applications, teams, bans, IP/VPN/wordfilter, tickets, help tickets, CFH, moderation actions, and mod-team workflows. +- [ ] Write query tests for pagination, stable sorting, missing users, capability-safe projections, ticket/CFH counts, guild detail, staff applications, sanctions, and dependency failures. +- [ ] Run People model/query/route tests and confirm RED. +- [ ] Implement server-only adapters around the matrix-listed Drizzle/RCON/service dependencies; never expose password hashes, auth tickets, secrets, or unredacted IPs without their explicit capability. +- [ ] Map all returned failures to the stable HK error set and canonical `/ase/people/*` links. +- [ ] Run People tests and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact People query files, and commit `feat(housekeeping): model people workflows`. + +### Task 12: Deliver People users, community, and staff workflows + +**Files:** + +- Create: `src/features/housekeeping/domains/people/commands/user-commands.ts` +- Create: `src/features/housekeeping/domains/people/commands/user-commands.test.ts` +- Create: `src/features/housekeeping/domains/people/commands/community-commands.ts` +- Create: `src/features/housekeeping/domains/people/commands/community-commands.test.ts` +- Create: `src/features/housekeeping/domains/people/pages/users.tsx` +- Create: `src/features/housekeeping/domains/people/pages/user-detail.tsx` +- Create: `src/features/housekeeping/domains/people/pages/user-edit.tsx` +- Create: `src/features/housekeeping/domains/people/pages/multi-accounts.tsx` +- Create: `src/features/housekeeping/domains/people/pages/community.tsx` +- Create: `src/features/housekeeping/domains/people/pages/staff.tsx` +- Create: `src/features/housekeeping/domains/people/pages/people-primary-pages.test.tsx` +- Modify: `src/actions/bulk-users.ts` +- Modify: `src/actions/users.ts` +- Modify: `src/actions/admin-guilds.ts` +- Modify: `src/actions/admin-applications.ts` +- Modify: `src/actions/admin-teams.ts` +- Modify: `src/actions/admin-ip.ts` +- Modify: `src/actions/admin-vpn.ts` +- Modify: `src/actions/admin-wordfilter.ts` + +**Interfaces:** + +```ts +export type UserCommandId = + | "people.user.update" | "people.user.ban" | "people.user.unban" + | "people.user.alert" | "people.user.disconnect" | "people.user.mute" + | "people.user.unmute" | "people.user.reset-password" + | "people.user.send-currency" | "people.user.trade-lock" + | "people.users.bulk-ban" | "people.users.bulk-unban" + | "people.users.bulk-currency" | "people.users.bulk-badge"; +``` + +- [ ] Write command tests for the exact IDs above plus guild disband, application decision, team change, IP action, VPN configuration, and wordfilter update; assert server capability rechecks and audit before/after. +- [ ] Run command tests and confirm RED. +- [ ] Extract redirect-free service functions from listed actions while preserving legacy server-action wrappers. +- [ ] Implement user list/detail/edit and multi-account pages; preserve existing fields/actions only when the matrix capability permits them. +- [ ] Implement community and staff workflows with canonical links and no duplicate show/edit aliases. +- [ ] Run People primary page/command tests, affected legacy tests, HK suite, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): deliver people account workflows`. +### Task 13: Deliver People support and moderation parity + +**Files:** + +- Create: `src/features/housekeeping/domains/people/commands/support-commands.ts` +- Create: `src/features/housekeeping/domains/people/commands/moderation-commands.ts` +- Create: `src/features/housekeeping/domains/people/commands/support-commands.test.ts` +- Create: `src/features/housekeeping/domains/people/commands/moderation-commands.test.ts` +- Create: `src/features/housekeeping/domains/people/pages/support.tsx` +- Create: `src/features/housekeeping/domains/people/pages/moderation.tsx` +- Create: `src/features/housekeeping/domains/people/pages/cfh-detail.tsx` +- Create: `src/features/housekeeping/domains/people/pages/ticket-detail.tsx` +- Create: `src/features/housekeeping/domains/people/pages/help-ticket-detail.tsx` +- Create: `src/features/housekeeping/domains/people/pages/people-support-pages.test.tsx` +- Create: `src/features/housekeeping/domains/people/search.ts` +- Create: `src/features/housekeeping/domains/people/inbox.ts` +- Create: `src/features/housekeeping/domains/people/widgets.ts` +- Create: `src/features/housekeeping/domains/people/people-providers.test.ts` +- Create: `src/features/housekeeping/domains/people/route-handlers.ts` +- Modify: `src/features/housekeeping/domains/people/manifest.ts` +- Modify: `src/actions/admin-bans.ts` +- Modify: `src/actions/admin-help-tickets.ts` +- Modify: `src/actions/help-tickets.ts` +- Modify: `src/actions/moderation.ts` +- Modify: `src/actions/tickets.ts` +- Modify: `src/actions/ticket-templates.ts` +- Modify: `src/lib/services/moderation.ts` +- Modify: `src/lib/services/ticket-replies.ts` + +**Interfaces:** + +```ts +export const PEOPLE_INBOX_SOURCE_IDS = [ + "people.tickets", "people.help-tickets", "people.cfh", "people.active-bans", +] as const; +export const PEOPLE_SEARCH_PROVIDER_IDS = [ + "people.users", "people.guilds", "people.tickets", +] as const; +``` + +- [ ] Write failing parity tests joining all People matrix rows, including every `/mod` row, to one route or removed decision. +- [ ] Write support/moderation command tests for assign/reply/close/reopen/template, CFH resolve/sanction, ban/unban, and moderation action; require reasons for sanctions and bans. +- [ ] Implement support and moderation pages with queue/detail flows, safe links, preserved mid-rank `mod.*` access, and no `admin.dashboard` dependency when the original route did not require it. +- [ ] Implement the exact People search/inbox providers and mandatory queue widget; enforce 25-result and item capability filtering at provider level. +- [ ] Register handlers/providers/widgets and prove no People manifest collection is empty. +- [ ] Run People, legacy moderation/ticket tests, HK suite, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): complete people moderation parity`. + +### Task 14: Deliver Content and engagement + +**Files:** + +- Create: `src/features/housekeeping/domains/content/routes.ts` +- Create: `src/features/housekeeping/domains/content/routes.test.ts` +- Create: `src/features/housekeeping/domains/content/queries/content-queries.ts` +- Create: `src/features/housekeeping/domains/content/queries/content-queries.test.ts` +- Create: `src/features/housekeeping/domains/content/commands/content-commands.ts` +- Create: `src/features/housekeeping/domains/content/commands/content-commands.test.ts` +- Create: `src/features/housekeeping/domains/content/pages/editorial.tsx` +- Create: `src/features/housekeeping/domains/content/pages/media.tsx` +- Create: `src/features/housekeeping/domains/content/pages/engagement.tsx` +- Create: `src/features/housekeeping/domains/content/pages/help.tsx` +- Create: `src/features/housekeeping/domains/content/pages/brand.tsx` +- Create: `src/features/housekeeping/domains/content/pages/localization.tsx` +- Create: `src/features/housekeeping/domains/content/pages/content-pages.test.tsx` +- Create: `src/features/housekeeping/domains/content/search.ts` +- Create: `src/features/housekeeping/domains/content/inbox.ts` +- Create: `src/features/housekeeping/domains/content/widgets.ts` +- Create: `src/features/housekeeping/domains/content/route-handlers.ts` +- Create: `src/features/housekeeping/domains/content/content-providers.test.ts` +- Modify: `src/features/housekeeping/domains/content/manifest.ts` +- Modify: `src/actions/admin-ads.ts` +- Modify: `src/actions/admin-articles.ts` +- Modify: `src/actions/admin-banners.ts` +- Modify: `src/actions/admin-email-templates.ts` +- Modify: `src/actions/admin-help.ts` +- Modify: `src/actions/admin-media.ts` +- Modify: `src/actions/admin-nav-menu.ts` +- Modify: `src/actions/admin-photos.ts` +- Modify: `src/actions/admin-tags.ts` +- Modify: `src/actions/admin-theme.ts` +- Modify: `src/actions/admin-writeable-boxes.ts` +- Modify: `src/actions/banners.ts` +- Modify: `src/actions/events.ts` +- Modify: `src/actions/polls.ts` +- Modify: `src/actions/prefixes.ts` +- Modify: `src/actions/save-favicon.ts` +- Modify: `src/actions/save-logo.ts` +- Modify: `src/actions/translations.ts` + +**Interfaces:** + +```ts +export const CONTENT_ROUTE_GROUPS = [ + "editorial", "media", "engagement", "help", "brand", "localization", +] as const; +export const CONTENT_SEARCH_PROVIDER_IDS = [ + "content.articles", "content.events", "content.media", "content.help", +] as const; +``` + +- [ ] Write route tests mapping every Content matrix row to one of the six exact route groups and canonical `/ase/content/*` destinations. +- [ ] Write query/command tests for articles, ads, banners, events/types, polls, photos/media, navigation, tags/prefixes, help questions, writable boxes, email templates, theme/favicon, and three translation stores. +- [ ] Run Content tests and confirm RED before production implementations. +- [ ] Extract redirect-free domain operations from the listed actions, retain legacy wrappers, and ensure global brand/localization mutations are sensitive and audited. +- [ ] Implement the six workflow pages, content search providers, publication/attention inbox source, mandatory editorial summary, and optional media/localization widgets. +- [ ] Register real routes/commands/providers/widgets and prove Content matrix closure. +- [ ] Run Content, affected legacy tests, HK suite, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): deliver content vertical`. + +### Task 15: Deliver Economy and catalog + +**Files:** + +- Create: `src/features/housekeeping/domains/economy/routes.ts` +- Create: `src/features/housekeeping/domains/economy/routes.test.ts` +- Create: `src/features/housekeeping/domains/economy/queries/catalog.ts` +- Create: `src/features/housekeeping/domains/economy/queries/commerce.ts` +- Create: `src/features/housekeeping/domains/economy/queries/value.ts` +- Create: `src/features/housekeeping/domains/economy/queries/economy-queries.test.ts` +- Create: `src/features/housekeeping/domains/economy/commands/economy-commands.ts` +- Create: `src/features/housekeeping/domains/economy/commands/economy-commands.test.ts` +- Create: `src/features/housekeeping/domains/economy/pages/catalog.tsx` +- Create: `src/features/housekeeping/domains/economy/pages/items.tsx` +- Create: `src/features/housekeeping/domains/economy/pages/commerce.tsx` +- Create: `src/features/housekeeping/domains/economy/pages/history.tsx` +- Create: `src/features/housekeeping/domains/economy/pages/value.tsx` +- Create: `src/features/housekeeping/domains/economy/pages/rewards.tsx` +- Create: `src/features/housekeeping/domains/economy/pages/economy-pages.test.tsx` +- Create: `src/features/housekeeping/domains/economy/search.ts` +- Create: `src/features/housekeeping/domains/economy/inbox.ts` +- Create: `src/features/housekeeping/domains/economy/widgets.ts` +- Create: `src/features/housekeeping/domains/economy/route-handlers.ts` +- Create: `src/features/housekeeping/domains/economy/economy-providers.test.ts` +- Modify: `src/features/housekeeping/domains/economy/manifest.ts` +- Modify: `src/actions/catalog.ts` +- Modify: `src/actions/catalog-bc.ts` +- Modify: `src/actions/catalog-items.ts` +- Modify: `src/actions/items-base.ts` +- Modify: `src/actions/admin-marketplace.ts` +- Modify: `src/actions/admin-rare-values.ts` +- Modify: `src/actions/admin-shop.ts` +- Modify: `src/actions/admin-vouchers.ts` +- Modify: `src/actions/shop.ts` +- Modify: `src/actions/soundtracks.ts` +- Modify: `src/actions/voucher.ts` +- Modify: `src/lib/services/catalog-audit.ts` +- Modify: `src/lib/services/catalog-items-loader.ts` +- Modify: `src/lib/services/catalog-tree.ts` +- Modify: `src/lib/services/paypal.ts` +- Modify: `src/lib/services/paypal-topup.ts` +- Modify: `src/lib/services/send-currency.ts` + +**Interfaces:** + +```ts +export const ECONOMY_ROUTE_GROUPS = [ + "catalog", "items", "commerce", "history", "value", "rewards", +] as const; +export const ECONOMY_SEARCH_PROVIDER_IDS = [ + "economy.catalog-pages", "economy.items", "economy.transactions", +] as const; +``` + +- [ ] Write route tests covering catalog/detail/Builder Club/maintenance, items/detail, shop, marketplace, transactions, vouchers, subscriptions, rare values, badges, achievements, sounds, and calendar matrix rows. +- [ ] Write adapter tests for catalog trees/items, commerce history, transactions, vouchers/subscriptions, marketplace, rare values, rewards, sounds, and calendar; verify stable pagination and money/value serialization. +- [ ] Write sensitive command tests for catalog/item edits, maintenance, vouchers, shop changes, rare values, badge/reward changes, and destructive catalog operations with reason/audit requirements. +- [ ] Run Economy tests and confirm RED before implementation. +- [ ] Extract redirect-free domain services, implement six pages plus exact providers/widgets, and retain specialized catalog editors as components inside canonical workflows. +- [ ] Register the Economy manifest and prove matrix closure and non-empty route/search/inbox/widget collections. +- [ ] Run Economy, catalog/service, HK tests, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): deliver economy vertical`. + +### Task 16: Deliver Hotel rooms, radio, badges, and runtime tools + +**Files:** + +- Create: `src/features/housekeeping/domains/hotel/routes.ts` +- Create: `src/features/housekeeping/domains/hotel/routes.test.ts` +- Create: `src/features/housekeeping/domains/hotel/queries/rooms.ts` +- Create: `src/features/housekeeping/domains/hotel/queries/radio.ts` +- Create: `src/features/housekeeping/domains/hotel/queries/assets.ts` +- Create: `src/features/housekeeping/domains/hotel/queries/hotel-queries.test.ts` +- Create: `src/features/housekeeping/domains/hotel/commands/room-commands.ts` +- Create: `src/features/housekeeping/domains/hotel/commands/radio-commands.ts` +- Create: `src/features/housekeeping/domains/hotel/commands/asset-commands.ts` +- Create: `src/features/housekeeping/domains/hotel/commands/hotel-commands.test.ts` +- Create: `src/features/housekeeping/domains/hotel/pages/rooms.tsx` +- Create: `src/features/housekeeping/domains/hotel/pages/room-detail.tsx` +- Create: `src/features/housekeeping/domains/hotel/pages/room-furni.tsx` +- Create: `src/features/housekeeping/domains/hotel/pages/radio.tsx` +- Create: `src/features/housekeeping/domains/hotel/pages/badges.tsx` +- Create: `src/features/housekeeping/domains/hotel/pages/sounds.tsx` +- Create: `src/features/housekeeping/domains/hotel/pages/hotel-pages.test.tsx` +- Modify: `src/actions/rooms.ts` +- Modify: `src/actions/admin-radio-api-keys.ts` +- Modify: `src/actions/admin-radio-autodj.ts` +- Modify: `src/actions/admin-radio-extra.ts` +- Modify: `src/actions/admin-radio-moderation.ts` +- Modify: `src/actions/admin-radio-points.ts` +- Modify: `src/actions/admin-badges.ts` +- Modify: `src/actions/admin-badge-upload.ts` +- Modify: `src/lib/services/radio.ts` +- Modify: `src/lib/services/import-badge.ts` +- Modify: `src/lib/services/rcon.ts` +- Modify: `src/lib/services/soundtracks.ts` + +**Interfaces:** + +```ts +export const HOTEL_PRIMARY_ROUTE_IDS = [ + "hotel.rooms", "hotel.room-detail", "hotel.room-furni", + "hotel.radio.overview", "hotel.radio.settings", "hotel.radio.monitoring", + "hotel.radio.moderation", "hotel.radio.autodj", "hotel.radio.history", + "hotel.radio.points", "hotel.radio.ranks", "hotel.radio.api-keys", + "hotel.radio.banners", "hotel.radio.embed", "hotel.badges", "hotel.sounds", +] as const; +``` + +- [ ] Write route tests proving room show/edit aliases merge into one detail and one furni route; assert every radio/badge/sound matrix row has a canonical destination. +- [ ] Write query tests for room/owner/furni, radio configuration/monitoring/history/ranks, badges, and soundtracks; include unavailable RCON/radio dependencies. +- [ ] Write command tests for room changes, furni movement/removal, radio configuration/moderation/API keys/points, badge upload, and sounds; require reasons for destructive or external operations. +- [ ] Run Hotel tests and confirm RED. +- [ ] Extract redirect-free services, implement the six workflow page modules, and keep legacy wrappers live until cutover. +- [ ] Run Hotel primary, RCON/radio/sound, HK tests, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): deliver hotel operations`. + +### Task 17: Integrate Studio and long-running asset operations + +**Files:** + +- Create: `src/features/housekeeping/domains/hotel/queries/studio.ts` +- Create: `src/features/housekeeping/domains/hotel/queries/studio.test.ts` +- Create: `src/features/housekeeping/domains/hotel/commands/studio-commands.ts` +- Create: `src/features/housekeeping/domains/hotel/commands/studio-commands.test.ts` +- Create: `src/features/housekeeping/domains/hotel/pages/studio.tsx` +- Create: `src/features/housekeeping/domains/hotel/pages/studio.test.tsx` +- Create: `src/features/housekeeping/domains/hotel/components/operation-progress.tsx` +- Create: `src/features/housekeeping/domains/hotel/components/operation-result.tsx` +- Create: `src/features/housekeeping/domains/hotel/components/operation-progress.test.tsx` +- Create: `src/features/housekeeping/domains/hotel/search.ts` +- Create: `src/features/housekeeping/domains/hotel/inbox.ts` +- Create: `src/features/housekeeping/domains/hotel/widgets.ts` +- Create: `src/features/housekeeping/domains/hotel/route-handlers.ts` +- Create: `src/features/housekeeping/domains/hotel/hotel-providers.test.ts` +- Modify: `src/features/housekeeping/domains/hotel/manifest.ts` +- Modify: `src/actions/import-furni.ts` +- Modify: `src/actions/furni-maintenance.ts` +- Modify: `src/lib/services/clone-import.ts` +- Modify: `src/lib/services/clothing-set-import.ts` +- Modify: `src/lib/services/effect-import.ts` +- Modify: `src/lib/services/figure-import.ts` +- Modify: `src/lib/services/furni-import.ts` +- Modify: `src/lib/services/furni-maintenance.ts` +- Modify: `src/lib/services/pet-import.ts` +- Modify: `src/lib/services/repair-icons.ts` +- Modify: `src/lib/services/repair-nitros.ts` +- Modify: `src/lib/services/upload-import.ts` + +**Interfaces:** + +```ts +export type StudioOperationKind = + | "badge" | "clone" | "clothing" | "effect" | "furni" + | "maintenance" | "pet" | "repair-icons" | "sync" | "upload"; +export interface StudioOperationEvent { + operationId: string; + kind: StudioOperationKind; + phase: "queued" | "running" | "completed" | "failed" | "partial"; + completed: number; + total: number | null; + messageKey: string; + correlationId: string; +} +``` + +- [ ] Write tests for all ten Studio kinds, progress ordering, partial batch results, cancellation/abort propagation, unavailable external sources, and persisted intent before filesystem/RCON work. +- [ ] Run Studio tests and confirm RED. +- [ ] Wrap existing services with typed operations while preserving their current progress/error semantics and stable internal `/api/admin/import/*` transport paths. +- [ ] Implement the canonical `/ase/hotel/studio/*` workflow, operation progress/result components, Studio search, operational inbox source, and mandatory active-operation widget. +- [ ] Register every Hotel matrix route and prove no duplicate Studio root or orphan handler. +- [ ] Run Studio/import/service tests, Hotel tests, HK suite, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): integrate studio operations`. + +### Task 18: Implement global navigation, entity search, and command discovery + +**Files:** + +- Create: `src/features/housekeeping/foundation/search/search-service.ts` +- Create: `src/features/housekeeping/foundation/search/search-service.test.ts` +- Create: `src/features/housekeeping/foundation/search/navigation-search.ts` +- Create: `src/features/housekeeping/foundation/search/navigation-search.test.ts` +- Create: `src/actions/housekeeping-search.ts` +- Create: `src/actions/housekeeping-search.test.ts` +- Create: `src/features/housekeeping/foundation/shell/command-deck.tsx` +- Create: `src/features/housekeeping/foundation/shell/command-deck.test.tsx` +- Modify: `src/features/housekeeping/foundation/shell/command-trigger.tsx` +- Modify: `src/features/housekeeping/foundation/shell/housekeeping-shell.tsx` +- Modify: `src/features/housekeeping/foundation/shell/housekeeping-shell.test.tsx` + +**Interfaces:** + +```ts +export interface HousekeepingSearchResponse { + navigation: readonly HousekeepingNavigationHit[]; + commands: readonly HousekeepingCommandHit[]; + entities: readonly HousekeepingSearchResult[]; + errors: readonly { providerId: string; code: HousekeepingErrorCode }[]; + correlationId: string; +} +export function searchHousekeeping( + term: string, + context: HousekeepingCapabilityContext, +): Promise; +``` + +- [ ] Write tests proving trimmed terms shorter than two characters search navigation/commands only; two-character terms invoke permitted entity providers. +- [ ] Add orchestration tests for 2,000 ms/provider, 25/provider, 50 combined, stable dedupe/order, forbidden provider omission, and partial errors. +- [ ] Run search/action/deck tests and confirm RED. +- [ ] Implement registry navigation/command matching, domain entity orchestration, and a cmdk-based keyboard dialog with grouped results and canonical-to-preview href projection. +- [ ] Test open/close shortcut, focus restoration, arrow navigation, Enter activation, Escape, loading, no-results, and partial-provider UI. +- [ ] Run search, shell, provider, HK tests, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): add global command deck search`. + +### Task 19: Implement the derived operational inbox + +**Files:** + +- Create: `src/features/housekeeping/foundation/inbox/inbox-service.ts` +- Create: `src/features/housekeeping/foundation/inbox/inbox-service.test.ts` +- Create: `src/actions/housekeeping-inbox.ts` +- Create: `src/actions/housekeeping-inbox.test.ts` +- Create: `src/features/housekeeping/foundation/shell/operational-inbox.tsx` +- Create: `src/features/housekeeping/foundation/shell/operational-inbox.test.tsx` +- Create: `src/features/housekeeping/domains/system/search.ts` +- Create: `src/features/housekeeping/domains/system/inbox.ts` +- Create: `src/features/housekeeping/domains/system/widgets.ts` +- Create: `src/features/housekeeping/domains/system/system-providers.test.ts` +- Modify: `src/features/housekeeping/domains/system/manifest.ts` + +**Interfaces:** + +```ts +export interface HousekeepingInboxResponse { + items: readonly HousekeepingWorkItem[]; + errors: readonly { sourceId: string; code: HousekeepingErrorCode }[]; + correlationId: string; +} +export const INBOX_POLICY = { + timeoutMs: 2_000, + combinedLimit: 200, + dedupe: "sourceId:itemId", +} as const; +``` + +- [ ] Write tests for `(sourceId,itemId)` dedupe, capability filtering before count, priority then age ordering, 2,000 ms/source, 200-item cap, and partial-source failures. +- [ ] Run inbox action/service/component tests and confirm RED. +- [ ] Implement composition over registered People, Content, Economy, Hotel, and System sources without adding assignment/read-status persistence. +- [ ] Add System alert, emulator-error, and operational-anomaly sources plus System search/widgets that Task 10 registered conceptually. +- [ ] Implement accessible filters by domain/state/priority, canonical links, source error summaries, and empty/loading/partial states. +- [ ] Run inbox, all domain-provider, HK tests, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): compose operational inbox`. + +### Task 20: Add recent work, favorites, and widget personalization + +**Files:** + +- Create: `src/features/housekeeping/foundation/recent/recent-work.ts` +- Create: `src/features/housekeeping/foundation/recent/recent-work.test.ts` +- Create: `src/actions/housekeeping-recent.ts` +- Create: `src/actions/housekeeping-recent.test.ts` +- Create: `src/features/housekeeping/foundation/shell/recent-work.tsx` +- Create: `src/features/housekeeping/foundation/shell/favorites.tsx` +- Create: `src/features/housekeeping/foundation/shell/widget-grid.tsx` +- Create: `src/features/housekeeping/foundation/shell/widget-settings.tsx` +- Create: `src/features/housekeeping/foundation/shell/personalization.test.tsx` +- Modify: `src/features/housekeeping/foundation/registry.ts` +- Modify: `src/features/housekeeping/foundation/registry.test.ts` + +**Interfaces:** + +```ts +export interface HousekeepingRecentItem { + routeId: string; + canonicalHref: CanonicalHousekeepingHref; + labelKey: string; + occurredAt: string; + source: "route-visit" | "audit"; +} +export interface HousekeepingWidgetLoadResult { + widgets: readonly { id: string; data: unknown }[]; + errors: readonly { widgetId: string; code: HousekeepingErrorCode }[]; +} +``` + +- [ ] Write tests deriving recent work from `housekeeping.route.visit` and mutation audit rows, deduping by route ID, capability-filtering, and limiting to 12. +- [ ] Write component tests for pin/unpin, drag/keyboard ordering, mandatory widget lock, optional widget enable/disable, reconciled stale IDs, and partial widget failure. +- [ ] Run recent/personalization/action tests and confirm RED. +- [ ] Implement route-visit recording in `admin_audit_log`, recent-work queries, reconciled preference actions, dnd-kit ordering, and provider-orchestrated widget loading. +- [ ] Ensure preferences never authorize content and failed preference saves retain the previous UI state with an error announcement. +- [ ] Run preference, recent, widget, HK tests, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): personalize command deck`. + +### Task 21: Build the Operations workspace and complete shell composition + +**Files:** + +- Create: `src/features/housekeeping/domains/operations/routes.ts` +- Create: `src/features/housekeeping/domains/operations/routes.test.ts` +- Create: `src/features/housekeeping/domains/operations/queries.ts` +- Create: `src/features/housekeeping/domains/operations/queries.test.ts` +- Create: `src/features/housekeeping/domains/operations/pages/workspace.tsx` +- Create: `src/features/housekeeping/domains/operations/pages/workspace.test.tsx` +- Create: `src/features/housekeeping/domains/operations/search.ts` +- Create: `src/features/housekeeping/domains/operations/inbox.ts` +- Create: `src/features/housekeeping/domains/operations/widgets.ts` +- Create: `src/features/housekeeping/domains/operations/route-handlers.ts` +- Modify: `src/features/housekeeping/domains/operations/manifest.ts` +- Modify: `src/features/housekeeping/foundation/shell/housekeeping-shell.tsx` +- Modify: `src/features/housekeeping/foundation/shell/housekeeping-shell.test.tsx` +- Modify: `src/app/ase-next/page.tsx` + +**Interfaces:** + +```ts +export const OPERATIONS_ROUTES = [{ + id: "operations.workspace", + href: "/ase", + capability: anyCapability(PERMS.ADMIN_DASHBOARD), +}] as const; +export interface OperationsWorkspaceModel { + inbox: HousekeepingInboxResponse; + recent: readonly HousekeepingRecentItem[]; + favorites: HousekeepingPreferences; + widgets: HousekeepingWidgetLoadResult; +} +``` + +- [ ] Write route/matrix tests mapping the legacy `/admin` row to `operations.workspace` at canonical `/ase`. +- [ ] Write workspace tests for capability-specific sections, independent partial failures, no accessible domain, and preview links. +- [ ] Run Operations tests and confirm RED. +- [ ] Implement parallel inbox/recent/preferences/widget loading and render the operational home directly at `/ase-next`; the canonical cutover renders the same handler directly at `/ase`. +- [ ] Populate the Operations manifest with real route, safe navigation command, operational inbox summary, and mandatory workspace widget. +- [ ] Run Operations, shell, registry, HK tests, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `feat(housekeeping): compose operations workspace`. +### Task 22: Finish responsive behavior, accessibility, and localization + +**Files:** + +- Modify: `src/features/housekeeping/foundation/shell/housekeeping-shell.tsx` +- Modify: `src/features/housekeeping/foundation/shell/domain-rail.tsx` +- Modify: `src/features/housekeeping/foundation/shell/context-nav.tsx` +- Modify: `src/features/housekeeping/foundation/shell/operator-summary.tsx` +- Modify: `src/features/housekeeping/foundation/shell/command-deck.tsx` +- Modify: `src/features/housekeeping/foundation/shell/operational-inbox.tsx` +- Modify: `src/features/housekeeping/foundation/shell/recent-work.tsx` +- Modify: `src/features/housekeeping/foundation/shell/favorites.tsx` +- Modify: `src/features/housekeeping/foundation/shell/widget-grid.tsx` +- Modify: `src/features/housekeeping/foundation/shell/widget-settings.tsx` +- Create: `src/features/housekeeping/foundation/shell/accessibility.test.tsx` +- Create: `src/features/housekeeping/foundation/shell/responsive-contract.test.tsx` +- Modify: `src/features/housekeeping/foundation/page/housekeeping-page-shell.tsx` +- Modify: `src/features/housekeeping/foundation/page/housekeeping-page-state.tsx` +- Modify: `src/features/housekeeping/foundation/page/housekeeping-page-state.test.tsx` +- Modify: `src/features/housekeeping/foundation/localization-contract.test.ts` +- Modify: `src/messages/ar.json` +- Modify: `src/messages/bg.json` +- Modify: `src/messages/cs.json` +- Modify: `src/messages/da.json` +- Modify: `src/messages/de.json` +- Modify: `src/messages/el.json` +- Modify: `src/messages/en.json` +- Modify: `src/messages/es.json` +- Modify: `src/messages/fi.json` +- Modify: `src/messages/fr.json` +- Modify: `src/messages/hr.json` +- Modify: `src/messages/hu.json` +- Modify: `src/messages/it.json` +- Modify: `src/messages/ja.json` +- Modify: `src/messages/nl.json` +- Modify: `src/messages/no.json` +- Modify: `src/messages/pl.json` +- Modify: `src/messages/pt.json` +- Modify: `src/messages/ro.json` +- Modify: `src/messages/ru.json` +- Modify: `src/messages/sk.json` +- Modify: `src/messages/sr.json` +- Modify: `src/messages/sv.json` +- Modify: `src/messages/tr.json` +- Modify: `src/messages/uk.json` +- Modify: `src/app/globals.css` + +**Interfaces:** + +```ts +export const HOUSEKEEPING_LANDMARKS = [ + "banner", "primary-navigation", "context-navigation", "main", +] as const; +export type HousekeepingPageState = + | "loading" | "empty" | "partial" | "error" | "forbidden" | "ready"; +``` + +- [ ] Write failing tests for one active nav item, landmark labels, heading hierarchy, skip link, focus-visible behavior, keyboard-reorder instructions, live error announcements, reduced-motion classes, and page-state semantics. +- [ ] Write responsive contracts for rail/context navigation collapse below `lg`, usable command deck at 320 px, non-overflowing tables/cards, and unchanged capabilities between mobile/desktop. +- [ ] Extend localization contract to collect every manifest/route/command/widget/state key and assert a non-empty value in all 25 locale files. +- [ ] Run accessibility/responsive/localization tests and confirm RED. +- [ ] Implement semantic shell behavior and complete every locale subtree with native-language operator copy; do not expose untranslated keys or preview wording after cutover. +- [ ] Run shell/page/localization tests, HK suite, and `pnpm typecheck`. +- [ ] Run targeted Biome, `git diff --check`, stage exact UI/locale files, and commit `feat(housekeeping): finish accessible command deck`. + +### Task 23: Close the 137-row matrix against the runtime registry + +**Files:** + +- Create: `src/features/housekeeping/cutover/parity.ts` +- Create: `src/features/housekeeping/cutover/parity.test.ts` +- Create: `src/features/housekeeping/cutover/source-boundaries.test.ts` +- Modify: `src/features/housekeeping/migration/operations.ts` +- Modify: `src/features/housekeeping/migration/people.ts` +- Modify: `src/features/housekeeping/migration/content.ts` +- Modify: `src/features/housekeeping/migration/economy.ts` +- Modify: `src/features/housekeeping/migration/hotel.ts` +- Modify: `src/features/housekeeping/migration/system.ts` +- Modify: `scripts/verify-housekeeping-matrix.ts` +- Modify: `package.json` + +**Interfaces:** + +```ts +export interface HousekeepingParityReport { + discovered: 137; + mapped: 137; + verified: 137; + removed: number; + unresolved: readonly string[]; + capabilityGaps: readonly string[]; + handlerGaps: readonly string[]; +} +export function verifyHousekeepingRuntimeParity( + matrix: readonly MigrationEntry[], + registry: HousekeepingRegistry, + handlers: readonly HousekeepingRouteHandler[], +): HousekeepingParityReport; +``` + +- [ ] Write a failing aggregate test requiring exactly 137 discovered/mapped/verified rows, zero unresolved/capability/handler gaps, every retained target under `/ase`, and every removed row with no handler. +- [ ] Write source-boundary tests forbidding foundation imports from domain internals/database/actions and forbidding one domain from another domain's internals. +- [ ] Run parity/boundary tests and confirm RED. +- [ ] Add concrete `parityEvidence` test IDs to each matrix row and change retained rows to `VERIFIED`, removed rows to `REMOVED` only after their evidence passes. +- [ ] Extend `hk:matrix:check` output with the runtime parity counts and make non-137 or any gap exit non-zero. +- [ ] Run `pnpm hk:matrix:check`, `pnpm test:housekeeping`, and `pnpm typecheck`. +- [ ] Run Biome, `git diff --check`, stage exact files, and commit `test(housekeeping): prove 137 route parity`. + +### Task 24: Pass cumulative pre-cutover verification + +**Files:** + +- Create: `docs/superpowers/evidence/2026-08-26-housekeeping-pre-cutover.md` +- Inspect: every file changed by Tasks 1-23; source failures return to their owning task and commit before this evidence-only task continues + +**Interfaces:** + +The evidence document records command, exit code, test count, date/time, environment limitations, visual viewport, route, actor capability fixture, and observed result. It never reports unavailable live services as passing. + +- [ ] Run `pnpm toolchain:check`, `pnpm hk:matrix:check`, `pnpm test:housekeeping`, `pnpm test`, and `pnpm typecheck`; record fresh output and fix only Housekeeping-caused failures with a RED/GREEN test. +- [ ] Run semantic Biome on all changed JS/TS/JSON with formatter disabled, run targeted formatter only on changed files, then run `git diff --check`. +- [ ] Run `pnpm build` with the repository's required temporary environment values; restore every environment file/value afterward and record restoration. +- [ ] Start the preview in non-production mode and verify `/ase-next` at 1440x900, 1024x768, 390x844, and 320x568 for all six domains plus loading, empty, partial, error, and forbidden states. +- [ ] Smoke permitted/denied access, safe/sensitive commands, provider timeout isolation, preference persistence/reconciliation, and all long-running Studio states. +- [ ] Record evidence, run `git diff --check`, stage only the evidence and verified fixes, and commit `test(housekeeping): record pre-cutover verification`. + +### Task 25: Perform the atomic `/ase` cutover + +**Files:** + +- Move: `src/app/ase-next` to `src/app/ase` +- Delete: `src/app/admin` +- Delete: `src/app/mod` +- Modify: `src/features/housekeeping/foundation/routing/href.ts` +- Modify: `src/features/housekeeping/foundation/preview-gate.ts` +- Modify: `src/features/housekeeping/foundation/preview-gate.test.ts` +- Modify: `src/features/housekeeping/foundation/preview-route-contract.test.ts` +- Create: `src/features/housekeeping/cutover/route-cutover.test.ts` +- Modify: `src/lib/admin/guard.ts` +- Modify: `src/lib/admin/guard.test.ts` +- Modify: `src/proxy.ts` +- Modify: `src/components/navigation.tsx` +- Modify: `src/components/top-header.tsx` +- Modify: `src/components/admin/admin-breadcrumb.tsx` +- Modify: `src/lib/admin-theme-source-audit.test.ts` +- Modify: `src/features/housekeeping/foundation/foundation-source-contract.test.ts` +- Modify: `src/env.ts` +- Modify: `.env.example` + +**Interfaces:** + +```ts +export const HOUSEKEEPING_ROOT = "/ase" as const; +// No runtime preview surface remains after cutover. +``` + +- [ ] Write the route-cutover test first: `src/app/ase/layout.tsx` and canonical dispatcher must exist; `src/app/admin`, `src/app/admin-next`, `src/app/ase-next`, and `src/app/mod` must not exist; proxy/global navigation/fallbacks must target `/ase`; no redirect maps removed UI paths. +- [ ] Run the cutover test and confirm RED before moving/removing route trees. +- [ ] Move the completed preview tree to `/ase`, remove preview-only badge/gate/flag behavior, and make all navigation/search/inbox/widget links canonical without preview projection. +- [ ] Remove legacy UI trees, rewrite global navigation and authorization fallbacks, and keep `/api/admin/*` internal endpoints unchanged because they are not UI compatibility routes. +- [ ] Remove imports/tests tied to deleted page modules; retain shared services/components only when the new domains import them without legacy route coupling. +- [ ] Run cutover, proxy/auth, source-boundary, theme, matrix, HK, full tests, and `pnpm typecheck`. +- [ ] Run Biome on changed files, `git diff --check`, stage the entire exact cutover set, and commit `feat(housekeeping): cut over administration to ase`. + +### Task 26: Run final review and release-quality verification + +**Files:** + +- Create: `docs/superpowers/evidence/2026-08-26-housekeeping-final.md` +- Inspect: every file changed on `origin/main...HEAD`; confirmed findings return to their owning task and commit before this evidence-only task continues + +**Interfaces:** + +Final acceptance requires a clean `git diff --check`, 137/137 runtime parity, production build success, no legacy UI route tree, no compatibility redirect, and recorded desktop/mobile evidence for `/ase`. + +- [ ] Review `git diff --stat origin/main...HEAD`, every commit, and the complete diff for authorization bypass, client-trusted capability, missing audit, leaked secret, unsafe external/file mutation, cross-domain import, dead legacy route, and unrelated churn. +- [ ] Invoke `superpowers:requesting-code-review`; classify each finding by evidence and fix confirmed findings in one reviewed wave using a failing regression test first. +- [ ] Re-run `pnpm toolchain:check`, `pnpm hk:matrix:check`, `pnpm test:housekeeping`, `pnpm test`, `pnpm typecheck`, semantic Biome, targeted formatting, and `git diff --check` from a clean process. +- [ ] Re-run `pnpm build` with temporary environment restoration and repeat canonical `/ase` route/access/command visual smoke at 1440x900 and 390x844. +- [ ] Verify direct requests to `/admin`, `/admin-next`, `/ase-next`, `/mod`, and removed routes are unreachable and not redirected; verify `/api/health` locally only if its dependencies are available. +- [ ] Record exact final evidence and residual pre-existing repository debt, stage only the evidence/fix wave, and commit `test(housekeeping): finalize release evidence`. +- [ ] Stop before network mutation. Present branch status, commits, checks, and evidence to the user; push and open the one final PR only after an explicit instruction. + +## Post-merge release gate + +After the final PR is explicitly authorized, pushed, reviewed, and merged, wait for the deployment pipeline to finish and verify the live `/api/health` response plus authenticated `/ase` access. A failed migration or health check blocks the release. Rollback deploys the prior application release; the additive `0023` schema remains compatible and is not destructively reversed. diff --git a/docs/superpowers/specs/2026-08-26-housekeeping-completion-design.md b/docs/superpowers/specs/2026-08-26-housekeeping-completion-design.md index f2c48525..da5b3b23 100644 --- a/docs/superpowers/specs/2026-08-26-housekeeping-completion-design.md +++ b/docs/superpowers/specs/2026-08-26-housekeeping-completion-design.md @@ -18,6 +18,10 @@ delivery details differ, this document is authoritative for phases 02 onward. The master architecture remains authoritative for domain ownership and product behavior. +This completion specification supersedes the earlier documents only for the +route namespace: the new surface uses `/ase`, never `/admin`, as its canonical +production root. + ## Approved decisions - Build complete verticals behind the existing non-production gate. @@ -26,8 +30,9 @@ behavior. - Implement in vertical slices rather than UI-first placeholders. - Keep current `/admin` and `/mod` behavior unchanged until the final cutover commit. -- At cutover, make the new Command Deck the real `/admin`, remove `/mod`, and - remove obsolete legacy routes without redirects. +- At cutover, make the new Command Deck live at `/ase`, remove the legacy + `/admin`, `/admin-next`, and `/mod` trees, and remove obsolete legacy routes + without redirects. - Use hybrid personalization: mandatory content is capability-derived; operators may pin and reorder allowed shortcuts and optional widgets. - Add only backward-compatible database migrations before cutover. @@ -55,10 +60,12 @@ All work is committed to `codex/housekeeping-complete`, based on the latest `origin/main`. No pull request is opened until every vertical and the cutover are implemented, reviewed, and verified. -The existing `/admin-next` entry remains unavailable when -`NODE_ENV=production`. Development and test environments use it to exercise the -new shell before cutover. The final cutover changes the canonical `/admin` route; -it does not weaken the production preview gate. +The foundation currently exposes `/admin-next`. The first completion change +renames that preview tree and its links to `/ase-next`; the preview remains +unavailable when `NODE_ENV=production`. Development and test environments use +`/ase-next` to exercise the new shell before cutover. The final cutover publishes +the canonical `/ase` tree and removes the preview entry; it does not weaken the +production preview gate before that point. The branch is built in this order: @@ -75,17 +82,18 @@ The branch is built in this order: After cutover the public administration route tree is: ```text -/admin Operations workspace -/admin/people/* users, tickets, CFH, bans, moderation, teams -/admin/content/* articles, events, polls, media, engagement -/admin/economy/* catalog, shop, transactions, vouchers, values -/admin/hotel/* rooms, furni, badges, radio, emulator, Studio -/admin/system/* settings, ACL, logs, DevOps, maintenance +/ase Operations workspace +/ase/people/* users, tickets, CFH, bans, moderation, teams +/ase/content/* articles, events, polls, media, engagement +/ase/economy/* catalog, shop, transactions, vouchers, values +/ase/hotel/* rooms, furni, badges, radio, emulator, Studio +/ase/system/* settings, ACL, logs, DevOps, maintenance ``` -`/admin` is the operational home, not a duplicate menu page. `/mod` has no route -after cutover. A workflow has one canonical owner and one canonical destination; -the new tree must not retain duplicate hubs or aliases. +`/ase` is the operational home, not a duplicate menu page. `/admin`, +`/admin-next`, and `/mod` have no route after cutover. A workflow has one +canonical owner and one canonical destination; the new tree must not retain +duplicate hubs or aliases. ## Module ownership @@ -331,11 +339,13 @@ legacy UI routes is an application cutover, not a destructive data migration. The final cutover commit is created only after all vertical gates pass. It: -1. moves the completed shell and Operations workspace to `/admin`; -2. changes domain preview hrefs to canonical `/admin/` hrefs; +1. moves the completed shell and Operations workspace from `/ase-next` to + `/ase`; +2. changes domain preview hrefs to canonical `/ase/` hrefs; 3. updates internal links, navigation configuration, and authorization fallback destinations; -4. removes `/mod` and every legacy route marked `REMOVE`; +4. removes the legacy `/admin`, `/admin-next`, and `/mod` route trees plus every + legacy route marked `REMOVE`; 5. removes legacy pages whose behavior moved or merged into canonical routes; 6. removes the temporary preview entry and flag if no longer used by tests; 7. adds no compatibility redirects. @@ -363,8 +373,8 @@ The final branch requires: - production build with temporary environment restoration; - visual verification at desktop and mobile widths for every domain and shared state; -- route-level smoke checks for canonical pages, denied access, and removal of - `/mod`/obsolete routes; +- route-level smoke checks for canonical `/ase` pages, denied access, and + removal of `/admin`, `/admin-next`, `/mod`, and obsolete routes; - a broad whole-branch code review followed by one reviewed fix wave if needed. Repository-wide pre-existing formatter debt is reported separately and must not @@ -389,7 +399,8 @@ before serving the cutover release. - A new task-assignment system for inbox items. - A replacement authentication or ACL model. - Rank-based authorization thresholds. -- Compatibility redirects for removed admin/mod routes. +- Compatibility redirects for removed `/admin`, `/admin-next`, or `/mod` + routes. - Destructive cleanup of legacy database data. - Rewriting specialized domain engines that already work; they are integrated behind consistent domain contracts instead. @@ -406,6 +417,6 @@ The program is complete only when: operate against real domain services; - capability enforcement and audit evidence cover every exposed read and mutation path; -- `/admin` serves the new HK, `/mod` and removed legacy routes are unreachable, - and no compatibility redirects exist; +- `/ase` serves the new HK; `/admin`, `/admin-next`, `/mod`, and removed legacy + routes are unreachable; and no compatibility redirects exist; - final local, CI, deployment, health, and visual evidence are all recorded.