---
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)_
