Skill: PR Review
Use this skill to review pull requests for VoxBento. Covers correctness, security, architecture compliance, and testing.
PR Review Checklist
1. Invariant Compliance
- No Vue, React, jQuery, inline
<script>blocks. - No Flask, Socket.IO, aiortc.
- No
AudioContext.destinationfor interpreter mic audio. -
from __future__ import annotationsat top of every new/modified Python file. - New Python code uses
portal.*imports (not relative imports or new top-level modules). -
uv.lockonly changed ifuv sync --python 3.13 --devwas run. - Role is never trusted from client data in WS handlers.
2. Auth & Security
- All redirects use
safe_redirect()— no rawRedirectResponse(url=user_input). - No open redirect:
next_url/nextquery params validated before use. - Admin routes have
dependencies=[Depends(require_admin)]. - API keys stored encrypted via
portal.crypto.encrypt_val; never stored plaintext. - No secrets logged or returned in API responses.
- JWT tokens use
settings.effective_jwt_secret; no hardcoded secrets. - New form inputs validated before DB write.
3. Database Changes
- Model changes in
portal/models.pyhave a corresponding Alembic migration. - Migration uses
batch_alter_tablefor column operations on SQLite (see migration 008 as reference). - New DB columns have appropriate defaults and nullability.
- Relationships that need
mediamtx_pathusejoinedload(DBBooth.event). - CRUD functions use
async with get_session() as session:pattern.
4. Route Changes
- New routes have correct auth dependency.
- Error cases raise
HTTPExceptionwith appropriate status codes. - New page routes redirect to login if unauthenticated (using
safe_redirect). - New API routes respect
_require_access(credentials, token)if applicable.
5. WebSocket Protocol
- New WS message types have a handler in
fastapi_app.pyws_boothloop. -
session.granted_roleused, notdata['role']. - New
Booth.as_public_dict()fields are intentional (broadcast to all clients).
6. Transcription Changes
- New provider implements
TranscriptionProviderABC. - New provider added to
PROVIDERSdict inworker.py. - New provider registered in
ProviderEnumandALLOWED_MODELS. - New API key column follows Fernet encryption pattern.
- Worker lifecycle handles
CancelledErrorand cleans up ffmpeg process.
7. Frontend Changes
- Plain ES modules — no import maps, no build step, no npm.
-
node --check static/js/*.jspasses. - No
AudioContext.destinationfor mic audio. - New UI elements have IDs/data attributes expected by JS (not hardcoded strings).
- WHIP/WHEP URLs constructed from
portal.dataset.*— not hardcoded.
8. Tests
- New functionality has at least one test.
- Tests use
anyio+pytest.mark.anyiofixture (seeconftest.py). - DB tests use
configure('sqlite+aiosqlite:///:memory:')+init_db(). - No test uses production DB URL.
-
uv run pytest tests/ -vpasses.
9. Documentation
-
README.mdupdated if user-facing behavior changed. -
docs/how-it-works.mdxupdated if system design changed. - Relevant context file in
.github/.agents/context/updated. -
agents.mdupdated if invariants changed.
High-Risk Patterns to Flag
| Pattern | Risk | Action |
|---|---|---|
RedirectResponse(url=request.query_params['next']) |
Open redirect | Replace with safe_redirect |
role = data.get('role') in WS handler |
Role injection | Use session.granted_role |
session.execute(f"... {user_input} ...") |
SQL injection | Use parameterized queries |
event.openai_api_key = openai_key (plaintext) |
API key exposure | Use encrypt_val |
logger.info(f"Key: {api_key}") |
Secret leakage | Remove log line |
| New npm/yarn/vite config | Violates no-build constraint | Remove |
New <script> tag in template |
Inline script | Move to ES module file |
Running Validation Locally
uv sync --python 3.13 --dev
uv run pytest tests/ -v
node --check static/js/interpreter-booth.js
node --check static/js/whep-listener.js
uv run alembic upgrade head # if migration added