443977be9b
Close Supabase advisor findings by enabling RLS and deny-by-default policies on new public agent-chat tables. Clarify backend RLS governance and incident runbook steps to prevent config-drift regressions.
124 lines
5.3 KiB
Markdown
124 lines
5.3 KiB
Markdown
## Python Environment
|
|
|
|
**MUST use uv for dependency management and virtual environment execution.**
|
|
|
|
- All Python commands: `uv run <command>`
|
|
- Add dependencies: `uv add <package>`
|
|
- All dependencies declared in `pyproject.toml`
|
|
|
|
## Code Quality Checks
|
|
|
|
**Git pre-commit hook enforces code quality before commit.**
|
|
|
|
Pre-commit hook automatically runs on backend/ directory:
|
|
- `ruff check` - code style and linting
|
|
- `basedpyright` - type checking with error level
|
|
|
|
If any error detected, commit is rejected. Fix errors before committing.
|
|
Do not bypass or weaken checks (no ignores, disables, or config relaxations). Resolve the underlying issues.
|
|
|
|
## Logging
|
|
|
|
**MUST use project logger for all runtime logging.**
|
|
|
|
- Use project logger from `backend/src/core/logging/*`
|
|
- Prohibit: print(), logging.info/warning/error directly
|
|
- Required: structured logging with context
|
|
- Log levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
|
|
## HTTP API Standards
|
|
|
|
**MUST follow RESTful conventions and RFC 7807 for error responses.**
|
|
|
|
- Errors must use `application/problem+json` with RFC 7807 fields
|
|
- No custom response envelopes for HTTP APIs
|
|
- Request and response validation must use Pydantic models
|
|
|
|
## Environment Variables
|
|
|
|
**Backend env access MUST go through** `backend/src/core/config/settings.py`.
|
|
|
|
- Only use `Settings()` / `config` from `core.config.settings`
|
|
- Do not call `os.environ`, `os.getenv`, `dotenv`, or manual parsing in backend runtime code
|
|
- Tests can set env vars via `monkeypatch.setenv`, and should read values via `Settings()` unless the test is explicitly validating env plumbing
|
|
- Canonical principle: one source of truth per setting; no duplicate/derived env vars in backend code
|
|
|
|
## TDD First Policy
|
|
|
|
**Principle: tests before implementation.**
|
|
|
|
### Coverage Requirements
|
|
- Minimum coverage: 80%
|
|
- Required test types:
|
|
- Unit: isolated functions, utilities, components
|
|
- Integration: API endpoints, database operations
|
|
- E2E: critical user flows (Playwright)
|
|
|
|
### Limited Exceptions
|
|
- Docs-only changes (README, comments, formatting) may skip integration/E2E
|
|
- Non-runtime config changes may skip E2E if no behavior changes
|
|
- Any runtime code change requires unit + integration + E2E
|
|
- If an exception is used, record the reason in the PR/test notes
|
|
|
|
### Mandatory TDD Workflow
|
|
1. Write tests (RED) - they must fail
|
|
2. Run tests - confirm failure
|
|
3. Implement minimal code (GREEN) - only to pass
|
|
4. Run tests - confirm success
|
|
5. Refactor (IMPROVE)
|
|
6. Verify coverage - must be 80%+
|
|
|
|
### Enforcement
|
|
- Must use the `tdd-guide` agent for new features
|
|
- Do not write implementation before tests
|
|
- Do not lower coverage requirements
|
|
- Must include unit, integration, and E2E tests
|
|
|
|
## Database Development Rules
|
|
|
|
### Core Principle
|
|
- **Supabase**: authentication (JWT source of truth)
|
|
- **Backend**: business authorization (service layer)
|
|
- **SQLAlchemy ORM**: data access layer (async + asyncpg, service_role connection)
|
|
|
|
### Architecture
|
|
Use `schemas / repository / service` pattern:
|
|
- `schemas.py` — Pydantic models
|
|
- `repository.py` — CRUD only, no auth, no commit (only flush), must receive session (never create session/engine)
|
|
- `service.py` — authorization + business logic + transaction boundary (must commit/rollback)
|
|
- `dependencies.py` — DI (`get_db`, `get_current_user`)
|
|
|
|
### Auth & Data Access
|
|
- Backend must verify JWT signature and expiration (not just decode)
|
|
- Extract `user_id` from JWT `sub` claim
|
|
- Backend connects with **service_role** (bypasses RLS)
|
|
- `owner_id` always derived from JWT, never from client
|
|
- Scope queries by owner/org; public access must be explicit
|
|
- service_role key is backend-only; never expose credentials
|
|
- Prohibit calling Supabase Admin API (service_role key) from repository/service layers
|
|
|
|
### Migrations
|
|
- **Alembic is the single source of truth** for schema migrations
|
|
- ORM model changes → `alembic revision --autogenerate`
|
|
- Raw SQL (policies, triggers, functions) → `op.execute()`
|
|
- Migrations must be reversible; no reliance on generated IDs
|
|
|
|
### RLS Guidance
|
|
- Backend does not rely on RLS for correctness (uses service_role), but RLS is mandatory as a defensive boundary for tables in PostgREST-exposed schemas.
|
|
- **Mandatory default**: any new business table in `public` must enable RLS in the same Alembic migration.
|
|
- The same migration must create policies covering `SELECT/INSERT/UPDATE/DELETE` (minimum requirement).
|
|
- Recommended default policy set for `anon, authenticated`: deny all operations first, then open explicit access only when required.
|
|
- `alembic_version` must not be exposed to `anon` or `authenticated`.
|
|
|
|
#### Exemption Rule (strict)
|
|
- Exemptions are allowed only when a new `public` table is guaranteed not to be exposed to PostgREST clients.
|
|
- Exemptions must be explicit in the migration file with rationale and verification notes (why safe, how exposure is prevented).
|
|
- If exposure is uncertain, do not exempt: enable defensive RLS by default.
|
|
|
|
#### Migration Acceptance Checklist (RLS)
|
|
- [ ] New `public` business table has `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` in migration
|
|
- [ ] Policies for `SELECT/INSERT/UPDATE/DELETE` are present in migration
|
|
- [ ] Policy target roles are explicit (`anon`, `authenticated`, or both)
|
|
- [ ] Downgrade path is reversible and does not silently weaken intended production security
|
|
- [ ] Any exemption is documented with clear non-exposure evidence
|