14 KiB
Task 12 — People users, community, and staff workflows
Status: DONE
Delivered scope
- Registered exactly the nine approved real People routes: users list/edit/multi-account/detail, community online/guilds/guild detail, and staff applications/teams. The remaining support and moderation routes stay catalogued in
routes.tsbut unregistered for Task 13. - Added query-backed People pages with explicit loading, empty, partial, dependency-error, forbidden, and ready states. Links are canonical
/ase/people/*links; optional mail/IP fields and mutation affordances remain absent unless their exact capability is present. - Added the exact fourteen user command IDs plus the six stable People-owned IDs
people.guild.disband,people.application.decide,people.team.change,people.ip.action,people.vpn.configure, andpeople.word-filter.update. - Added bounded Zod command schemas, stable rate limits, dispatcher capability rechecks, confirmation metadata, a redirect-free server-only mutation service, and deterministic bootstrap registration.
- Extracted shared mutation behavior behind the existing actions while preserving the legacy action exports, exact ACLs,
/adminrevalidation, VPN redirect/fail-soft behavior, word-filterActionResultshapes, already-gone delete semantics, and failure propagation where legacy persistence errors previously propagated. - Added neutral EN/IT labels only for the nine runtime routes.
Security and behavior decisions
- No ACL slug or route authorization rank threshold was added. Exact legacy capabilities remain authoritative: single ban/unban use
USERS_BAN, reset-password usesUSERS_RESET_PASSWORD, bulk/user/community/team/application operations useUSERS_EDIT, IP/VPN useSETTINGS_EDIT, and word filter usesWORDFILTER_EDIT. - The existing target hierarchy safeguard remains for legacy user mutations that previously used
guardRank; alert remains capability-authorized without a new target-rank rule. - Ordinary user edit and alert remain reason-free because their existing semantics are non-destructive. Sanctions, destructive operations, global/security changes, currency delivery, and bulk mutations require a nonblank dispatcher reason. Ban and bulk-ban operational reasons are also persisted with the mutation audit evidence.
- Every public service call rechecks the exact capability before production work. The production adapter is module-private; server-only placement is not treated as authorization.
- Successful mutations emit before/after audit evidence and a stable correlation ID. Audit persistence failure is fail-closed and maps to
DEPENDENCY_UNAVAILABLE. User mutation audit snapshots omit mail because it is unnecessary PII; page projection continues to follow Task 11 exactly. - User pages consume only Task 11 guarded read models, preserving bounded pagination, deterministic sorting, fail-closed DTO validation, serialization, zero-sentinel rules, watched state, permission context, and PII projection.
- Legacy wrappers remain on
/adminbehavior until Task 25. No/admin,/mod, API, redirect, database schema, deployment, or cutover behavior was changed.
Strict TDD evidence
Initial command/page/route RED
pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people/commands/user-commands.test.ts src/features/housekeeping/domains/people/commands/community-commands.test.ts src/features/housekeeping/domains/people/pages/people-primary-pages.test.tsx
Test Files 3 failed (3)
Tests 0
Missing modules: community-commands, ../services/mutations, ../route-handlers
Initial focused GREEN:
Command tests: 2 files passed, 22 tests passed
Primary page/route tests: 3 files passed, 12 tests passed
Bootstrap tests: 1 file passed, 2 tests passed
Foundation integration RED/GREEN
RED after enabling People:
pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people src/features/housekeeping/foundation
Test Files 3 failed | 31 passed
Tests 4 failed | 376 passed
Failures: stale System-only registry assertions, stale preview expectation, and an unapproved People vertical runtime edge.
GREEN after updating the explicit runtime-edge and registry contracts:
Focused foundation contracts: 3 files passed, 40 tests passed
People + foundation: 34 files passed, 380 tests passed
Audited sanction reason
RED:
pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people/services/mutations-reason-production.test.ts
Test Files 1 failed (1)
Tests 1 failed (1)
The ban audit after-snapshot did not contain the nonblank sanction reason.
GREEN:
Production mutation contracts: 2 files passed, 4 tests passed
The audited snapshot includes the reason and excludes mail.
Legacy wrapper failure parity
RED:
pnpm exec vitest run --coverage.enabled=false src/actions/people-wrapper-errors.test.ts
Test Files 1 failed (1)
Tests 3 failed (3)
Persistence failures were swallowed and already-gone word-filter deletion was not idempotent.
GREEN:
Test Files 1 passed (1)
Tests 3 passed (3)
Single authorization check for positive bulk adjustment
RED:
pnpm exec vitest run --coverage.enabled=false src/actions/bulk-adjust-wrapper.test.ts
Test Files 1 failed (1)
Tests 1 failed (1)
Expected one requirePermission call; received two.
GREEN:
Test Files 1 passed (1)
Tests 1 passed (1)
Static gates during implementation
pnpm typecheck
RED: one unused `describe` import in admin-ip.test.ts
GREEN: tsc --noEmit, exit 0
pnpm exec biome check --formatter-enabled=false <exact Task 12 src files>
RED: 14 import-order assists
GREEN: checked 44 files, no fixes applied
Final verification
Focused wrapper/command/page/service/route/authorization/audit matrix
Test Files 22 passed (22)
Tests 129 passed (129)
pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people src/features/housekeeping/foundation
Test Files 35 passed (35)
Tests 381 passed (381)
pnpm test:housekeeping
Test Files 54 passed (54)
Tests 469 passed (469)
pnpm typecheck
tsc --noEmit
Exit 0
pnpm exec biome check --formatter-enabled=false <44 exact changed Task 12 src files>
Checked 44 files. No fixes applied.
git diff --check
Exit 0
The Node engine warning remains the approved non-blocker: the repository requests Node >=26.8.1 <27, while this host runs Node v26.7.0 with pnpm 11.24.0. All test and type gates exited successfully.
No database operation, deployment, push, or pull-request update was performed.
Official review fix round 1
The official review reported 0 Critical and 5 Important findings. This round addresses the five findings without widening the Task 12 route catalog or changing legacy redirects, safe-action response shapes, or cutover behavior.
RED evidence
Production server authority: auth resolver was called 0 times at the public service boundary (1 failing regression).
Canonical external audit: 3 failing regressions for missing durable intent/outcome behavior.
Transactional audit: expected one transaction and observed zero (1 failing regression).
Legacy/production workflows: new production matrix initially exposed bulk truncation/deduplication, trade-lock hierarchy/state, missing StaffActivities, and missing word-filter refresh behavior.
Primary workflows: page suite started at 7 passed / 2 failed (no executable command form and no bounded URL parser); preview contract started at 61 passed / 3 failed (searchParams/loading propagation).
Import boundary after real forms: test:housekeeping reached 481 passed / 1 failed, then the focused boundary exposed one exact page-state -> People models edge (22 passed / 1 failed).
GREEN implementation
- Production People services now rehydrate
getHousekeepingCapabilityContext()on every public mutation. Invocation data can carry correlation and an expected actor only; it cannot synthesize permissions. The production adapter stays private, while test factories inject an authority resolver. - Pure database mutations write their canonical before/after audit evidence in the same transaction. Mixed database/RCON/cache work writes sanitized intent first and a correlated success, failure, or partial outcome afterward. Audit-outcome persistence errors retain truthful completed/partial state, and legacy wrappers preserve their observable behavior.
- Ban/unban use observed active-ban state; trade-lock uses observed sanction/settings state. Passwords, hashes, API keys, and secrets are excluded from canonical evidence.
- Shared legacy bulk paths preserve original order, duplicates, totals, and iteration with no service-side 100-item cap. The <=100 bound remains in command schemas. Trade lock has no invented hierarchy gate and its missing-user wrapper message remains exactly
User not found. - Original
StaffActivitiesside effects are retained for bulk ban/unban/currency/badge, guild disband, VPN, and trade lock. Missing word-filter deletion still reloads local cache, sends RCON refresh, and returns legacy success. - The nine registered pages now expose capability-gated, accessible command forms backed by
executeHousekeepingCommand; no inert command spans remain. Edit submits a mutation, list inputs come from bounded URL search parameters, and the dynamic preview route passes them through. Atomic Task 11 queries keep their fail-closed contracts; the impossible synthetic partial state was removed. A real Next loading route was added. - The foundation contract allows only the exact same-domain edges required here: each People page to the shared People command form, the form to the single housekeeping command action, and page-state to the People
ListInputmodel. No wildcard or prefix relaxation was introduced.
Final verification after review fixes
Focused wrapper/command/page/service/route/auth/audit/staff-smoke matrix
Test Files 24 passed (24)
Tests 187 passed (187)
pnpm test:housekeeping
Test Files 58 passed (58)
Tests 482 passed (482)
pnpm test
Test Files 206 passed | 3 skipped (209)
Tests 1321 passed | 5 skipped (1326)
pnpm typecheck
tsc --noEmit
Exit 0
pnpm exec biome check --formatter-enabled=false <40 exact changed Task 12 source files>
Checked 40 files. No fixes applied.
git diff --check e1b31ff7738eb5cc59e7765c8ed7290d62130972 --
Exit 0
The approved Node engine warning remains: the repository requests Node >=26.8.1 <27, while the host runs Node v26.7.0 with pnpm 11.24.0. No database operation, deployment, push, or pull-request update was performed.
Official review fix round 2
The round-1 re-review reported 0 Critical, 7 Important, and no Minor findings. This round addresses all seven findings without changing the nine-route Task 12 manifest or exposing any raw production adapter.
RED evidence
Typed partial/result contract: 5 failed / 8 passed before completion metadata and audit-outcome handling were added.
Observed active-ban and absent-settings snapshots: 2 focused failures before deterministic active reads and null-preserving trade snapshots.
BIGINT preservation: 8 focused failures across application, team, IP, and wordfilter before decimal string/BigInt boundaries.
Real form/reset workflow: 2 primary-page failures before the actual action adapter and one-time credential result were added.
Production operation closure: 2 failed / 10 passed before unban observed-after and bulk false-RCON partial truth.
Post-commit notification/legacy parity: 2 failed / 12 passed before update/reset transactional intent and legacy throw/false mapping.
Cumulative gate exposed one unsupported custom Zod schema, one stale direct-alert expectation, and one stale numeric audit-ID expectation; each received a minimal regression-preserving fix.
GREEN implementation
- Mixed database/external operations now commit sanitized intent with the mutation and return correlated typed
partialcompletion when RCON, cache, notification, or final audit persistence fails afterward. Pre-mutation external failure and intent persistence failure remain blocking. The dispatcher and public server action preserve one serializable partial result and emit no contradictory generic failure evidence. - Ban and unban reuse the permanent-or-unexpired Task 11 filter, deterministic timestamp/ID ordering, and observed before/after reads. An absent
UsersSettingsrow remains null before and after a no-op trade settings update. - Legacy alert calls RCON without a target query or hierarchy guard. Legacy wrappers retain their prior false/throw behavior while new Housekeeping commands report synchronization false as partial truth.
- Application, team, IP, and wordfilter identifiers remain canonical decimal strings/
BigIntthrough wrappers and Drizzle, including values aboveNumber.MAX_SAFE_INTEGER. The cloneable command regex accepts the full unsigned BIGINT range and rejects overflow without a Zod custom refinement. - Reset-password returns the generated credential once in the current authorized form result. It is rendered through an accessible
output, excluded from durable service evidence, and recursively redacted by the canonical audit sanitizer. - Production tests execute all twenty Task 12 operation IDs with meaningful database/RCON/audit assertions. The primary-page test invokes the actual form action adapter, and the unused multi-accounts-to-command-form boundary exception was removed.
Final verification after review fix round 2
Focused People + foundation + wrappers + audit + action + staff-smoke matrix
Test Files 45 passed (45)
Tests 459 passed (459)
pnpm test:housekeeping
Test Files 58 passed (58)
Tests 502 passed (502)
pnpm test
Test Files 206 passed | 3 skipped (209)
Tests 1345 passed | 5 skipped (1350)
pnpm typecheck
tsc --noEmit
Exit 0
pnpm exec biome check --formatter-enabled=false <28 exact changed TypeScript/TSX files>
Checked 28 files. No fixes applied.
The approved Node engine warning remains: the repository requests Node >=26.8.1 <27, while the host runs Node v26.7.0 with pnpm 11.24.0. No database operation, deployment, push, or pull-request update was performed.