fix(12-05): bump version to 0.2.1 and update docs for migration-gated startup

- backend/main.py, frontend/package.json: version 0.2.0 → 0.2.1
- CLAUDE.md, AGENTS.md: current state updated to reflect gap-closure
- README.md: startup instructions note migrate runs automatically; update migration commands
- RUNBOOK.md: startup diagram includes migrate service; migration gate explanation
- SECURITY.md: Phase 12 gap-closure threat register and security gate evidence
This commit is contained in:
curo1305
2026-06-20 10:49:21 +02:00
parent de2efd1664
commit 3ca57dcd0c
6 changed files with 49 additions and 14 deletions
+3 -3
View File
@@ -4,7 +4,7 @@
DocuVault is a multi-user SaaS document management platform built on FastAPI (Python) + Vue 3. It handles document upload, text extraction (PDF/DOCX/image/text), AI-based topic classification, per-user isolated storage, folder organization, document sharing, and pluggable cloud storage backends (OneDrive, Google Drive, Nextcloud, WebDAV).
**Current state:** v0.2.0 — Phase 12 complete (2026-06-19). Cloud resource foundation: connection-ID browse API, CloudResourceAdapter contract, durable cloud item metadata, capability-aware action rendering, breadcrumb freshness indicators. Cloud and local files share one StorageBrowser implementation. Security-negative suite in test_cloud_security.py. Not cleared for public internet deployment.
**Current state:** v0.2.1 — Phase 12 gap-closure complete (2026-06-20). Migration-gated startup: one-shot migrate service runs alembic upgrade head before backend/worker/beat start (service_completed_successfully gate). 0005→0006 upgrade regression in test_migration_0006.py. RUNBOOK migration ops section. Cloud resource foundation: connection-ID browse API, CloudResourceAdapter contract, durable cloud item metadata, capability-aware action rendering. Not cleared for public internet deployment.
## Stack
@@ -123,9 +123,9 @@ This project uses the GSD (Get Shit Done) planning workflow. Planning artifacts
/gsd:progress — check status and advance workflow
```
### Current state: v0.2.0 — Phase 12 complete (2026-06-19)
### Current state: v0.2.1 — Phase 12 gap-closure complete (2026-06-20)
Phase 12 (cloud-resource-foundation): full cloud browse foundation — CloudResourceAdapter contract, connection-ID browse API (`GET /api/cloud/connections/{id}/items`), durable cloud item metadata (CloudItem ORM), connection-ID routing (`/cloud/:connectionId`), capability-aware action rendering (CapabilityButton / aria-disabled), breadcrumb freshness indicators (refreshing/fresh/stale), session-only folder memory, display-name rename for cloud connections, and a dedicated security-negative integration suite (`test_cloud_security.py`). Cloud and local files share one StorageBrowser row/action implementation. The browse foundation is complete; Phase 13 adds cloud mutations (upload/move/delete) and Phase 14 adds byte caching. The app is functional but alpha-quality — not cleared for public internet deployment.
Phase 12 gap-closure: migration-gated Compose startup (migrate service, service_completed_successfully dependency gate on backend/worker/beat), 0005→0006 upgrade regression tests (test_migration_0006.py), RUNBOOK migration ops section. Full Phase 12 cloud browse foundation — CloudResourceAdapter contract, connection-ID browse API (`GET /api/cloud/connections/{id}/items`), durable cloud item metadata (CloudItem ORM), connection-ID routing (`/cloud/:connectionId`), capability-aware action rendering (CapabilityButton / aria-disabled), breadcrumb freshness indicators (refreshing/fresh/stale), session-only folder memory, display-name rename for cloud connections, and a dedicated security-negative integration suite (`test_cloud_security.py`). Cloud and local files share one StorageBrowser row/action implementation. The browse foundation is complete; Phase 13 adds cloud mutations (upload/move/delete) and Phase 14 adds byte caching. The app is functional but alpha-quality — not cleared for public internet deployment.
## Development Setup
+2 -2
View File
@@ -4,7 +4,7 @@
DocuVault is a multi-user SaaS document management platform built on FastAPI (Python) + Vue 3. It handles document upload, text extraction (PDF/DOCX/image/text), AI-based topic classification, per-user isolated storage, folder organization, document sharing, and pluggable cloud storage backends (OneDrive, Google Drive, Nextcloud, WebDAV).
**Current state:** v0.2.0 — Phase 12 complete 2026-06-19. Provider-neutral cloud resource contract (CloudResourceAdapter, CloudResource, CloudCapability), durable CloudItem/CloudFolderState metadata layer (Alembic migration 0006), connection-ID browse API with IDOR protection and stale-while-revalidate, refresh_cloud_folder Celery task, and capability-aware StorageBrowser frontend. 12 of 12 phases complete. Not cleared for public internet deployment.
**Current state:** v0.2.1 — Phase 12 gap-closure complete 2026-06-20. Migration-gated Compose startup (migrate service, service_completed_successfully dependency gate), 0005→0006 upgrade regression tests, RUNBOOK migration ops section. Provider-neutral cloud resource contract (CloudResourceAdapter, CloudResource, CloudCapability), durable CloudItem/CloudFolderState metadata layer (Alembic migration 0006), connection-ID browse API with IDOR protection and stale-while-revalidate, refresh_cloud_folder Celery task, and capability-aware StorageBrowser frontend. 12 of 12 phases complete. Not cleared for public internet deployment.
## Stack
@@ -119,7 +119,7 @@ This project uses the GSD (Get Shit Done) planning workflow. Planning artifacts
/gsd:progress — check status and advance workflow
```
### Current state: v0.2.0 — Phase 12 (cloud-resource-foundation) complete (2026-06-19)
### Current state: v0.2.1 — Phase 12 gap-closure complete (2026-06-20)
All phases are marked complete in `.planning/ROADMAP.md`. The app is functional but alpha-quality — not cleared for public internet deployment. The next milestone has not been started.
+10 -7
View File
@@ -117,16 +117,15 @@ print('JWT_PUBLIC_KEY=' + base64.b64encode(k.public_key().public_bytes(serializ
# (everything else has usable defaults for local dev)
nano .env
# 5. Start all services
# 5. Start all services (migrations run automatically before backend starts)
docker compose up -d --build
# 6. Run database migrations
docker compose exec backend alembic upgrade head
# 7. Open the app
# 6. Open the app
open http://localhost:5173
```
The `migrate` service runs `alembic upgrade head` automatically before the backend, Celery worker, and Celery beat start. No manual migration step is needed on a normal `docker compose up`.
The bootstrap admin account (email + password from `.env`) is created automatically on first startup if no users exist.
---
@@ -222,8 +221,12 @@ npm run dev # http://localhost:5173
### Database migrations
```bash
# Apply all pending migrations
docker compose exec backend alembic upgrade head
# Migrations run automatically on docker compose up via the migrate service.
# To run them manually (e.g., after a git pull without restarting):
docker compose run --rm migrate
# Check current revision
docker compose run --rm migrate alembic current
# Create a new migration after changing db/models.py
docker compose exec backend alembic revision --autogenerate -m "describe change"
+32
View File
@@ -385,3 +385,35 @@ None. Phase 12 adds a read-only cloud browse surface (no MinIO writes, no quota
|---------|-----------|---------------|-----------|
| T-12-SC | pip-audit tooling | pip-audit not installed in local Python 3.9 environment | No new Python packages added in Phase 12; existing packages audited in Phase 8. Operator should run pip-audit in CI or with Python 3.12 target environment. |
| T-12-disabled | DISABLED connection returns 200 with unavailable capabilities | Browse endpoint is accessible but all operations are unavailable | This is correct behavior: ownership is still asserted (user owns the connection), and capabilities convey the unavailability. No cloud bytes or credentials are served. |
---
## Phase 12 Gap Closure — Schema Drift Fix (2026-06-20)
### Gap Closure Threat Register
| Threat ID | Threat | Disposition | Status | Evidence |
|-----------|--------|-------------|--------|----------|
| T-12-05-01 | App starts against stale schema (UndefinedColumn) | mitigate | CLOSED | `docker-compose.yml` migrate service + `service_completed_successfully` dependency gate; `backend/tests/test_compose_migrations.py` — 7 static assertions |
| T-12-05-02 | DDL privilege escalation (app uses migrate URL) | mitigate | CLOSED | migrate service sets only `DATABASE_MIGRATE_URL`; backend/worker/beat receive only `DATABASE_URL`; test assertions in `test_compose_migrations.py:test_migrate_service_uses_migrate_url` |
| T-12-05-03 | Concurrent migrations (multiple replicas upgrading) | mitigate | CLOSED | Exactly one migrate service; replicas only wait on `service_completed_successfully` — never run Alembic themselves |
| T-12-05-04 | Secrets in Compose config or test output | mitigate | CLOSED | Environment references via `${VAR}` only; no rendered values in source or committed output |
| T-12-05-05 | Integration test destroys developer database | mitigate | CLOSED | `test_migration_0006.py` skips without `INTEGRATION=1`/`INTEGRATION_DATABASE_URL`; explicit skip message; never touches `DATABASE_URL` |
### Gap Closure Security Gate Evidence
**Gate 1 — Compose configuration invariants:**
```
pytest -q backend/tests/test_compose_migrations.py
7 passed
```
**Gate 2 — Migration regression tests:**
```
pytest -q backend/tests/test_migration_0006.py
12 skipped (INTEGRATION not set — correct behavior without disposable DB)
```
Integration tests activate with `INTEGRATION=1` against a disposable PostgreSQL database.
**Gate 3 — No new Python or frontend packages introduced.**
**Gate 4 — No hardcoded credentials in Compose or test files.**
+1 -1
View File
@@ -244,7 +244,7 @@ async def lifespan(app: FastAPI):
# ── Application factory ───────────────────────────────────────────────────────
app = FastAPI(title="Document Scanner API", version="0.2.0", lifespan=lifespan)
app = FastAPI(title="Document Scanner API", version="0.2.1", lifespan=lifespan)
# Rate limiter state (slowapi)
app.state.limiter = auth_limiter
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "document-scanner-frontend",
"version": "0.2.0",
"version": "0.2.1",
"type": "module",
"scripts": {
"dev": "vite",