# Development Principles — Accessible Video Processing Platform

<!-- SCOPE: principles | owner: ln-111 | generated: 2026-04-29 -->

These 11 principles govern all engineering decisions on this project. They are ordered by priority — earlier principles override later ones when they conflict.

---

## P-01: Standards First

Implement to agreed specifications. The `video_accessibility_development_plan.txt` is the authoritative source for API contracts, schemas, state machine transitions, and worker pipeline behaviour. Read it before implementing a new feature. When the spec and the code diverge, fix the code.

## P-02: Security by Design

Security controls are non-negotiable:
- Access tokens in JS memory only — never localStorage
- Refresh tokens in HttpOnly cookies only
- RBAC enforced on every endpoint server-side
- Signed GCS URLs with 24h expiry — never store URLs
- All reviewer actions must emit audit log entries
- Generic error messages — never return internal exception details to clients

## P-03: Async Correctness

The backend is async (FastAPI + asyncio). Celery workers run in a separate sync process. Rules:
- Never call synchronous blocking I/O (`requests.get`, `time.sleep`) in async FastAPI routes
- Never share asyncio connections across Celery task boundaries — create connections per task
- Use `httpx.AsyncClient` for HTTP in async routes
- Use `asyncio.get_running_loop().run_in_executor()` only as a last resort for unavoidable sync calls

## P-04: Fail Loudly on Configuration

Missing required secrets must crash startup, not fall back to insecure defaults. Use `os.environ["KEY"]` (raises `KeyError`) instead of `os.environ.get("KEY", "weak_default")`. The `DEFAULT_ADMIN_PASSWORD` fallback is a known violation that must be fixed.

## P-05: YAGNI

Build only what is specified and currently needed. No speculative abstractions, no helper utilities for hypothetical future use cases. Three similar lines is better than a premature abstraction. If a feature is out of scope, defer it — don't build a "foundation" for it.

## P-06: KISS

Simple code is correct code. Prefer flat, readable functions over clever abstractions. A 20-line function that does one thing clearly is better than a 5-line function using three layers of metaprogramming.

## P-07: DRY — but only after the second time

Do not abstract on first encounter. Abstract when the same logic appears in a second place. The `broadcast_status_update()` function is copy-pasted in two task files — this is the known violation to fix.

## P-08: No Comments for What, Only for Why

Code identifiers describe what the code does. Comments explain non-obvious constraints, hidden invariants, or bug workarounds. `# logger undefined here` is a good comment. `# increment counter` is not.

## P-09: Validate at System Boundaries Only

Validate untrusted input (HTTP request bodies, AI model output, file uploads) at the boundary. Trust internal code, framework guarantees, and typed function signatures. Do not add defensive null-checks on values that the framework guarantees are non-null.

## P-10: No Silent Failures

Every error has two acceptable outcomes: it is handled with a logged warning, or it propagates up as an exception. Swallowed exceptions that log nothing (`except Exception: pass`) are forbidden. The `authz.py` `cache_key` NameError swallowed silently is a known violation.

## P-11: Test What Matters

Follow risk-based testing (Priority = Business Impact × Probability). Tests with Priority ≥15 must exist before a feature is considered production-ready. Current critical gaps: RBAC (`authz.py`), job state machine (`ingest_and_ai.py`), audit logger, glossary retrieval — all Priority ≥20.

---

## Maintenance

**Update triggers:** New architectural decision that changes how engineers should approach a class of problems.
**Verification:** Each principle has a measurable compliance check — run a brief audit against recent commits before each production deploy.

<!-- END SCOPE: principles -->
