docs: define complete housekeeping cutover

This commit is contained in:
Simo committed 2026-08-26 20:08:42 +02:00
1 parent d5eadeb3a7
commit d42cd2af53
1 file changed
+411
@@ -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/<domain>` 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.