fix(housekeeping): complete people read fidelity
CI / check (pull_request) Successful in 31s
CI / deploy (pull_request) Skipped
CI / release (pull_request) Skipped

This commit is contained in:
Simo committed 2026-08-29 10:34:44 +02:00
1 parent d1382c839e
commit 7920d4f46c
8 files changed
+564 -37

No files matched your search

@@ -1,6 +1,6 @@
# Task 11 — People workflow read models
Status: DONE — fix round 1
Status: DONE — fix round 2
## Delivered scope
@@ -15,12 +15,12 @@ Status: DONE — fix round 1
- User mail and current IP remain independently nullable fields. Each is projected only when the capability context contains the existing `PERMS.USERS_VIEW`; `PERMS.MOD_USERS_VIEW` alone receives the safe base projection with both values set to `null`, and a context with neither permission is forbidden.
- No new ACL slug or rank threshold was introduced. Staff filtering reuses the existing `getMinStaffRank()` source.
- The production user selection is explicit and excludes passwords, authentication tickets, secrets, and two-factor material. VPN settings intentionally exclude `vpn_api_key`.
- Adapters fail closed. Malformed driver envelopes, invalid or non-positive identifiers, corrupt links, non-serializable DTO values, and count failures map to `DEPENDENCY_UNAVAILABLE`. Missing valid detail entities map to `NOT_FOUND`; invalid request identifiers map to `VALIDATION`. No partial-result shape is returned because no People DTO explicitly names failed sources.
- Adapters fail closed. Malformed driver envelopes, invalid identifiers, corrupt links, non-serializable DTO values, and count failures map to `DEPENDENCY_UNAVAILABLE`. Primary/entity identifiers remain positive safe integers; zero is accepted only for the schema-declared guild `userId`/`roomId` and CFH `senderId`/`reportedId`/`roomId`/`moderatorId` sentinels. Missing valid detail entities map to `NOT_FOUND`; invalid request identifiers map to `VALIDATION`.
- Pagination clamps page size to 100 and offset to 10,000. Deterministic primary sorting, numeric-ID tie breaking, and `LIMIT`/`OFFSET` now execute in the database; no list query fetches a prefix for locale re-sorting or second slicing.
- Raw production adapters and `buildPeopleUserSelection` are module-private. Runtime exports expose only context-authorized query factories and singleton query surfaces.
- Multi-account clusters use one bounded CTE/window query, cap accounts per cluster at 100, and never issue one query per IP cluster.
- Multi-account clusters use one bounded CTE/window page query plus one independent matching-cluster count query, cap accounts per cluster at 100, and never issue one query per IP cluster.
- User detail/edit now includes the operator's watched state and canonical permission-rank data. Support ticket reads use the existing unified inbox through a strict, fail-closed, database-paged mode that includes CMS and help-center rows while leaving the legacy tolerant mode unchanged.
- Support desk/detail DTOs explicitly include queue counts, bounded staff, and the relevant active ban. Ticket messages/replies and staff rows are bounded.
- The unified `/support/tickets` inbox still merges CMS and help-center rows. The ticket desk now has its own strict CMS-only page loader preserving priority, category, assignee, and message count; help summaries include reply count. Support desk/detail DTOs explicitly include queue counts, bounded staff, and the relevant active ban.
- Active bans are filtered before sorting. Expiry `0` remains the permanent-active sentinel; expired rows cannot hide permanent or future-active bans in lists or details.
## Official fix round 1 findings
@@ -35,6 +35,12 @@ Status: DONE — fix round 1
The two official Minor findings remain parked and unchanged as instructed.
## Official fix round 2 findings
1. **Entity-aware sentinels:** canonical guild DTOs now use the real schema names `userId` and `roomId`. Serialization permits zero only on those two guild fields and the four named CFH fields when the containing DTO has the matching canonical entity href. Generic `*Id` zero values, negatives, unsafe integers, and primary ID zero remain unavailable failures.
2. **Support source fidelity:** `people.support.tickets` remains on strict `fetchUnifiedTicketInbox`. `people.support.ticket-desk` now dispatches to a distinct strict `website_tickets` loader with message aggregation and no help-center source. Real priority/category/assignee/message count and help reply count are present in canonical DTOs; malformed driver rows fail closed.
3. **Multi-account total:** the page CTE and matching-cluster count run as two bounded parallel queries. Empty pages retain the correct total without prefix loading or N+1 queries.
## Strict TDD evidence
### Cycle 1 — exact route catalog
@@ -253,27 +259,86 @@ Test Files 1 passed (1)
Tests 1 passed | 4 skipped (5)
```
## Fix round 2 strict behavioral TDD evidence
### Entity-aware schema sentinels
RED:
```text
pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people/queries/people-queries.test.ts -t "zero sentinels"
Test Files 1 failed (1)
Tests 2 failed | 19 skipped (21)
Valid guild userId/roomId zero and CFH senderId/reportedId/moderatorId/roomId zero were rejected by generic identifier validation.
```
GREEN:
```text
same command
Test Files 1 passed (1)
Tests 2 passed | 19 skipped (21)
```
### CMS-only ticket desk and help reply counts
RED:
```text
pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people/queries/people-queries.test.ts src/features/housekeeping/domains/people/queries/people-adapters-production.test.ts -t "CMS-only context loader|reply counts"
Test Files 2 failed (2)
Tests 2 failed | 28 skipped (30)
The desk received a help row with synthetic normal priority; help summary omitted replyCount.
```
GREEN:
```text
same command
Test Files 2 passed (2)
Tests 2 passed | 28 skipped (30)
```
### Independent multi-account total
RED:
```text
pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people/queries/people-adapters-production.test.ts -t "beyond the last page"
Test Files 1 failed (1)
Tests 1 failed | 8 skipped (9)
Expected total 4 on the empty page; received 0.
```
GREEN:
```text
same command
Test Files 1 passed (1)
Tests 1 passed | 8 skipped (9)
```
## Verification
```text
pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people/routes.test.ts src/features/housekeeping/domains/people/models.test.ts src/features/housekeeping/domains/people/queries/people-queries.test.ts src/features/housekeeping/domains/people/queries/people-adapters-production.test.ts
Test Files 4 passed (4)
Tests 35 passed (35)
Tests 40 passed (40)
pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/domains/people src/features/housekeeping/foundation/foundation-source-contract.test.ts src/features/housekeeping/foundation/authorization.test.ts src/features/housekeeping/foundation/capability-context.test.ts src/features/housekeeping/foundation/server-capability-context.test.ts src/features/housekeeping/foundation/contracts/contracts.test.ts
Test Files 9 passed (9)
Tests 81 passed (81)
Tests 86 passed (86)
pnpm test:housekeeping
Test Files 49 passed (49)
Tests 430 passed (430)
Tests 435 passed (435)
pnpm typecheck
tsc --noEmit
Exit 0
pnpm exec biome check --formatter-enabled=false <11 exact changed Task 11 TypeScript files>
Checked 11 files. No fixes applied.
pnpm exec biome check --formatter-enabled=false <7 exact changed Task 11 TypeScript files>
Checked 7 files. No fixes applied.
git diff --check
Exit 0