docs: define complete housekeeping cutover
This commit is contained in:
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.
|
||||||
Reference in new issue
Block a user