Compare commits

...
2 Commits
Author SHA1 Message Date
curo1305andClaude Sonnet 4.6 a42f0ed623 test(08): UAT complete — 7/7 passed, phase 8 marked complete
All 7 UAT tests passed (cold start, auth, documents, admin, cloud storage,
session revocation, Vite 6 build). Security gate: 0 threats open. Phase 8
transition complete — ROADMAP.md updated, PROJECT.md evolved with Phase 8
decisions, stale handoff deleted. Next: Phase 9 (Admin Panel Rearchitecture).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 11:28:56 +02:00
curo1305andClaude Sonnet 4.6 ee3d2d2d3d docs(phase-08): mark VALIDATION.md complete — Nyquist-compliant 9/9
Audit confirmed all phase-8 requirements are COVERED:
- CR-01/02/03: session revocation tests promoted from stubs → 3 passing
- CODE-01/02/03: route regression — 58 passed, 4 xfailed
- CODE-04: frontend smoke — 136/136 passed
- CODE-08: single CloudConnectionOut definition confirmed
- PERF-01: npm packages verified

Full suite: 405 passed, 6 skipped, 7 xfailed (1 pre-existing env gap: no local python-docx)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 11:24:49 +02:00
5 changed files with 71 additions and 126 deletions
+6 -2
View File
@@ -65,7 +65,7 @@ Every user's documents — and the credentials they use to store them — are in
## Context
- **Current state**: v0.1 complete (2026-06-06). All foundation features shipped and security-hardened. App is functional for local/self-hosted use but not hardened for public internet deployment.
- **Current state**: v0.2 in progress — Phase 8 complete (2026-06-12). Backend routers decomposed into focused sub-packages, frontend API client decomposed into domain modules, Vite 6 shipped, CVEs resolved. Phase 9 (Admin Panel Rearchitecture) up next.
- **Tech stack**: FastAPI 0.136+ (Python 3.12), SQLAlchemy 2.0 async, Alembic, MinIO SDK; Vue 3 (Options API), Pinia, Vue Router 4, Vite, Tailwind CSS.
- **Code quality**: v0.1 was built feature-first under time pressure. Both backend and frontend contain duplication, inconsistent patterns, and components that grew beyond their original scope. v0.2 addresses this systematically.
- **Admin panel**: AdminView.vue currently renders admin functionality as tabs appended to the main user layout. This is architecturally wrong — the admin interface needs its own route subtree, layout component, and nav.
@@ -95,6 +95,10 @@ Every user's documents — and the credentials they use to store them — are in
| JTI claim + Redis revocation | Closes 15-min window where revoked session's access token stays valid | Shipped v0.1 Phase 7.2 |
| Options API preserved in v0.2 refactor | Composition API migration is scope-creep for a UX milestone; refactor within Options API | Decision logged to prevent scope drift |
| Admin panel as standalone route subtree | AdminView.vue as tabs-on-user-layout is an architectural mistake | v0.2 introduces /admin/* routes with own layout |
| `client.js` barrel re-export pattern | Zero consumer churn — all 35+ import sites stay unchanged; domain modules hidden behind barrel | Shipped Phase 8 (CODE-04) |
| Sub-routers carry NO prefix | Parent `include_router(sub, prefix=...)` propagates; sub-router with own prefix causes double-segment URLs | Discovered during Phase 8 backend decomposition |
| FastAPI 0.128+ empty-path restriction | `@router.get("")` on a sub-router with empty include prefix raises `FastAPIError` — register root routes on parent aggregator directly | Discovered Phase 8; affects all future sub-router patterns |
| Vite 6 upgrade | Resolved two moderate CVEs (CVE-2026-39363/39364) present in Vite 5; build time unchanged | Shipped Phase 8 (PERF-01) |
## Evolution
@@ -114,4 +118,4 @@ This document evolves at phase transitions and milestone boundaries.
4. Update Context with current state
---
*Last updated: 2026-06-07milestone v0.2 started*
*Last updated: 2026-06-12after Phase 8*
+5 -5
View File
@@ -529,7 +529,7 @@ _Started: 2026-06-07_
## v0.2 Phases
- [ ] **Phase 8: Stack Upgrade & Backend Decomposition** — Dependency bumps land; all three backend router monoliths split into focused sub-packages; frontend API client decomposed into domain modules; shared Pydantic schemas extracted
- [x] **Phase 8: Stack Upgrade & Backend Decomposition** — Dependency bumps land; all three backend router monoliths split into focused sub-packages; frontend API client decomposed into domain modules; shared Pydantic schemas extracted _(2026-06-12)_
- [ ] **Phase 9: Admin Panel Rearchitecture** — Admin panel moves to `/admin/*` route subtree with its own layout, sidebar, and deep-linkable child routes; Tailwind safelist configured; redundant comments purged
- [ ] **Phase 10: UX & Interaction** — Empty states, loading skeletons, keyboard shortcuts, drag-and-drop upload, toast notifications, breadcrumbs, and icon centralization land across the full UI
- [ ] **Phase 11: Visual Design, Responsive Layout & Cleanup** — Consistent spacing, form styling, hover/focus states, and typography applied; mobile-responsive sidebar and layouts ship; dead code deleted; bundle measured
@@ -566,11 +566,11 @@ _Started: 2026-06-07_
**Wave 2** *(blocked on Wave 1)* — Backend decomposition + frontend (parallel)
- [ ] 08-04-PLAN.md — Split `api/admin.py``api/admin/` package: users.py, quotas.py, ai.py, shared.py, __init__.py (CODE-01)
- [ ] 08-05-PLAN.md — Split `api/documents.py``api/documents/` package: upload.py, crud.py, content.py, shared.py, __init__.py (CODE-02)
- [ ] 08-06-PLAN.md — Split `api/auth.py``api/auth/` package: tokens.py, totp.py, password.py, sessions.py, shared.py, __init__.py (CODE-03)
- [x] 08-04-PLAN.md — Split `api/admin.py``api/admin/` package: users.py, quotas.py, ai.py, shared.py, __init__.py (CODE-01)
- [x] 08-05-PLAN.md — Split `api/documents.py``api/documents/` package: upload.py, crud.py, content.py, shared.py, __init__.py (CODE-02)
- [x] 08-06-PLAN.md — Split `api/auth.py``api/auth/` package: tokens.py, totp.py, password.py, sessions.py, shared.py, __init__.py (CODE-03)
- [x] 08-07-PLAN.md — Frontend `client.js` decomposition: utils.js + 7 domain modules + barrel rewrite (CODE-04)
- [ ] 08-08-PLAN.md — PERF-01 dependency bump + tailwind/vite config wiring + requirements.txt exact pinning (PERF-01, D-17)
- [x] 08-08-PLAN.md — PERF-01 dependency bump + tailwind/vite config wiring + requirements.txt exact pinning (PERF-01, D-17)
**Cross-cutting constraints:**
+7 -4
View File
@@ -3,21 +3,22 @@ gsd_state_version: 1.0
milestone: v0.2
milestone_name: Phases
current_phase: 9
status: ready
last_updated: "2026-06-12T00:00:00.000Z"
status: ready_to_plan
last_updated: 2026-06-12T09:27:34.436Z
last_activity: 2026-06-12
progress:
total_phases: 17
completed_phases: 14
total_plans: 73
completed_plans: 73
percent: 80
percent: 82
stopped_at: Phase 08 complete (8/8) — ready to discuss Phase 9
---
# Project State
**Project:** DocuVault
**Status:** Phase 8 complete — ready for Phase 9
**Status:** Ready to plan
**Current Phase:** 9
**Last Updated:** 2026-06-12
@@ -60,6 +61,8 @@ Last activity: 2026-06-12
| Sub-routers carry NO prefix | Parent APIRouter prefix propagates — sub-routers with prefix cause double-segment URLs |
| AdminLayout as route component, not App.vue branch | Router resolves AdminLayout as the /admin component; its router-view renders children |
| to.matched.some() for requiresAdmin guard | Vue Router 4 does not inherit meta to children; direct to.meta check is a security regression |
| FastAPI 0.128+ empty-path sub-router restriction | `@router.get("")` on sub-router with empty include prefix fails; register root routes on parent aggregator |
| Vite 6 upgrade resolves 2 CVEs | CVE-2026-39363/39364 closed; npm audit now clean |
### Roadmap Evolution
@@ -1,82 +0,0 @@
---
context: phase
phase: 08-stack-upgrade-backend-decomposition
plan: 8
total_tasks: 8
status: in_progress
last_updated: 2026-06-12T08:33:19.724Z
---
# BLOCKING CONSTRAINTS — Read Before Anything Else
> These are not suggestions. Each constraint below was discovered through failure.
> Acknowledge each one explicitly before proceeding.
- [ ] CONSTRAINT: FastAPI empty-path sub-router — `@router.get("")` on a sub-router included via `include_router(sub, prefix="")` raises `FastAPIError: Prefix and path cannot be both empty` in FastAPI 0.128+. Register the list route directly on the parent router using `router.add_api_route("", handler, methods=["GET"])` instead.
- [ ] CONSTRAINT: Test monkeypatching after decomposition — Tests that patch `api.documents.X` break after decomposition because `X` moves to a sub-module. Fix: re-export `X` from `__init__.py` AND use a late import inside the handler so the call resolves through the package namespace at call time.
- [ ] CONSTRAINT: Worktree permission lockdown — Spawned agents with `isolation="worktree"` were denied write access inside their worktree paths this session. If re-spawning agents, test write access first or run inline.
**Do not proceed until all boxes are checked.**
## Critical Anti-Patterns
| Pattern | Description | Severity | Prevention Mechanism |
|---------|-------------|----------|---------------------|
| Empty-path sub-router | `@router.get("")` on a sub-router fails in FastAPI 0.128 when include prefix is also empty | blocking | Register root-path routes directly on the parent aggregator router, not on the sub-router |
| Monkeypatch target drift | Test patches `api.documents.X` but `X` moved to `api.documents.upload` after decomposition | blocking | After decomposition: re-export patched names from `__init__.py`; use late import in handler |
| Worktree write lockdown | Agents spawned with `isolation="worktree"` were denied write permission inside the worktree path | advisory | Verify write access or avoid worktrees; inline execution always has permissions |
<current_state>
Phase 8, Wave 2 complete. Plans 08-01 through 08-07 all done with SUMMARY.md files. Only plan 08-08 remains (Wave 3 — dependency upgrades). Plan 08-08 is `autonomous: false` and requires user to manually verify npm packages on npmjs.com before execution. Full backend suite: 405/406 passed (1 pre-existing python-docx env skip).
</current_state>
<completed_work>
- Plan 08-01: xfail stubs + CR contract locks ✅
- Plan 08-02: CloudConnectionOut migration + schemas.py ✅
- Plan 08-03: CR-01/02/03 session-revocation + toastStore stub ✅
- Plan 08-04: `backend/api/admin/` package (13 routes, monolith deleted) ✅
- Plan 08-05: `backend/api/documents/` package (9 routes, monolith deleted) ✅
- Plan 08-06: `backend/api/auth/` package (15 routes, CR-01/02/03 preserved, limiter re-exported) ✅
- Plan 08-07: `frontend/src/api/` decomposed — `client.js` 20 lines, 7 domain modules + `utils.js`
</completed_work>
<remaining_work>
- Plan 08-08 (Wave 3): Dependency upgrades
- Add @vueuse/core, @vueuse/integrations, sortablejs, @tailwindcss/forms, rollup-plugin-visualizer to frontend/package.json
- Upgrade vite to ^6.4.3
- Wire @tailwindcss/forms plugin in tailwind.config.js
- Pin all backend/requirements.txt to exact == versions
- Run pytest -v and npm run build to verify no regressions
- **REQUIRES user to verify packages on npmjs.com first** (see plan frontmatter `user_setup`)
</remaining_work>
<decisions_made>
- `list_documents` registered directly on parent router in `documents/__init__.py` (not via `include_router`) — FastAPI 0.128 empty-path restriction
- `extract_and_classify` and `get_storage_backend_for_document` re-exported from `api.documents.__init__` for test monkeypatching compatibility; late import used inside handlers
- Wave 2 agents committed directly to main (permission lockdown in worktrees); recovery done inline
</decisions_made>
<blockers>
- Plan 08-08 requires user npm package verification on npmjs.com before execution (see plan frontmatter `user_setup` section)
</blockers>
## Required Reading (in order)
1. `.planning/phases/08-stack-upgrade-backend-decomposition/08-08-PLAN.md` — the only remaining plan; read the `user_setup` section carefully before executing
2. `CLAUDE.md` §"Backend: shared module map" and §"Frontend: shared module map" — non-negotiable rules
## Infrastructure State
- Docker services: not required for tests (SQLite used in tests)
- Full backend suite baseline: 405 passed, 1 skipped (python-docx not installed locally — pre-existing)
- All previous SUMMARY.md files committed
<context>
Session executed Wave 2 (plans 08-04 through 08-07) in parallel worktrees, hit permission lockdowns, recovered inline. All backend API monoliths decomposed. Frontend client.js barrel pattern in place. Wave 3 is one plan (08-08) gated on user npm supply-chain verification.
</context>
<next_action>
Start with: Ask user to verify npm packages on npmjs.com (see 08-08-PLAN.md `user_setup`), then run `/gsd:execute-phase 8` to execute plan 08-08.
</next_action>
@@ -1,10 +1,11 @@
---
phase: 8
slug: stack-upgrade-backend-decomposition
status: draft
nyquist_compliant: false
wave_0_complete: false
status: complete
nyquist_compliant: true
wave_0_complete: true
created: 2026-06-07
audited: 2026-06-12
---
# Phase 8 — Validation Strategy
@@ -24,7 +25,7 @@ created: 2026-06-07
| **Frontend framework** | Vitest 4.1.7 |
| **Frontend config** | `frontend/vitest.config.js` |
| **Frontend quick run** | `cd frontend && npm test` |
| **Estimated runtime** | ~30 seconds (backend), ~10 seconds (frontend) |
| **Estimated runtime** | ~60 seconds (backend), ~10 seconds (frontend) |
---
@@ -38,43 +39,42 @@ created: 2026-06-07
## Phase Requirements → Test Map
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|--------|----------|-----------|-------------------|-------------|
| CR-01 | `change_password` revokes other sessions, keeps current | unit/integration | `pytest tests/test_auth.py::test_change_password_revokes_other_sessions -x` | ❌ Wave 0 |
| CR-02 | `enable_totp` revokes other sessions, keeps current | unit/integration | `pytest tests/test_auth.py::test_enable_totp_revokes_other_sessions -x` | ❌ Wave 0 |
| CR-03 | `disable_totp` revokes other sessions, keeps current | unit/integration | `pytest tests/test_auth.py::test_disable_totp_revokes_other_sessions -x` | ❌ Wave 0 |
| CODE-01 | All admin URLs still respond on same paths after split | integration | `pytest tests/test_admin.py -x` | ✅ |
| CODE-02 | All document URLs still respond on same paths after split | integration | `pytest tests/test_documents.py -x` | ✅ |
| CODE-03 | All auth URLs still respond on same paths after split | integration | `pytest tests/test_auth.py -x` | ✅ |
| CODE-04 | All existing consumer imports resolve; no 35+ files changed | smoke | `cd frontend && npm test` | ✅ |
| CODE-08 | No model defined twice across packages | static/grep | `grep -rn "class CloudConnectionOut" backend/` | N/A |
| PERF-01 | npm list confirms all packages present | install verification | `cd frontend && npm list` | N/A |
| Req ID | Behavior | Test Type | Automated Command | Status |
|--------|----------|-----------|-------------------|--------|
| CR-01 | `change_password` revokes other sessions, keeps current | integration | `pytest tests/test_auth.py::test_change_password_revokes_other_sessions -x` | ✅ COVERED |
| CR-02 | `enable_totp` revokes other sessions, keeps current | integration | `pytest tests/test_auth.py::test_enable_totp_revokes_other_sessions -x` | ✅ COVERED |
| CR-03 | `disable_totp` revokes other sessions, keeps current | integration | `pytest tests/test_auth.py::test_disable_totp_revokes_other_sessions -x` | ✅ COVERED |
| CODE-01 | All admin URLs still respond on same paths after split | integration | `pytest tests/test_admin_api.py -x` | ✅ COVERED |
| CODE-02 | All document URLs still respond on same paths after split | integration | `pytest tests/test_documents.py -x` | ✅ COVERED |
| CODE-03 | All auth URLs still respond on same paths after split | integration | `pytest tests/test_auth.py -x` | ✅ COVERED |
| CODE-04 | All existing consumer imports resolve; no 35+ files changed | smoke | `cd frontend && npm test` | ✅ COVERED |
| CODE-08 | No model defined twice across packages | static/grep | `grep -rn "class CloudConnectionOut" backend/` | ✅ COVERED |
| PERF-01 | npm list confirms all packages present | install verification | `cd frontend && npm list` | ✅ COVERED |
---
## Wave 0 Gaps
## Wave 0 Gaps (Resolved)
New tests to write before decomposition work begins:
All stubs promoted to real passing tests:
- [ ] `backend/tests/test_auth.py`add 3 xfail stubs promoted to real tests for CR-01/02/03:
- `test_change_password_revokes_other_sessions`
- `test_enable_totp_revokes_other_sessions`
- `test_disable_totp_revokes_other_sessions`
- [ ] Frontend: verify `useToastStore` mock wiring in `SettingsAccountTab.test.js` and `TotpEnrollment.test.js` after stub creation
- [x] `backend/tests/test_auth.py:173``test_change_password_revokes_other_sessions` (PASSED)
- [x] `backend/tests/test_auth.py:217``test_enable_totp_revokes_other_sessions` (PASSED)
- [x] `backend/tests/test_auth.py:270` `test_disable_totp_revokes_other_sessions` (PASSED)
- [x] Frontend: `SettingsAccountTab.test.js` and `TotpEnrollment.test.js` — 136 frontend tests pass
---
## Critical Test Gates
| Gate | Command | Must pass before |
|------|---------|-----------------|
| Wave 0 stubs xfail | `pytest --tb=no -q` | Any decomposition work |
| Admin split | `pytest tests/test_admin.py -x -v` | Advancing past CODE-01 |
| Documents split | `pytest tests/test_documents.py -x -v` | Advancing past CODE-02 |
| Auth split | `pytest tests/test_auth.py -x -v` | Advancing past CODE-03 |
| Full backend suite | `pytest -v` | Phase gate |
| Frontend smoke | `npm test` | Phase gate |
| URL regression | `pytest tests/test_admin.py tests/test_documents.py tests/test_auth.py -v` | Phase complete |
| Gate | Command | Status |
|------|---------|--------|
| Wave 0 stubs xfail | `pytest --tb=no -q` | ✅ Complete |
| Admin split | `pytest tests/test_admin_api.py -x -v` | ✅ Passed |
| Documents split | `pytest tests/test_documents.py -x -v` | ✅ Passed |
| Auth split | `pytest tests/test_auth.py -x -v` | ✅ Passed |
| Full backend suite | `pytest -v` | ✅ 405 passed, 6 skipped, 7 xfailed |
| Frontend smoke | `npm test` | ✅ 136/136 passed |
| URL regression | `pytest tests/test_admin_api.py tests/test_documents.py tests/test_auth.py -v` | ✅ 58 passed |
---
@@ -83,6 +83,26 @@ New tests to write before decomposition work begins:
| Threat | Test | Evidence |
|--------|------|---------|
| Router prefix doubling | `pytest -v` — all existing route tests pass | URL paths unchanged |
| Admin endpoint auth leakage | `pytest tests/test_admin.py -k "not_admin"` | 403 on all admin routes for non-admin |
| Admin endpoint auth leakage | `pytest tests/test_admin_api.py -k "not_admin"` | 403 on all admin routes for non-admin |
| Circular import crash | App starts cleanly: `uvicorn main:app --reload` exits 0 | No ImportError |
| CloudConnectionOut import break | `pytest tests/test_cloud.py -x` | cloud.py imports succeed |
| CloudConnectionOut import break | `pytest tests/test_cloud.py -x` | Single definition in `backend/api/schemas.py` |
---
## Known Environment Note
`tests/test_extractor.py::test_extract_docx` fails with `ModuleNotFoundError: No module named 'docx'` in the local macOS Python 3.9 environment only. `python-docx` is in `requirements.txt` and runs correctly inside Docker. This is a pre-existing local environment gap unrelated to Phase 8.
---
## Validation Audit 2026-06-12
| Metric | Count |
|--------|-------|
| Gaps found (Wave 0) | 3 |
| Resolved | 3 |
| Escalated | 0 |
| Total requirements | 9 |
| COVERED | 9 |
| PARTIAL | 0 |
| MISSING | 0 |