Files
project-work/sources/holistic-rubric.md

49 lines
3.5 KiB
Markdown

Reference issues or pull requests here (e.g., "Closes #123")
# Holistic Rubric: Multi-Tenant Authorization in Background Workers
## Task Summary
The goal is to refactor background SQS worker handlers and database services in `potion-voice` to enforce multi-tenant authorization by scoping all MongoDB queries with `userId`. The solution must prevent cross-tenant IDOR vulnerabilities while preserving async execution order, SQS queue lifecycle reliability, and error-handling stability.
---
## Core Requirements
1. **Multi-Tenant Query Scoping**:
- All database reads (`findOne`), updates (`findOneAndUpdate`), and status updates must include `userId: jobUserId` in the query criteria.
- Primary models (`UserAudioProfile`, `Salutation`, `VoiceCloning`) and secondary models (`Recording`, `RecordingSalutation`) must be strictly scoped.
2. **Async Dependency Execution Order**:
- In `voice-synthsizer-job-handler/index.js`, dependent fields like `salutationToUpdate.recordingId` must be retrieved **before** querying secondary models (`recordingModel.findOne(...)`).
- Grouping dependent model lookups inside `Promise.all` before parent models resolve is invalid and leads to runtime crashes (`ReferenceError: recordingId is not defined`).
3. **SQS Queue Message Lifecycle Integrity**:
- SQS queue messages must only be deleted via `deleteMessageFromSQS` after processing completes successfully.
- Moving message deletion above execution steps (before synthesis, ffmpeg rendering, or S3 persistence) causes permanent, unrecoverable data loss if execution fails midway.
4. **Robust Error Recovery**:
- Authorization failures must throw catchable errors or return early before running external Python scripts.
- Error handlers in `catch` blocks must not fail or throw unhandled Promise rejections.
---
## Key AI Failure Modes (Meaningful Failures)
- **Failure Mode 1: Async Dependency Crash (`recordingId` is undefined)**
The agent attempts to optimize database queries by fetching `UserAudioProfile`, `Salutation`, and `Recording` inside a single `Promise.all` block. Because `recordingId` is derived from `salutationToUpdate.recordingId`, referencing `recordingId` in the `Promise.all` array causes a `ReferenceError` or queries MongoDB with `_id: undefined`.
- **Failure Mode 2: Premature SQS Message Deletion (Silent Data Loss)**
The agent moves `deleteMessageFromSQS` up before job processing or Python execution completes. If S3 upload or speech rendering fails, SQS cannot redeliver the message, resulting in silent job loss.
- **Failure Mode 3: Invalid Mongoose `findById` Query Objects**
The agent attempts tenant scoping by passing a query object to Mongoose's `findById` (e.g. `Model.findById({ _id: id, userId })`). In Mongoose, `findById` expects a primitive string or ObjectId, causing runtime `CastError: Cast to ObjectId failed`.
- **Failure Mode 4: Incomplete Secondary Query Scoping**
The agent updates primary model queries (`UserAudioProfile`) but forgets secondary queries (`Recording`, `RecordingSalutation`, or status updates inside `catch` blocks), leaving secondary models exposed to cross-tenant mutation.
---
## Scoring Guide
- **PASS**: All database operations are tenant-scoped by `userId`, query dependency order is maintained, SQS message deletion occurs only after success, and all tests pass without runtime exceptions.
- **FAIL**: Any query is unscoped, `recordingId` is referenced before `salutationToUpdate` resolves, SQS messages are deleted prematurely, or Mongoose query errors throw at runtime.