Files
EpicNext-Cms/.superpowers/sdd/2026-08-26-housekeeping-completion/task-12-report.md
T
Simo a6a288d9ff
CI / check (pull_request) Successful in 42s
CI / release (pull_request) Skipped
CI / deploy (pull_request) Skipped
fix(housekeeping): restore people partial compatibility
2026-08-29 21:03:43 +02:00

22 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.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

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 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

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 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

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.

Official review fix round 3

The round-2 re-review reported 0 Critical, 2 Important, and 2 adjacent Minor findings. This round addresses all four findings without changing the nine-route manifest, the public command IDs, or legacy external call ordering and permissions.

RED evidence

Focused external-audit, bulk-production, and dispatcher matrix
Test Files 2 failed (2)
Tests 15 failed | 54 passed (69)

The ten external-only false/throw cases persisted optimistic desired after-state instead of confirmed unchanged or unknown delivery evidence. Four bulk currency/badge false/throw cases reported completed=0 and Database error after a committed database write. The dispatcher accepted one ok:false result carrying impossible completion metadata.

GREEN implementation

  • External-only alert, disconnect, mute, unmute, and send-currency keep desired state in intent/success evidence. Confirmed RCON false now writes a dedicated unchanged/no-delivery failure snapshot; an exception writes unknown delivery with a null after-state. Correlation and failure outcome stay identical across intent/outcome records.
  • Bulk currency and badge count a successful database write before RCON. RCON false/throw is additive externalSyncFailures sync debt, never a database failure; failedIds remains reserved for database/business failures and the result is typed partial with an explicit no-automatic-retry warning.
  • Webhook notification remains explicit fire-and-forget best effort (void notify(...)) and no longer participates in mutation completion. The impossible promise-rejection test was replaced with the real void contract.
  • The dispatcher runtime schema now accepts completion only for ok: true, matching the TypeScript HousekeepingResult contract; failure envelopes containing it are rejected as malformed.

Final verification after review fix round 3

Focused external audit + production bulk + dispatcher
Test Files 3 passed (3)
Tests 73 passed (73)

People + foundation + legacy wrappers + staff-smoke matrix
Test Files 48 passed (48)
Tests 470 passed (470)

pnpm test:housekeeping
Test Files 58 passed (58)
Tests 516 passed (516)

pnpm test
Test Files 206 passed | 3 skipped (209)
Tests 1359 passed | 5 skipped (1364)

pnpm typecheck
tsc --noEmit
Exit 0

pnpm exec biome check --formatter-enabled=false <4 exact changed source/test files>
Checked 4 files. No fixes applied.

git diff --check
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 4

The round-3 re-review reported 0 Critical, 2 Important, and 0 Minor findings. This round restores the pre-cutover legacy bulk result contract at the wrapper boundary and makes committed-database/emulator-sync debt explicit in the successful partial operator result. Canonical Housekeeping accounting, audit evidence, routes, commands, and ACLs remain unchanged.

Pre-Task12 parity evidence

git show e1b31ff7^:src/actions/bulk-users.ts confirms that currency and badge wrappers awaited RCON inside the same try: an RCON exception entered the catch, did not increment given, and appended { userId, reason: "Database error" }; an RCON false return did not throw and therefore remained a legacy success. Positive bulk adjustment delegated to the same currency wrapper and had the same result semantics.

RED evidence

pnpm exec vitest run --coverage.enabled=false src/actions/bulk-users.test.ts src/actions/bulk-adjust-wrapper.test.ts
Test Files 2 failed (2)
Tests 3 failed | 3 passed (6)
Currency, badge, and positive-adjust wrappers returned given/adjusted=1 with no failedIds for the canonical external-sync debt produced by a thrown RCON call; historical results require 0 plus Database error.

pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people/pages/people-primary-pages.test.tsx
Test Files 1 failed (1)
Tests 2 failed | 12 passed (14)
The operator result rendered only Partially completed and exposed neither an alert/do-not-retry instruction nor the typed user sync debt returned by the real form adapter.

GREEN implementation

  • src/actions/bulk-users.ts translates only externalSyncFailures at the legacy wrapper boundary into historical Database error failures and subtracts those entries from given/positive adjusted. Canonical completed counts and sync-debt evidence are untouched; the existing legacy false path still produces no external-sync entry and remains successful. Failure entries are restored in input order, including duplicate IDs.
  • src/features/housekeeping/domains/people/pages/people-command-form.tsx reads only a successful typed partial result with failed external completion and a bounded after.externalSyncFailures array. It renders an alert, explicit do-not-retry instruction, and safe user/reason debt entries. Unknown payload fields and malformed entries are never rendered, and the generated reset password path remains one-time and unchanged.

Focused GREEN:

pnpm exec vitest run --coverage.enabled=false src/actions/bulk-users.test.ts src/actions/bulk-adjust-wrapper.test.ts
Test Files 2 passed (2)
Tests 6 passed (6)

pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people/pages/people-primary-pages.test.tsx
Test Files 1 passed (1)
Tests 14 passed (14)

pnpm exec vitest run --coverage.enabled=false src/actions/bulk-users.test.ts src/actions/bulk-adjust-wrapper.test.ts src/features/housekeeping/domains/people/pages/people-primary-pages.test.tsx src/features/housekeeping/domains/people/services/mutations-production-workflows.test.ts
Test Files 4 passed (4)
Tests 48 passed (48)

Cumulative verification

People + foundation + Task12 legacy wrappers + route/audit/staff-smoke matrix
Test Files 52 passed (52)
Tests 491 passed (491)

pnpm test:housekeeping
Test Files 58 passed (58)
Tests 518 passed (518)

pnpm test
Test Files 206 passed | 3 skipped (209)
Tests 1364 passed | 5 skipped (1369)

pnpm typecheck
tsc --noEmit
Exit 0

pnpm exec biome check --formatter-enabled=false src/actions/bulk-users.ts src/actions/bulk-users.test.ts src/actions/bulk-adjust-wrapper.test.ts src/features/housekeeping/domains/people/pages/people-command-form.tsx src/features/housekeeping/domains/people/pages/people-primary-pages.test.tsx
Checked 5 files. No fixes applied.

git diff --check
Exit 0

The only warning is the approved Node engine mismatch: the repository requests Node >=26.8.1 <27, while the host runs Node v26.7.0 with pnpm 11.24.0.

Exact tracked paths

  • .superpowers/sdd/2026-08-26-housekeeping-completion/task-12-report.md
  • src/actions/bulk-adjust-wrapper.test.ts
  • src/actions/bulk-users.test.ts
  • src/actions/bulk-users.ts
  • src/features/housekeeping/domains/people/pages/people-command-form.tsx
  • src/features/housekeeping/domains/people/pages/people-primary-pages.test.tsx

The required controller lines were appended to the git-ignored .superpowers/sdd/2026-08-26-housekeeping-completion/progress.md; it is excluded from the commit. .remember/ remains untouched.

Self-review

  • Scope and compatibility: the production mutation service, canonical audit/accounting, manifest, route, command, ACL, database, redirect, and cutover behavior are unchanged. The adapter applies only to legacy currency/badge results and their historical positive-adjust delegate.
  • Security: the operator surface requires an ok: true partial/external-failed envelope, accepts at most 100 positive safe-integer user IDs, renders only the canonical safe reason, and does not inspect or serialize arbitrary result payloads. Reset-password display and audit redaction tests remain green.
  • Test quality: legacy tests exercise the real exported wrappers against a complete canonical partial response and fail on either wrong count or missing historical failure; UI tests render the real component, invoke the real form adapter, and prove malformed/extra payload is not displayed.

Commit

Single local commit message: fix(housekeeping): restore people partial compatibility. The final SHA of the commit containing this report is returned to the controller after creation.

No database operation, deployment, push, pull, or pull-request update was performed.