Skip to content

[1529] Improve verifier quorum and administrator transitions: bounded performance and operational visibility(closes #1529) - #1597

Merged
1nonlypiece merged 2 commits into
Disciplr-Org:mainfrom
danielchukuma-dev:fix/1529-verifier-quorum-admin-transitions
Aug 30, 2026
Merged

[1529] Improve verifier quorum and administrator transitions: bounded performance and operational visibility(closes #1529)#1597
1nonlypiece merged 2 commits into
Disciplr-Org:mainfrom
danielchukuma-dev:fix/1529-verifier-quorum-admin-transitions

Conversation

@danielchukuma-dev

@danielchukuma-dev danielchukuma-dev commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Closes #1529

Summary

This PR addresses all acceptance criteria for #1529: bounded rendering/polling/network/memory costs and observable degraded behavior for the verifier quorum and administrator transitions state machine anchored at src/routes/verifications.ts and src/routes/adminVerifiers.ts.


Changes

src/routes/adminVerifiers.ts

  • Pagination bounds: GET / enforces MAX_PAGE_LIMIT=200; default reduced to 50. Response includes pagination metadata (limit, offset, count, hasMore).
  • Structured diagnostics: emitDiagnostic() emits structured JSON events on every status transition — fields: action, requestId, actorUserId, targetUserId, latencyMs, outcome, errorCode, fromStatus, toStatus. No secrets or PII are included.
  • X-Request-Id header: All transition responses set this header; echoes the client-supplied value when present, otherwise generates a UUID.
  • Concurrent in-flight guard: A process-local inFlightTransitions Set prevents duplicate concurrent mutations for the same userId on /approve and /suspend, returning 429 on collision. Limitation documented in code: process-local only; cross-replica protection requires a distributed lock (e.g. Redis SET NX PX).
  • Reinstate/deactivate/reactivate: All three handlers now emit diagnostics with latency, outcome, and transition direction.

src/routes/verifications.ts

  • Paginated admin list: GET / gains limit (default 100, max 500) and offset params plus pagination metadata in the response. Prevents unbounded full-table scans.
  • Bulk in-flight guard: A process-local bulkInFlight Set rejects a second concurrent bulk submission from the same verifier with 400, preventing duplicate evidence records or DB amplification during rapid reconnects. Different verifiers are not blocked by each other.
  • Bulk diagnostics: emitVerificationDiagnostic() emits latencyMs, outcome (success/partial), and count after every bulk call. Evidence URLs and raw user strings are explicitly excluded.
  • Cleanup guarantee: Bulk loop wrapped in try/finally so bulkInFlight.delete() always runs even if an unexpected error escapes the per-item catch.

src/services/verifiers.ts

  • listVerifierProfiles: Clamps caller-supplied limit to MAX_VERIFIER_PROFILES_LIMIT=200 (exported). Default changed from 100 → 50.
  • listVerifications: Now accepts ListVerificationsOptions (limit, offset) with MAX_VERIFICATIONS_LIMIT=500 cap (exported). Signature is backward-compatible.
  • getMilestoneApprovalProgress invariants: Throws with a descriptive message when approvalThreshold < 1, approvalThreshold is non-integer, totalVerifiers < 1, or totalVerifiers < approvalThreshold — making the quorum configuration contract explicit.

Tests

Three new focused test files following the existing Jest ESM (jest.unstable_mockModule) pattern:

File Focus
src/tests/adminVerifiers.quorum.test.ts Pagination bounds, permission matrix, full transition matrix, reinstate variants, concurrent guard (429), diagnostic structure, X-Request-Id echoing, userId boundary
src/tests/verifications.quorum.test.ts Paginated list (defaults, cap, hasMore), bulk in-flight guard (same/different verifier), bulk diagnostics, bulk boundary invariants
src/tests/verifiers.service.quorum.test.ts listVerifierProfiles clamp, listVerifications pagination, canTransition matrix (16 cases), getMilestoneApprovalProgress invariant rejection

Validation commands

# Run all tests
npm test

# Run only the new quorum tests
node --experimental-vm-modules node_modules/jest/bin/jest.js \
  --testPathPattern="quorum" --forceExit

# Type-check
npm run build

Design tradeoffs

  • In-process guards vs distributed locks: inFlightTransitions and bulkInFlight are Sets — zero-dependency, zero-latency, but process-local. For multi-replica deployments a Redis SETNX pattern is the production path. Documented inline.
  • offset/limit vs cursor pagination: Matches the existing pattern on other admin list endpoints in this codebase. Cursor pagination is more efficient at large offsets but is out of scope per the issue non-goals (no unrelated refactors).
  • Diagnostics via console.error to stderr: Flows to existing log aggregators without adding a new runtime dependency. Structured JSON makes it grep-able and machine-parseable.

Acceptance criteria checklist

  • Defines and enforces invariants for normal and adversarial inputs (quorum threshold validation, state machine transition matrix, userId sanitization)
  • Sets explicit bounds for pagination (list verifiers max 200, list verifications max 500, bulk max 100)
  • Avoids redundant fetches and state updates — concurrent transition guard (429) and bulk in-flight guard (400) prevent duplicate mutations during rapid interaction or reconnects
  • Exposes actionable client telemetry for latency, failure, and recovery paths without leaking secrets (structured JSON logs, X-Request-Id header)
  • Automated tests cover success, failure, boundary, retry, and permission behavior
  • PR includes validation commands, design tradeoffs, and remaining limitations

Limitations

  • Tests validated by structural review against the project's Jest ESM mock pattern; CI will run them on first push.
  • inFlightTransitions and bulkInFlight are process-local — noted as a known limitation in code comments.

… performance and operational visibility

Refs Disciplr-Org#1529

## What changed

### src/routes/adminVerifiers.ts
- Enforce MAX_PAGE_LIMIT=200 on GET / list; default reduced to 50. Response
  now includes pagination metadata (limit, offset, count, hasMore).
- Add emitDiagnostic() helper: structured JSON events (action, requestId,
  actorUserId, targetUserId, latencyMs, outcome, errorCode, fromStatus,
  toStatus) emitted to stderr on every transition, with no secrets or PII.
- Set X-Request-Id response header on all transition endpoints; echoes
  client-supplied header when present, otherwise generates a UUID.
- Add inFlightTransitions Set to guard /approve and /suspend against
  duplicate concurrent mutations for the same userId (429 response).
- Update reinstate, deactivate, reactivate handlers to emit diagnostics.

### src/routes/verifications.ts
- Add Router import (was accidentally removed) and pagination to GET /:
  default limit=100, max limit=500, offset param, hasMore flag, pagination
  metadata in response. Prevents unbounded full-table scans.
- Add bulkInFlight Set: rejects a second concurrent bulk submission from
  the same verifier with 400 to prevent duplicate evidence writes.
- Add emitVerificationDiagnostic() helper for bulk action telemetry
  (latencyMs, outcome success/partial, count, no URLs or secrets logged).
- Wrap bulk loop body in try/finally so bulkInFlight.delete() is always
  called even if an unexpected error escapes the per-item catch.

### src/services/verifiers.ts
- listVerifierProfiles: clamp uncapped caller limit to
  MAX_VERIFIER_PROFILES_LIMIT=200; export the constant.
- listVerifications: add ListVerificationsOptions (limit, offset) with
  MAX_VERIFICATIONS_LIMIT=500 cap; export both.
- getMilestoneApprovalProgress: enforce invariants — approvalThreshold
  must be a positive integer; totalVerifiers when provided must be a
  positive integer and ≥ approvalThreshold.

### src/tests/adminVerifiers.quorum.test.ts (new)
- Pagination bounds (default, cap, hasMore, negative/NaN limit).
- Permission matrix (USER, VERIFIER, ADMIN, unauthenticated).
- Full transition matrix (approve, suspend, deactivate, reactivate).
- Reinstate: already-active no-op, suspended→approved, suspended→pending,
  deactivated→approved, not-found 404.
- Concurrent in-flight guard (429, clears after first completes).
- Diagnostic log structure (no secrets, includes required fields).
- X-Request-Id echoing.
- userId sanitization boundary (whitespace, >128 chars, exactly 128).

### src/tests/verifications.quorum.test.ts (new)
- Paginated admin list (defaults, cap, hasMore, non-numeric limit, VERIFIER
  403 on GET).
- Bulk in-flight guard (same verifier → 400, sequential ok, different
  verifiers not blocked).
- Bulk diagnostic emission (success outcome, partial outcome).
- Bulk boundary invariants (>100 items → 400, =100 accepted, empty → 400,
  non-array → 400).

### src/tests/verifiers.service.quorum.test.ts (new)
- listVerifierProfiles: MAX_VERIFIER_PROFILES_LIMIT=200, clamping, default,
  negative/Infinity limit, negative offset.
- listVerifications: MAX_VERIFICATIONS_LIMIT=500, clamping, default,
  offset, whereIn with/without targetIds.
- canTransition: full valid + invalid edge coverage (16 cases).
- getMilestoneApprovalProgress: invariant rejection (threshold=0, negative,
  float, totalVerifiers<threshold, totalVerifiers=0/negative), valid cases.

## Design tradeoffs
- inFlightTransitions and bulkInFlight are process-local Sets. In a
  multi-replica deployment these guards only prevent intra-process
  duplicates; a Redis SETNX lock would be needed for cross-process
  protection. The limitation is documented in code comments.
- Diagnostics use console.error (stderr) for structured JSON so they flow
  to existing log aggregators without adding a new dependency. They omit
  evidence URLs and raw user-controlled strings to avoid log injection.
- Pagination uses offset/limit (not cursor) to match the existing pattern
  already established on other admin list endpoints in this codebase.

## Limitations
- node_modules not present in this environment; tests validated by
  structural review against the Jest ESM mock pattern used in the
  existing test suite.
@danielchukuma-dev danielchukuma-dev changed the title [1529] Improve verifier quorum and administrator transitions: bounded performance and operational visibility [1529] Improve verifier quorum and administrator transitions: bounded performance and operational visibility(close #1529) Aug 30, 2026
@danielchukuma-dev danielchukuma-dev changed the title [1529] Improve verifier quorum and administrator transitions: bounded performance and operational visibility(close #1529) [1529] Improve verifier quorum and administrator transitions: bounded performance and operational visibility(closes #1529) Aug 30, 2026
@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@danielchukuma-dev Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@1nonlypiece
1nonlypiece merged commit c648266 into Disciplr-Org:main Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Quality][Medium] Improve verifier quorum and administrator transitions: bounded performance and operational visibility

3 participants