---
phase: 3
slug: document-migration-multi-user-isolation
status: draft
nyquist_compliant: false
wave_0_complete: false
created: 2026-05-23
---

# Phase 3 — Validation Strategy

> Per-phase validation contract for feedback sampling during execution.

---

## Test Infrastructure

| Property | Value |
|----------|-------|
| **Framework** | pytest + pytest-asyncio (existing in codebase) |
| **Config file** | `backend/pytest.ini` or `backend/pyproject.toml` |
| **Quick run command** | `cd backend && pytest tests/test_documents.py tests/test_quota.py tests/test_topics.py -x -q` |
| **Full suite command** | `cd backend && pytest -v` |
| **Estimated runtime** | ~30–60 seconds |

---

## Sampling Rate

- **After every task commit:** Run `cd backend && pytest tests/test_documents.py tests/test_quota.py tests/test_topics.py -x -q`
- **After every plan wave:** Run `cd backend && pytest -v`
- **Before `/gsd:verify-work`:** Full suite must be green
- **Max feedback latency:** 60 seconds

---

## Per-Task Verification Map

| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
| Migration null-user cleanup | 01 | 1 | D-02 | SEC-04 | Null-user docs deleted before NOT NULL constraint | integration | `pytest tests/test_alembic.py::test_migration_0003 -x` | ❌ W0 | ⬜ pending |
| Quota reconciliation | 01 | 1 | D-03 | STORE-03 | used_bytes matches SUM(size_bytes) post-migration | integration | `pytest tests/test_alembic.py::test_migration_0003 -x` | ❌ W0 | ⬜ pending |
| Atomic quota enforce | 02 | 2 | STORE-03 | STORE-03 | No double-spend on concurrent uploads | unit+integration | `pytest tests/test_quota.py -x` | ❌ W0 | ⬜ pending |
| Concurrent quota race | 02 | 2 | STORE-03 (SC2) | STORE-03 | Two concurrent uploads at limit → exactly one 413 | integration | `pytest tests/test_quota.py::test_concurrent_quota_race -x` | ❌ W0 | ⬜ pending |
| Quota exceeded response | 02 | 2 | STORE-05 | STORE-05 | 413 with {used_bytes, limit_bytes, rejected_bytes} | unit | `pytest tests/test_quota.py::test_quota_exceeded_response -x` | ❌ W0 | ⬜ pending |
| Atomic quota decrement | 02 | 2 | STORE-06 | STORE-06 | Delete decrements quota atomically | unit | `pytest tests/test_quota.py::test_delete_decrements_quota -x` | ❌ W0 | ⬜ pending |
| Upload-url endpoint | 02 | 2 | D-05 | SEC-04 | Creates pending Document row + returns presigned URL | unit | `pytest tests/test_documents.py::test_upload_url_endpoint -x` | ❌ W0 | ⬜ pending |
| Confirm endpoint | 02 | 2 | D-05 | STORE-03 | stat_object size used, status=uploaded set | unit | `pytest tests/test_documents.py::test_confirm_endpoint -x` | ❌ W0 | ⬜ pending |
| Quota bar endpoint | 02 | 2 | STORE-04 | — | GET /api/me/quota returns {used_bytes, limit_bytes} | unit | `pytest tests/test_documents.py::test_get_quota -x` | ❌ W0 | ⬜ pending |
| Cross-user access | 03 | 3 | SEC-04 | SEC-04 | Cross-user document access returns 404 | unit | `pytest tests/test_documents.py::test_cross_user_access_404 -x` | ❌ W0 | ⬜ pending |
| Admin 403 on documents | 03 | 3 | SEC-04 (SC4) | SEC-04 | Admin JWT on /api/documents/* returns 403 | unit | `pytest tests/test_documents.py::test_admin_cannot_access_documents -x` | ❌ W0 | ⬜ pending |
| Topic namespace isolation | 03 | 3 | DOC-04 | — | Topic list = system topics + own topics only | unit | `pytest tests/test_topics.py::test_topic_namespace -x` | ❌ W0 | ⬜ pending |
| Per-user AI provider | 04 | 4 | DOC-03/DOC-05 | — | Classifier uses user's assigned provider not global | unit | `pytest tests/test_classifier.py::test_per_user_provider -x` | ❌ W0 | ⬜ pending |
| Celery task provider lookup | 04 | 4 | DOC-05 | — | Celery task resolves provider from document owner's DB | unit | `pytest tests/test_classifier.py::test_celery_task_uses_user_provider -x` | ❌ W0 | ⬜ pending |
| Settings endpoint removed | 04 | 4 | D-12 | — | /api/settings returns 404 | unit | `pytest tests/test_settings.py::test_settings_endpoint_removed -x` | ❌ W0 | ⬜ pending |

*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*

---

## Wave 0 Requirements

- [ ] `backend/tests/test_quota.py` — stubs for STORE-03, STORE-05, STORE-06, concurrent race
- [ ] `backend/tests/test_alembic.py` — stub for migration 0003 test
- [ ] `backend/tests/test_classifier.py` — stubs for DOC-03, DOC-05 per-user provider
- [ ] `backend/tests/conftest.py` — `auth_user` fixture (authenticated user with quota row), `admin_user` fixture, MinIO mock fixtures for `presigned_put_object` and `stat_object`
