This runbook is for operators diagnosing reputation API and score recomputation problems in TalentTrust Backend. It reflects the current implementation, including the places where recovery still requires an application maintenance action.
| Component | Path | Responsibility |
|---|---|---|
| HTTP routes | src/routes/reputation.routes.ts |
Authenticates requests and enforces reviews.read or reviews.create permissions |
| Controller | src/controllers/reputation.controller.ts |
Maps service and validation failures to HTTP responses |
| Reputation service | src/services/reputation.service.ts |
Applies rating rules, calculates scores, persists ratings, and writes audit events |
| Repository | src/repositories/reputationRepository.ts |
Reads and writes reputation_entries in SQLite |
| Recompute processor | src/queue/processors/reputation-recompute-processor.ts |
Recomputes profiles in pages and isolates failures by subject |
| Queue manager | src/queue/queue-manager.ts |
Runs BullMQ workers, retries jobs, and exposes queue health |
| Scheduler service | src/services/reputation-scheduler.service.ts |
Can enqueue periodic or manual recomputes |
| Checkpoint store | src/models/reputation-checkpoint.store.ts |
Tracks recompute progress in process memory |
| Profile store | src/models/reputation.store.ts |
Holds recomputed profiles in process memory |
| Audit service | src/audit/service.ts |
Records REPUTATION_UPDATED after a rating is stored |
The API is mounted at /api/v1/reputation in src/app.ts.
| Method | Path | Permission | Purpose |
|---|---|---|---|
GET |
/api/v1/reputation/:id |
reviews.read |
Return the current reputation profile |
PUT |
/api/v1/reputation/:id |
reviews.create |
Validate and submit a review |
The current router serves the write operation as PUT. Do not use the
POST /:id/rate path described by older API documentation unless the router is
changed to mount it.
| Variable | Default | Validation | Effect |
|---|---|---|---|
REPUTATION_DECAY_LAMBDA |
0.005 |
Number greater than 0 and at most 1 | Controls how quickly old ratings lose weight |
REPUTATION_SCORE_ALGORITHM_VERSION |
exp-decay-v1 |
Non-empty string | Identifies the algorithm in API responses |
Invalid scoring configuration is caught by validateEnv(). During profile reads,
the reputation service falls back to the defaults above if full environment
validation fails. Treat repeated fallback behavior as a configuration defect
rather than an acceptable production state.
| Variable | Default | Effect |
|---|---|---|
DB_PATH |
talenttrust.db |
SQLite file containing reputation_entries |
REDIS_HOST |
localhost |
BullMQ Redis host |
REDIS_PORT |
6379 |
BullMQ Redis port |
REDIS_PASSWORD |
unset | Redis password; treat as a secret |
QUEUE_CONCURRENCY |
5 |
Worker concurrency shared by queue types |
QUEUE_JOB_TIMEOUT_MS |
30000 |
Default job attempt timeout in milliseconds |
QUEUE_JOB_TIMEOUT_REPUTATION_UPDATE_MS |
default timeout | Timeout override for reputation update jobs |
QUEUE_JOB_TIMEOUT_REPUTATION_RECOMPUTE_MS |
default timeout | Timeout override for reputation recompute jobs |
Retry policies are defined in src/queue/retry-policy.ts:
| Queue | Attempts | Backoff |
|---|---|---|
reputation-update |
2 | Fixed, 5 seconds |
reputation-recompute |
3 | Exponential, starting at 2 seconds |
RETRY_POLICY_REPUTATION_UPDATE_* and
RETRY_POLICY_REPUTATION_RECOMPUTE_* environment overrides are supported by the
shared retry-policy loader. See docs/configuration.md before changing them.
- Authentication and
reviews.createauthorization run before the controller. - The payload is validated.
- The service rejects self-ratings, duplicate ratings, non-participants, and invalid comments.
- The repository inserts one row into
reputation_entries. - The audit service writes a
REPUTATION_UPDATEDevent.
The database enforces one rating for each
reviewer_id + target_id + context_id combination.
ReputationService.getProfile() reads all ratings for the target and returns both
an arithmetic score and a recency-weighted score. The weighted score uses
REPUTATION_DECAY_LAMBDA.
The reputation-recompute processor:
- Pages through distinct
target_idvalues, 100 at a time by default. - Calls
ReputationService.getProfile()for each target. - Skips profiles updated within 24 hours unless
forceRecomputeis true. - Writes computed profiles to the in-memory reputation store.
- Updates the in-memory checkpoint after each successful target.
- Logs and skips an individual target when its recompute fails.
Important limitations:
ReputationSchedulerServicedefaults to a daily interval, butsrc/app.tsdoes not currently start it. Do not assume daily jobs are running merely because the service exists.- Checkpoints and recomputed profiles use in-memory
Mapstores. They do not survive a process restart.
An admin can inspect all initialized queues and recent failures:
GET /api/v1/admin/queue-health
Authorization: Bearer <admin-token>For reputation-update and reputation-recompute, inspect:
isInitialized: the queue and worker were created;waitinganddelayed: backlog or repeated retries;active: work currently running;failed: jobs retained after exhausting retries;paused: worker is not processing work;failures: recent sanitized failure reasons.
Logs are structured JSON. Search on these stable fields and messages:
| Signal | Meaning |
|---|---|
processor="reputation" |
Reputation update processor activity |
processor="reputation-recompute" |
Bulk recompute activity |
Failed to start reputation scheduler |
Redis or queue initialization failed |
Failed to schedule reputation recompute job |
A recompute was not enqueued |
Failed to recompute reputation for subject; skipping |
One target was omitted from the run |
Audit logging failed |
The rating row may exist without its audit record |
Reputation recompute job completed |
A batch finished; inspect totalProcessed |
There are no dedicated reputation Prometheus counters or gauges in the current source. Use the admin queue-health endpoint, structured logs, audit records, and generic HTTP metrics.
| Condition | Severity | Response |
|---|---|---|
Any retained failed reputation-update job |
Warning | Inspect validation and Redis errors before replaying |
Any retained failed reputation-recompute job |
Warning | Check the failure reason and whether a full recompute is required |
| Queue is uninitialized or paused in a service expected to process reputation jobs | Critical | Restore Redis connectivity and restart the worker |
| Repeated per-subject recompute warnings | Warning | Identify affected target IDs and repair the underlying data |
| Any audit logging failure | Critical | Reconcile the rating row and audit trail before accepting another write |
| Recompute has no successful completion within the intended schedule | Warning | Verify a scheduler or external trigger is actually configured |
Likely cause: missing/invalid JWT, missing role permission, self-rating, or a reviewer/target that is not part of the referenced contract.
Recovery:
- Use the request ID from the response to find the request log.
- Verify the caller has
reviews.readorreviews.createas appropriate. - Verify both users belong to the referenced contract.
- Do not bypass the participation or self-rating guards.
Likely cause: malformed payload, rating outside integer range 1-5, missing identifier, or an invalid comment.
Recovery: correct the request. Retrying the same payload will not help.
Likely cause: the reviewer already rated this target for this context.
Recovery: fetch or query the existing row. Ratings are immutable; do not delete one merely to make a retry succeed.
Symptom: logs contain Audit logging failed.
Risk: the repository insert happens before the audit write. The API error message says the rating was not persisted, but the SQLite row may already exist.
Recovery:
- Stop automatic client retries for the affected request.
- Query
reputation_entriesusing reviewer, target, and context IDs. - Check the audit store for the corresponding
REPUTATION_UPDATEDevent. - If the rating exists without an audit event, follow the audit reconciliation procedure rather than submitting the rating again.
- Restore the audit backend and verify it with a controlled write.
Recovery:
- Check
/api/v1/admin/queue-health. - Verify
REDIS_HOST,REDIS_PORT, and secret injection forREDIS_PASSWORD. - Confirm Redis is reachable from the application environment.
- Restart the worker after connectivity is restored.
- Confirm both reputation queues report
isInitialized: truebefore enqueuing more work.
Recovery:
- Read the retained failure reason from queue health or worker logs.
- Correct Redis, database, timeout, or data problems first.
- Re-enqueue a
REPUTATION_RECOMPUTEjob through approved maintenance tooling. There is currently no public HTTP endpoint for this action. - Use
forceRecompute: truewhen the goal is to rebuild all profiles. - Monitor for a completion log and compare
totalProcessedwith the expected number of distinct reputation targets.
Symptom: Failed to recompute reputation for subject; skipping warnings.
Recovery:
- Collect the affected target IDs from correlated logs.
- Verify their rating timestamps and related rows are readable.
- Repair the data or configuration error.
- Run a forced recompute and confirm the warning does not recur.
The checkpoint and profile stores are in memory, so restart recovery cannot rely on the previous checkpoint.
Recovery: enqueue a new forced recompute from the beginning. Do not claim the old run resumed unless the checkpoint implementation has first been replaced by durable storage.
Recovery:
- Verify the deployed
REPUTATION_DECAY_LAMBDAand algorithm version. - Compare a small sample against the formula in
docs/reputation-scoring.md. - Restore the previous configuration if the change was accidental.
- Run a forced recompute after configuration is stable.
After recovery:
GET /api/v1/reputation/:idreturns the expected score for a known target.- Authorization still rejects an unauthorized write.
/api/v1/admin/queue-healthshows initialized, unpaused reputation queues.- No new reputation queue failures appear.
- A controlled rating produces one database row and one
REPUTATION_UPDATEDaudit event. - A recompute emits a completion log with the expected processed count.