--- milestone: v1.0 audited: "2026-05-30T00:00:00Z" status: gaps_found scores: requirements: 44/54 phases_verified: 2/6 integration_blockers: 4 integration_warnings: 7 flows_complete: 3/6 gaps: requirements: - id: "SHARE-02" status: "partial" phase: "4" claimed_by_plans: ["04-04-PLAN.md", "06.1-01-PLAN.md"] completed_by_plans: ["06.1-01-PLAN.md"] verification_status: "gaps_found" evidence: "Two distinct bugs: (1) backend/api/documents.py line 542 checks doc.user_id != current_user.id and raises 404 — share recipients get 404 on GET /api/documents/{id} despite having a valid Share record; (2) SharedView.vue accesses share.document?.original_name, share.shared_by, share.document?.created_at but /api/shares/received returns a flat object with filename/owner_handle/created_at — all metadata fields render blank." - id: "DOC-01" status: "partial" phase: "4" claimed_by_plans: ["04-01-PLAN.md", "04-09-PLAN.md"] completed_by_plans: ["04-09-PLAN.md"] verification_status: "missing" evidence: "Owners can view document metadata and extracted text. Share recipients cannot — documents.py:542 enforces ownership-only check, returning 404 for recipients who navigate to /document/{id}. No share-grant lookup performed before the 404." - id: "SHARE-03" status: "partial" phase: "4" claimed_by_plans: ["04-04-PLAN.md", "06.1-01-PLAN.md"] completed_by_plans: ["06.1-01-PLAN.md"] verification_status: "gaps_found" evidence: "ShareCreate model has no permission field. grant_share hardcodes permission='view'. No PATCH /api/shares/{id} endpoint exists to change permission after creation. SHARE-03 requires 'owner controls permission level' — only the 'view-only default' half is satisfied. The stored permission field in the shares table cannot be changed through any API endpoint." - id: "SHARE-05" status: "partial" phase: "4" claimed_by_plans: ["04-04-PLAN.md", "06.1-01-PLAN.md"] completed_by_plans: ["06.1-01-PLAN.md"] verification_status: "gaps_found" evidence: "is_shared computed per document in documents.py lines 433-445 and 498-510 (two separate DB subqueries per list request). Zero occurrences of is_shared in any .vue or .js file. DocumentCard.vue has no visual indicator for shared documents. 06.1-VALIDATION added test_share_indicator_in_owner_list which confirms is_shared=True in the API response — but no frontend component reads or renders it." - id: "STORE-06" status: "partial" phase: "3" claimed_by_plans: ["03-02-PLAN.md"] completed_by_plans: ["03-02-SUMMARY.md (STORE-06)"] verification_status: "missing" evidence: "MinIO path: services/storage.py:168-175 implements atomic CASE WHEN quota decrement — correct for MinIO documents. Cloud path: delete_document() calls self._backend().delete_object(doc.object_key) where _backend() always returns MinIOBackend regardless of doc.storage_backend. Cloud-stored documents: (1) MinIO delete_object gets NoSuchKey (silently swallowed); (2) MinIO quota decremented even though no quota was charged at cloud upload; (3) actual file in Google Drive / OneDrive / Nextcloud / WebDAV is never deleted. Additionally, test_delete_decrements_quota is @pytest.mark.xfail(strict=False) — ROADMAP phase gate requires INTEGRATION=1 confirmation against live PostgreSQL." - id: "SEC-09" status: "partial" phase: "4" claimed_by_plans: ["04-07-PLAN.md", "05-05-PLAN.md"] completed_by_plans: ["04-07-SUMMARY.md", "05-05-SUMMARY.md"] verification_status: "partial" evidence: "Admin-initiated account deletion (admin.py lines 518-565) correctly purges all CloudConnection rows and calls delete_user_files() before MinIO+DB cleanup — SEC-09 satisfied for the account deletion path. However, user-initiated document deletion (services/storage.delete_document) does not call get_storage_backend_for_document — cloud provider files are orphaned when a user deletes a cloud-stored document." - id: "ADMIN-06" status: "partial" phase: "4" claimed_by_plans: ["04-06-PLAN.md", "06.1-02-PLAN.md"] completed_by_plans: ["06.1-02-PLAN.md"] verification_status: "gaps_found" evidence: "GET /api/admin/audit-log JSON viewer works end-to-end. Filter behavioral tests pass (test_audit_log_filter_by_event_type added in commit 451fff1). GET /api/admin/audit-log/export: AuditLogTab.vue:191 uses window.location.href which sends no Authorization: Bearer header. get_current_admin requires HTTPBearer — CSV export always returns 403." - id: "CLOUD-03" status: "partial" phase: "5" claimed_by_plans: ["05-06-PLAN.md"] completed_by_plans: ["05-06-SUMMARY.md"] verification_status: "human_needed" evidence: "PATCH /api/users/me/default-storage fully implemented (cloud.py:927, registered in main.py). updateDefaultStorage() exported from client.js:448. However, updateDefaultStorage() is never imported or called by any Vue component. SettingsCloudTab.vue renders cloud connections but has no radio/select to change the default storage backend. Users cannot change their default backend through the UI." - id: "FOLD-01" status: "partial" phase: "4" claimed_by_plans: ["04-03-PLAN.md"] completed_by_plans: ["04-03-PLAN.md"] verification_status: "missing" evidence: "_folder_to_dict() in folders.py:65 returns {id, name, parent_id, user_id, created_at} — no doc_count field. FolderDeleteModal.vue:31 and FolderRow.vue:32 display folder.doc_count ?? 0. FOLD-01 requires 'delete confirms content count before proceeding' — confirmation always shows '0 documents' regardless of actual folder content." - id: "FOLD-05" status: "partial" phase: "4" claimed_by_plans: ["04-03-PLAN.md"] completed_by_plans: ["04-03-PLAN.md"] verification_status: "missing" evidence: "SearchBar rendered with v-if='currentFolderId' in FileManagerView.vue — hidden at the root level (no folder selected). Users browsing the root document library have no search input. FOLD-05 requires 'Full-text search across user's documents' — search is only available when inside a folder, not at root." integration: - blocker: "SHARE-02 / DOC-01 — Share recipient blocked from document detail" description: "documents.py:542 checks doc.user_id != current_user.id → HTTPException(404). No share-grant lookup performed. Recipients with valid Share records cannot access GET /api/documents/{id}, breaking the shared document detail view. SharedView.vue also accesses wrong field paths (share.document?.original_name instead of share.filename etc.) — all metadata renders blank even on the list." - blocker: "STORE-06 / SEC-09 — Cloud document delete corrupts quota and orphans files" description: "services/storage.delete_document() calls self._backend().delete_object() where _backend() always returns MinIOBackend. Cloud-stored docs: MinIO delete_object silently fails (NoSuchKey), MinIO quota decremented unconditionally, actual cloud provider file never deleted." - blocker: "ADMIN-06 — Audit log CSV export always returns 403" description: "AuditLogTab.vue:191 uses window.location.href for CSV export. Browser navigation strips Authorization: Bearer header. Backend endpoint requires HTTPBearer. All CSV export clicks result in 403." - blocker: "CLOUD-03 — Default storage UI orphaned" description: "updateDefaultStorage() exported from client.js but never called by any component. No frontend UI exists to change the default storage backend." flows: - name: "Recipient views shared document detail" breaks_at: "documents.py:542 ownership-only check" affected_requirements: ["SHARE-02", "DOC-01"] - name: "User deletes cloud-stored document" breaks_at: "services/storage.delete_document() — MinIO backend hardcoded" affected_requirements: ["STORE-06", "SEC-09"] - name: "Admin exports audit log as CSV" breaks_at: "AuditLogTab.vue:191 window.location.href drops Bearer token" affected_requirements: ["ADMIN-06"] tech_debt: - phase: "01-infrastructure-foundation" items: - "No VERIFICATION.md exists (phase not formally verified by gsd-verifier)" - "VALIDATION.md: nyquist_compliant: true, audited 2026-05-30" - phase: "02-users-authentication" items: - "VERIFICATION.md exists (gaps_found 4/5) — SC5 gap closed by Phase 3, no re-verification run" - "No VALIDATION.md — Nyquist compliance MISSING for Phase 2" - "4 human verification items pending: TOTP enrollment e2e, password reset email, sign out all devices, admin panel visuals" - phase: "03-document-migration-multi-user-isolation" items: - "No VERIFICATION.md exists (phase not formally verified)" - "VALIDATION.md: nyquist_compliant: false, status: draft — Nyquist PARTIAL" - "Document.user_id ORM column has nullable=True but DB has NOT NULL constraint (migration 0003 alters it) — ORM/schema drift" - "test_delete_decrements_quota is xfail(strict=False) on SQLite — INTEGRATION=1 gate requires live PostgreSQL to confirm" - phase: "04-folders-sharing-quotas-document-ux" items: - "No VERIFICATION.md exists (phase not formally verified)" - "VALIDATION.md: nyquist_compliant: false, status: draft — Nyquist PARTIAL" - "AdminView.vue has no frontend role guard — unauthenticated-role users who navigate to /admin see full UI (all backend calls return 403 but no redirect occurs)" - "FOLD-01: _folder_to_dict() omits doc_count; delete confirmation always shows 0 documents" - "FOLD-05: SearchBar hidden at root level (v-if='currentFolderId')" - "SHARE-05: is_shared computed per document (2 DB subqueries) but never rendered in any Vue component" - "SHARE-03: permission hardcoded 'view', no PATCH endpoint to change it" - phase: "05-cloud-storage-backends" items: - "VERIFICATION.md: human_needed — 6 items require live cloud credentials (Google OAuth, OneDrive OAuth, live Nextcloud/WebDAV server)" - "VALIDATION.md: nyquist_compliant: true, audited 2026-05-30" - "CLOUD-05 REQUIRES_REAUTH transition implemented for OAuth providers only (Google Drive, OneDrive). Nextcloud/WebDAV credential failures produce generic 502 — no REQUIRES_REAUTH state for non-OAuth backends. Spec-compliant but UX gap." - "_doc_to_dict() omits storage_backend and folder_id — document list response cannot distinguish cloud vs local documents" - "CLOUD-03: updateDefaultStorage() exported but no UI element calls it" - phase: "06.1-close-v1-audit-gaps" items: - "VERIFICATION.md: stale (written before commit 451fff1 which added audit filter test). 06.1-VALIDATION.md supersedes." - "VALIDATION.md: nyquist_compliant: true, gaps_found: 3, gaps_resolved: 2, gaps_manual: 1" - "STORE-06 INTEGRATION=1 gate: manual-only — requires live PostgreSQL Docker stack to confirm" - "conftest.py WR-03: dependency_overrides not cleared on exception in async_client fixture (low-probability correctness gap)" - phase: "all" items: - "REQUIREMENTS.md checkboxes are stale — 22 satisfied requirements still show [ ]. Not maintained during execution." - "CLAUDE.md specifies ES256 JWT algorithm, email_hmac deterministic index, fgp token fingerprint claim — none implemented (HS256, plaintext email, no fingerprint). Outside 54 v1 REQ-IDs; v2 hardening scope." nyquist: compliant_phases: [1, 5, "6.1"] partial_phases: [3, 4] missing_phases: [2] overall: partial --- # 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 `BackgroundTasks` usages remain; all async work runs through Celery (`document_tasks.py`, `email_tasks.py`). - **DOC-02**: PDF proxy chain complete — `fetchDocumentContent()` → Bearer-authenticated `GET /api/documents/{id}/content` → `get_storage_backend_for_document()` → byte stream → blob URL. - **SEC-07**: `get_regular_user` raises 403 for admin role on all `/api/documents/*` endpoints — Phase 2 gap confirmed closed by Phase 3. - **SEC-08**: `CloudConnectionOut` whitelist (provider, display_name, connected_at, status only) used at `cloud.py:637,661` — `credentials_enc` excluded 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)_