From d42cd2af53990dc4c4a4e298c60cfa6467031253 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Wed, 26 Aug 2026 20:08:42 +0200 Subject: [PATCH] docs: define complete housekeeping cutover --- ...26-08-26-housekeeping-completion-design.md | 411 ++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-26-housekeeping-completion-design.md diff --git a/docs/superpowers/specs/2026-08-26-housekeeping-completion-design.md b/docs/superpowers/specs/2026-08-26-housekeeping-completion-design.md new file mode 100644 index 0000000000..f2c4852557 --- /dev/null +++ b/docs/superpowers/specs/2026-08-26-housekeeping-completion-design.md @@ -0,0 +1,411 @@ +# Housekeeping Completion and Atomic Cutover Design + +**Status:** Approved in conversation on 2026-08-26 +**Delivery branch:** `codex/housekeeping-complete` +**Delivery shape:** one final pull request +**Cutover:** atomic, with no compatibility redirects + +## Relationship to the existing design + +This specification completes the program described by +`2026-08-24-housekeeping-modernization-design.md` after the merged Inventory & +Foundation subproject. The existing foundation is not the finished product: it +provides the 137-route migration matrix, capability-aware contracts, validated +domain manifests, shell primitives, and a non-production preview. + +This document defines the remaining implementation and the final cutover. Where +delivery details differ, this document is authoritative for phases 02 onward. +The master architecture remains authoritative for domain ownership and product +behavior. + +## Approved decisions + +- Build complete verticals behind the existing non-production gate. +- Keep all remaining work on one branch and deliver it through one final pull + request. +- 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. +- 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. + +## Outcomes + +The completed program must: + +1. Give every one of the 137 legacy routes a verified canonical destination or + an explicit removal decision. +2. Replace the fragmented admin and moderator surfaces with one capability-aware + Command Deck. +3. Deliver real workflows for all retained administration responsibilities, not + wrappers around legacy pages. +4. Provide global search, safe commands, derived operational inboxes, recent + work, favorites, and optional widgets. +5. Enforce the existing ACL model on navigation, reads, mutations, commands, + search results, inbox items, and widgets. +6. Produce durable and sanitized audit evidence for sensitive operations. +7. Preserve a release-level rollback path without destructive database rollback. + +## Delivery model + +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 branch is built in this order: + +1. access, audit, error, and preference core; +2. People, moderation, and support; +3. Content and engagement; +4. Economy and catalog; +5. Hotel, world, and operational systems; +6. Command Deck operations and cross-domain composition; +7. atomic route cutover and legacy removal. + +## Canonical route structure + +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 +``` + +`/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. + +## Module ownership + +`src/features/housekeeping/foundation` owns only cross-cutting composition: + +- request-scoped actor and capability context; +- registry and navigation projection; +- Command Deck chrome and page-state primitives; +- command dispatch contracts; +- search and inbox orchestration; +- preference reconciliation; +- shared error and audit envelopes. + +Each domain owns its routes, pages, query services, commands, search providers, +inbox sources, widgets, and domain-specific validation. Domains communicate with +the foundation through the published contracts. They do not import another +domain's internal modules. + +The foundation must not import database clients, server actions, or domain page +modules. Server-only domain adapters may import data and action services. + +## Domain manifests + +Every manifest registers real, non-placeholder definitions for: + +- canonical routes and contextual navigation; +- safe and sensitive commands; +- entity-search providers; +- derived-inbox sources; +- mandatory and optional widgets; +- localization keys and capability requirements. + +Registry validation rejects duplicate IDs across all provider categories, +duplicate routes, invalid ownership, missing localization, unknown capability +slugs, invalid widget kinds, and commands without an owning domain. + +The migration matrix and manifests are linked by contract tests. Every retained +matrix row must resolve to one registered route or workflow. Every manifest +capability set must cover the capabilities attributed to its matrix rows. + +## Authorization flow + +Each request creates one capability context from `getAdminContext()`. The context +contains the authenticated actor and immutable effective permission slugs. +Rank is informational and may influence presentation defaults only; it is never +used as a new authorization threshold. + +Authorization is applied at every layer: + +1. registry projection removes inaccessible domains and routes; +2. provider orchestration calls only permitted providers; +3. providers filter inaccessible results and items; +4. page loaders revalidate their required capability; +5. command execution revalidates capability and input on the server; +6. the underlying mutation service retains its own permission guard. + +Client state, hidden navigation, preferences, or a previously loaded page never +authorize an operation. + +## Commands and audit + +Commands use typed input schemas and typed success/error results. Safe commands +may execute directly from the palette. Sensitive commands open a dedicated +contextual confirmation flow and require a reason when the command contract says +so. + +The existing `admin_audit_log` remains the canonical audit store. An additive +migration adds nullable `correlation_id varchar(64)`, `outcome varchar(32)`, +`reason text`, and `domain varchar(32)` columns plus an index on +`correlation_id`. Existing `action`, `target`, `target_id`, `before`, `after`, +`diff`, `ip_address`, and actor fields remain in use. + +- Database mutations write mutation and audit evidence in the same transaction + whenever the affected service uses the same database connection. +- Sensitive external or file operations persist an audit intent before + execution and a final outcome afterward. Failure to persist the intent blocks + execution. +- Audit payloads pass through the existing recursive secret redaction. +- Every command result and audit record carries the same correlation ID. +- Failed, denied, and partially completed sensitive operations are audited. + +## Preferences + +No suitable user-scoped HK preference store currently exists. Add +`housekeeping_user_preferences` with: + +- `user_id int` as the primary key and unique owner; +- `schema_version int not null default 1`; +- `payload longtext not null`, containing validated JSON presentation state; +- `created_at datetime` and `updated_at datetime` timestamps. + +The payload stores pinned route/command IDs, shortcut order, widget order, and +enabled optional widget IDs. It never stores permissions, authorization +decisions, workflow state, or inbox status. + +Every read reconciles stored IDs against the current registry and effective +capabilities. Unknown, removed, or unauthorized entries are dropped before the +payload reaches the UI. Mandatory widgets cannot be disabled. + +## Command Deck experience + +The shell has four stable regions: + +1. a compact six-domain rail; +2. domain-owned contextual navigation; +3. a global search and command field with keyboard access; +4. an operational workspace for pages, inboxes, recent work, and widgets. + +Desktop and mobile share the same semantic hierarchy. Mobile collapses the rail +and contextual navigation without changing route ownership or available +actions. Focus order, landmarks, headings, active-state uniqueness, keyboard +navigation, reduced motion, and semantic theme tokens are tested contracts. + +Loading, empty, partial, error, forbidden, and ready states use the shared page +state primitives. Partial provider failure is visible without replacing valid +results from other providers. + +## Search + +Search supports navigation, entity results, and commands. It is not a raw +database search endpoint. + +- A term shorter than two trimmed characters performs navigation/command + matching only. +- Entity providers have a two-second timeout and a maximum of 25 results each. +- The combined entity response is capped at 50 results before client rendering. +- Providers run only when their declared capability is satisfied. +- Results include stable ID, owner, type, title, optional description, canonical + href, and capability metadata. +- Provider errors produce a typed partial result and do not fail unrelated + providers. +- Search terms and result payloads are not written to audit logs by default. + +## Derived operational inbox + +The inbox is a read model over domain-owned work: tickets, CFH reports, alerts, +emulator errors, operational anomalies, and other existing live states. It does +not introduce a second assignment or task-status system. + +Each inbox item exposes stable source/item IDs, domain, type, title, priority, +age, state, canonical href, available actions, and required capability. Source +items are deduplicated by the pair `(sourceId, itemId)`. + +Sources run independently with a two-second timeout. The composed response +contains successful items plus per-source errors. The server caps the result at +200 items after capability filtering and deterministic priority/age ordering. + +## Recent work, favorites, and widgets + +Recent work is derived from the operator's existing audit events and canonical +route visits; it does not create workflow state. Favorites and ordering come +from the reconciled preference payload. + +Mandatory widgets are supplied by the system according to capability and cannot +be removed. Optional widgets can be enabled and reordered. Widget loaders are +server-side, capability-checked, independently timed out, and represented as +partial failures rather than shell failures. + +## Vertical scope + +### Access, audit, and system core + +- command dispatcher and confirmation model; +- audit extension and correlation IDs; +- typed error taxonomy and boundary mapping; +- preference repository and reconciliation; +- shared provider orchestration and timeout behavior; +- System routes for ACL, settings, logs, DevOps, and maintenance. + +### People, moderation, and support + +- user discovery, details, editing, password/reset controls, account relations, + bans, and permitted staff actions; +- help tickets and moderator tickets; +- CFH queues and details; +- moderation actions, team views, and ban workflows; +- People search providers, inbox sources, commands, and widgets. + +This vertical proves that all retained `/mod` responsibilities work inside the +new capability model before `/mod` is removed. + +### Content and engagement + +- articles, events, polls, media, navigation content, tags, banners, and related + editorial tools; +- Content search, commands, inbox sources, and widgets; +- consolidation of duplicate editorial hubs into canonical workflows. + +### Economy and catalog + +- catalog and item management, Builder Club catalog, maintenance, shop, + transactions, vouchers, subscriptions, marketplace, and value tools; +- Economy search, commands, anomaly sources, and widgets; +- existing specialized editors remain components of canonical workflows rather + than parallel navigation roots. + +### Hotel, world, and operational systems + +- rooms and room furni, badges, sounds, radio, emulator controls, imports, and + Studio tools; +- Hotel search, commands, operational sources, and widgets; +- long-running operations retain progress/error behavior and gain consistent + capability and audit envelopes. + +### Operations composition + +- global search and command palette; +- derived inbox and partial-source reporting; +- recent work and favorites; +- mandatory operational summaries and optional widgets; +- no duplicate mutation logic: actions route to the owning domain command. + +## Error model + +All HK services return typed errors from this stable set: + +- `UNAUTHENTICATED`; +- `FORBIDDEN`; +- `VALIDATION`; +- `NOT_FOUND`; +- `CONFLICT`; +- `RATE_LIMITED`; +- `DEPENDENCY_UNAVAILABLE`; +- `TIMEOUT`; +- `INTERNAL`. + +User messages are localized and do not expose internal details. Server logs and +audit evidence include correlation IDs. Expected domain errors do not rely on +framework exception text. Unknown errors are sanitized at the boundary and +logged once. + +## Database changes + +Allowed pre-cutover migrations are additive only: + +1. nullable HK audit metadata columns on `admin_audit_log`; +2. the `housekeeping_user_preferences` table and its unique user index. + +No legacy table or column is dropped or repurposed in this program. Removal of +legacy UI routes is an application cutover, not a destructive data migration. + +## Atomic cutover + +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; +3. updates internal links, navigation configuration, and authorization fallback + destinations; +4. removes `/mod` and 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. + +The cutover must leave no links, imports, route discovery entries, or tests that +depend on removed UI modules. + +## Verification strategy + +Each vertical uses TDD and has four gates: + +1. contract and authorization tests; +2. domain query/command behavior tests, including denied and failure paths; +3. page and accessibility behavior tests; +4. cumulative Housekeeping and repository verification. + +The final branch requires: + +- the migration matrix reporting 137/137 valid with every retained row linked to + a canonical implementation; +- mutation-sensitive authorization, provider, command, audit, and preference + tests; +- full project tests, Housekeeping tests, typecheck, semantic Biome, targeted + formatting checks, and `git diff --check`; +- 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; +- 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 +be hidden by mass-formatting unrelated files. + +## Merge, deployment, and rollback + +The single pull request targets `main` only after all final gates pass. Merging +is the atomic release boundary; no partial vertical is intentionally exposed to +production operators. + +After merge, the deployment pipeline must complete and `/api/health` must be +verified live. A push or successful build alone is not deployment evidence. + +Rollback deploys the prior application release. Because database changes are +additive and ignored by the prior release, rollback does not require manual data +reversal. If audit or preference migrations themselves fail, deployment stops +before serving the cutover release. + +## Explicit non-goals + +- 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. +- Destructive cleanup of legacy database data. +- Rewriting specialized domain engines that already work; they are integrated + behind consistent domain contracts instead. +- Unrelated CMS redesign or repository-wide formatting cleanup. + +## Completion criteria + +The program is complete only when: + +- all retained legacy capabilities are available through canonical new routes; +- all six manifests contain real routes/providers/widgets rather than empty + placeholders; +- the Command Deck search, commands, inbox, preferences, recent work, and widgets + 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; +- final local, CI, deployment, health, and visual evidence are all recorded.