423 lines
17 KiB
Markdown
423 lines
17 KiB
Markdown
# 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.
|
|
|
|
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.
|
|
- 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 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.
|
|
|
|
## 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 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:
|
|
|
|
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
|
|
/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
|
|
```
|
|
|
|
`/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
|
|
|
|
`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 from `/ase-next` to
|
|
`/ase`;
|
|
2. changes domain preview hrefs to canonical `/ase/<domain>` hrefs;
|
|
3. updates internal links, navigation configuration, and authorization fallback
|
|
destinations;
|
|
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.
|
|
|
|
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 `/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
|
|
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`, `/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.
|
|
- 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;
|
|
- `/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.
|