- CLAUDE.md: add Code Standards section with backend and frontend shared module maps, component architecture rules, duplication checklist, and no-dead-code enforcement rule - SECURITY.md: Phase 02 + 03 security audit results (all threats CLOSED) - .planning: update milestone audit, config, and add plan/UAT files for phases 01, 02-06, and 06.2-05 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
26 KiB
milestone, audited, status, scores, gaps, tech_debt, nyquist
| milestone | audited | status | scores | gaps | tech_debt | nyquist | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| v1.0 | 2026-05-30T00:00:00Z | gaps_found |
|
|
|
|
DocuVault v1.0 — Milestone Audit Report
Milestone: v1.0 Audited: 2026-05-30 Phases audited: 1, 2, 3, 4, 5, 6.1 (Phase 6 not started — excluded) Status: ⚠ GAPS FOUND
Executive Summary
All 6 planned v1 phases executed and marked complete. Phase 5 formally verified (7/7 truths, human_needed). Phase 2 VERIFICATION.md status=gaps_found (4/5); gap confirmed closed by Phase 3. Phases 1, 3, 4 have no VERIFICATION.md. Phase 6.1 VALIDATION.md supersedes its stale VERIFICATION.md.
The integration check found 4 blockers and 7 warnings. Ten requirements are partially satisfied, primarily due to frontend wiring gaps and a cloud-delete path defect.
| Metric | Score |
|---|---|
| Requirements satisfied | 44/54 (81%) |
| Requirements partial | 10/54 (19%) |
| Requirements unsatisfied | 0/54 |
| Phases formally verified | 2/6 (Phases 2, 5) |
| Nyquist compliant | 3/6 phases |
| Test gate | 309 passed, 1 pre-existing failure (test_extract_docx — missing python-docx module; unrelated to milestone scope) |
Requirements Coverage
Satisfied (44/54)
| Phase | REQ-IDs | Count |
|---|---|---|
| 1 — Infrastructure | STORE-01, STORE-02, STORE-07 | 3 |
| 2 — Auth | AUTH-01, AUTH-02, AUTH-03, AUTH-04, AUTH-05, AUTH-06, AUTH-07, AUTH-08, SEC-01, SEC-02, SEC-03, SEC-05, SEC-06, SEC-07, ADMIN-01, ADMIN-02, ADMIN-03, ADMIN-04, ADMIN-05, ADMIN-07 | 20 |
| 3 — Documents | STORE-03, STORE-04, STORE-05, STORE-08, SEC-04, DOC-03, DOC-04, DOC-05 | 8 |
| 4 — Folders/Sharing | FOLD-02, FOLD-03, FOLD-04, SHARE-01, SHARE-04, SEC-08, DOC-02 | 7 |
| 5 — Cloud | CLOUD-01, CLOUD-02, CLOUD-04, CLOUD-05, CLOUD-06, CLOUD-07 | 6 |
Total satisfied: 44
Notable confirmations from integration check:
- STORE-08: Zero
BackgroundTasksusages remain; all async work runs through Celery (document_tasks.py,email_tasks.py). - DOC-02: PDF proxy chain complete —
fetchDocumentContent()→ Bearer-authenticatedGET /api/documents/{id}/content→get_storage_backend_for_document()→ byte stream → blob URL. - SEC-07:
get_regular_userraises 403 for admin role on all/api/documents/*endpoints — Phase 2 gap confirmed closed by Phase 3. - SEC-08:
CloudConnectionOutwhitelist (provider, display_name, connected_at, status only) used atcloud.py:637,661—credentials_encexcluded from all responses.
Partial (10/54) — Blockers and Warnings
| REQ-ID | Phase | Severity | Root Cause |
|---|---|---|---|
| SHARE-02 | 4 | BLOCKER | Recipients get 404 on GET /api/documents/{id} (ownership check only). SharedView.vue field names wrong (blank metadata display). |
| DOC-01 | 4 | BLOCKER | Owners: ✅. Share recipients: 404 at documents.py:542 (doc.user_id != current_user.id, no share-grant check). |
| STORE-06 | 3 | BLOCKER | MinIO delete-path correct. Cloud delete-path: MinIO backend called unconditionally → quota corrupted, cloud file orphaned. |
| SEC-09 | 4 | BLOCKER | Admin account deletion: ✅. User-initiated document delete: cloud provider file not deleted (only MinIO attempted). |
| ADMIN-06 | 4 | BLOCKER | JSON audit viewer: ✅. CSV export: window.location.href drops Bearer header → 403. |
| SHARE-03 | 4 | WARNING | permission="view" hardcoded. No PATCH /api/shares/{id} endpoint. Owner cannot change permission level. |
| SHARE-05 | 4 | WARNING | is_shared computed per document (2 DB subqueries / request) but never rendered by any Vue component. |
| CLOUD-03 | 5 | WARNING | PATCH /api/users/me/default-storage implemented. updateDefaultStorage() exported but no UI calls it. |
| FOLD-01 | 4 | WARNING | _folder_to_dict() omits doc_count. Delete confirmation modal always shows "0 documents". |
| FOLD-05 | 4 | WARNING | SearchBar hidden at root (v-if="currentFolderId"). Full-text search unavailable in root document library. |
Phase Verification Status
| Phase | VERIFICATION.md | Status | Notes |
|---|---|---|---|
| 01 — Infrastructure Foundation | ❌ MISSING | Unverified | Phase marked complete; no formal verification run |
| 02 — Users & Authentication | ✅ exists | gaps_found (4/5) | SC5 gap (admin JWT → 403 on docs) closed by Ph3; confirmed by integration check |
| 03 — Document Migration | ❌ MISSING | Unverified | Phase marked complete; no formal verification run |
| 04 — Folders, Sharing, Quotas | ❌ MISSING | Unverified | Phase marked complete; no formal verification run |
| 05 — Cloud Storage Backends | ✅ exists | human_needed (7/7) | All must-haves verified; 6 human UAT items require live cloud credentials |
| 6.1 — Gap Closure | ✅ exists (stale) | Superseded by VALIDATION.md | 06.1-VALIDATION.md: gaps_found 3, resolved 2, manual 1 |
Nyquist Coverage
| Phase | VALIDATION.md | nyquist_compliant | Action |
|---|---|---|---|
| 01 — Infrastructure Foundation | ✅ audited 2026-05-30 | true |
None |
| 02 — Users & Authentication | ❌ MISSING | — | /gsd:validate-phase 2 |
| 03 — Document Migration | ✅ exists, status: draft | false |
/gsd:validate-phase 3 |
| 04 — Folders, Sharing, Quotas | ✅ exists, status: draft | false |
/gsd:validate-phase 4 |
| 05 — Cloud Storage Backends | ✅ audited 2026-05-30 | true |
None |
| 6.1 — Gap Closure | ✅ audited 2026-05-30 | true |
None |
Critical Blockers (5)
BLOCKER-1 — Share Recipient Cannot View Document Metadata (SHARE-02, DOC-01)
File: backend/api/documents.py line 542
Root cause: if doc is None or doc.user_id != current_user.id: raise HTTPException(404) — no share-grant check.
Broken flow: SharedView.vue → click shared item → DocumentView.vue → getDocument(id) → 404 for recipient despite valid Share record.
Secondary bug: SharedView.vue accesses share.document?.original_name, share.shared_by, share.document?.created_at but /api/shares/received returns a flat object (filename, owner_handle, created_at). All metadata renders blank even on the list.
Fix (backend): In get_document(), after the ownership 404, add: check Share table for (document_id=doc_id, recipient_id=current_user.id) and allow if found.
Fix (frontend): In SharedView.vue, update field access to match flat response shape.
BLOCKER-2 — Cloud Document Delete Corrupts Quota and Orphans Files (STORE-06, SEC-09)
File: backend/services/storage.py (delete_document function)
Root cause: self._backend().delete_object(doc.object_key) always uses MinIOBackend regardless of doc.storage_backend. Then decrements MinIO quota unconditionally.
Impact: Cloud-stored documents: (1) MinIO delete_object gets NoSuchKey (silently swallowed), (2) MinIO quota decremented below actual usage, (3) actual cloud provider file never deleted → GDPR Article 17 obligation not met for cloud storage.
Fix: Use get_storage_backend_for_document(doc, session) in delete_document(). Gate quota decrement on doc.storage_backend == "minio".
BLOCKER-3 — Admin Audit Log CSV Export Always Returns 403 (ADMIN-06)
File: frontend/src/components/admin/AuditLogTab.vue line 191
Root cause: window.location.href = '/api/admin/audit-log/export?${params}' — browser navigation strips the Authorization: Bearer header. get_current_admin requires HTTPBearer.
Fix: Replace window.location.href with fetch() using Authorization: Bearer ${accessToken}, then create a Blob URL for download. The fetchDocumentContent() pattern in client.js is the correct model.
BLOCKER-4 — Default Storage Backend Has No Frontend UI (CLOUD-03)
File: frontend/src/components/settings/SettingsCloudTab.vue
Root cause: updateDefaultStorage() is exported from client.js:448 but never imported or called by any component. SettingsCloudTab.vue has no UI control to select a default backend.
Fix: Add a "Set as default" button or radio to each connected provider row in SettingsCloudTab.vue; wire it to updateDefaultStorage(provider).
Warnings (7)
| # | Description | Requirement |
|---|---|---|
| W-1 | is_shared computed per document (2 subqueries per list request) but no Vue component renders it |
SHARE-05 |
| W-2 | SHARE-03: permission hardcoded to "view"; no PATCH /api/shares/{id} endpoint |
SHARE-03 |
| W-3 | _folder_to_dict() omits doc_count — delete confirmation modal always shows "0 documents" |
FOLD-01 |
| W-4 | SearchBar hidden at root level (v-if="currentFolderId") — search unavailable in root library |
FOLD-05 |
| W-5 | Document.user_id ORM column nullable=True; DB has NOT NULL constraint (migration 0003) — ORM/schema drift |
STORE-03 |
| W-6 | AdminView.vue has no frontend role guard — regular users who navigate to /admin see full UI; backend returns 403 but no redirect |
— |
| W-7 | CLAUDE.md specifies ES256 JWT, email_hmac index, fgp fingerprint claim — none implemented (HS256, plaintext email, no fingerprint). v2 hardening scope, outside 54 v1 REQ-IDs. |
v2 |
E2E Flow Results
| Flow | Status | Break Point |
|---|---|---|
| MinIO upload → quota updated → Celery AI classification | ✅ COMPLETE | — |
| Password reset → TOTP gate on next login | ✅ COMPLETE | — |
| Cloud upload → authenticated content proxy (blob URL) | ✅ COMPLETE | — |
| Share document → "Shared with me" list → recipient views detail | ❌ BROKEN | documents.py:542 ownership-only check + SharedView field mismatch |
| User deletes cloud-stored document → files purged | ❌ BROKEN | delete_document() hardcodes MinIOBackend |
| Admin views audit log → exports CSV | ⚠️ PARTIAL | JSON viewer works; CSV export → 403 (no Bearer in window.location.href) |
Integration Wiring Summary (47 connections)
| Connection | Status |
|---|---|
Auth deps (get_regular_user / get_current_admin) on all Phase 3-5 endpoints |
✅ All wired (verified across documents.py, folders.py, shares.py, cloud.py, audit.py) |
Phase 2 admin gap (admin JWT → 403 on /api/documents/*) |
✅ Closed in Phase 3 by get_regular_user |
| Atomic quota at upload (MinIO path) | ✅ Wired (documents.py:342-346) |
| Atomic quota decrement at delete (MinIO path) | ✅ Wired (services/storage.py:168-175) |
| Atomic quota decrement at delete (cloud path) | ❌ Not wired — MinIOBackend hardcoded |
| Cloud document content proxy (authenticated fetch → blob URL) | ✅ Wired |
| Admin delete: cloud cleanup → MinIO cleanup → DB delete | ✅ Wired (admin.py:518-565) |
| User-initiated doc delete: cloud provider cleanup | ❌ Not wired |
Share recipient access to /api/documents/{id}/content |
✅ Wired (content proxy uses get_storage_backend_for_document) |
Share recipient access to GET /api/documents/{id} metadata |
❌ Ownership check blocks recipients |
| Admin audit log JSON viewer | ✅ Wired end-to-end |
| Admin audit log CSV export | ❌ Bearer header dropped by window.location.href |
| Default storage backend selection UI | ❌ updateDefaultStorage() orphaned — no UI calls it |
| HKDF credential encryption through all cloud flows | ✅ Wired |
write_audit_log() called from documents (3), shares (2), folders (3), cloud (4) |
✅ All wired |
All API routers registered in main.py |
✅ Confirmed |
get_storage_backend_for_document() factory in content proxy + Celery task |
✅ Wired |
SHARE-05 is_shared computed in API → frontend |
❌ Computed, never rendered |
Tech Debt Summary
Phase 01: No VERIFICATION.md. VALIDATION.md compliant.
Phase 02: VERIFICATION.md gaps_found (Phase 2 SC5 closed by Phase 3). No VALIDATION.md (Nyquist MISSING for Phase 2). 4 human verification items pending.
Phase 03: No VERIFICATION.md. VALIDATION.md draft (Nyquist PARTIAL). Document.user_id ORM nullable vs DB NOT NULL drift. test_delete_decrements_quota xfail — INTEGRATION=1 required.
Phase 04: No VERIFICATION.md. VALIDATION.md draft (Nyquist PARTIAL). AdminView.vue missing frontend role guard. Multiple UI gaps (FOLD-01, FOLD-05, SHARE-05, SHARE-03, SHARE-02 field names).
Phase 05: VERIFICATION.md human_needed (must-haves all confirmed). VALIDATION.md compliant. CLOUD-03 UI orphaned. CLOUD-05 REQUIRES_REAUTH only for OAuth providers.
Phase 06.1: VERIFICATION.md stale (superseded). VALIDATION.md compliant. STORE-06 manual gate pending. conftest.py WR-03 teardown gap.
Cross-cutting: REQUIREMENTS.md checkboxes not maintained (22 satisfied reqs still show [ ]). CLAUDE.md v2 hardening items (ES256, email_hmac, fgp fingerprint) not yet implemented.
Remediation Guide
Run Nyquist validation first (may close some verification gaps retroactively):
/gsd:validate-phase 2
/gsd:validate-phase 3
/gsd:validate-phase 4
Then insert closure phases for remaining blockers:
/clear then:
/gsd:phase --insert 6.2 "Close v1 sharing + cloud-delete + CSV export gaps"
/gsd:discuss-phase 6.2
/gsd:plan-phase 6.2
/gsd:execute-phase 6.2
Suggested scope for Phase 6.2 (all small fixes, could ship as one phase):
| Fix | Files | Effort | REQ-IDs |
|---|---|---|---|
Share recipient access: add share-grant check to get_document() |
documents.py:542 |
~15 lines | SHARE-02, DOC-01 |
| Fix SharedView.vue field names | SharedView.vue |
~10 lines | SHARE-02 |
Cloud delete: use get_storage_backend_for_document() + gate quota decrement |
services/storage.py |
~25 lines | STORE-06, SEC-09 |
| Audit CSV export: fetch() + Bearer + blob download | AuditLogTab.vue:191 |
~20 lines | ADMIN-06 |
| Default storage UI: "Set as default" button in SettingsCloudTab | SettingsCloudTab.vue |
~30 lines | CLOUD-03 |
Add is_shared indicator to DocumentCard.vue |
DocumentCard.vue |
~15 lines | SHARE-05 |
Add doc_count to _folder_to_dict() |
folders.py:65 |
~10 lines | FOLD-01 |
Remove v-if="currentFolderId" gate from SearchBar |
FileManagerView.vue |
~5 lines | FOLD-05 |
Add PATCH /api/shares/{id} permission endpoint |
shares.py |
~30 lines | SHARE-03 |
| Add frontend role guard to AdminView route or component | router/index.js or AdminView.vue |
~10 lines | — |
Confirm test_delete_decrements_quota under INTEGRATION=1 |
test_quota.py:196 |
manual | STORE-06 |
Also available:
cat .planning/v1.0-MILESTONE-AUDIT.md— this report/gsd:complete-milestone v1.0— proceed with gaps noted (accept as tech debt)
Audited: 2026-05-30 Auditor: Claude (gsd-audit-milestone) Integration checker: gsd-integration-checker (155 tool calls, 47 connections verified)