# 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.ts` but 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`, and `people.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, `/admin` revalidation, VPN redirect/fail-soft behavior, word-filter `ActionResult` shapes, 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 uses `USERS_RESET_PASSWORD`, bulk/user/community/team/application operations use `USERS_EDIT`, IP/VPN use `SETTINGS_EDIT`, and word filter uses `WORDFILTER_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 `/admin` behavior until Task 25. No `/admin`, `/mod`, API, redirect, database schema, deployment, or cutover behavior was changed. ## Strict TDD evidence ### Initial command/page/route RED ```text 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: ```text 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: ```text 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: ```text Focused foundation contracts: 3 files passed, 40 tests passed People + foundation: 34 files passed, 380 tests passed ``` ### Audited sanction reason RED: ```text 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: ```text Production mutation contracts: 2 files passed, 4 tests passed The audited snapshot includes the reason and excludes mail. ``` ### Legacy wrapper failure parity RED: ```text 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: ```text Test Files 1 passed (1) Tests 3 passed (3) ``` ### Single authorization check for positive bulk adjustment RED: ```text 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: ```text Test Files 1 passed (1) Tests 1 passed (1) ``` ### Static gates during implementation ```text pnpm typecheck RED: one unused `describe` import in admin-ip.test.ts GREEN: tsc --noEmit, exit 0 pnpm exec biome check --formatter-enabled=false RED: 14 import-order assists GREEN: checked 44 files, no fixes applied ``` ## Final verification ```text 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 ```text 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 `StaffActivities` side 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 `ListInput` model. No wildcard or prefix relaxation was introduced. ### Final verification after review fixes ```text 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 ```text 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 `partial` completion 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 `UsersSettings` row 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/`BigInt` through wrappers and Drizzle, including values above `Number.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 ```text 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.