diff --git a/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-VERIFICATION.md b/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-VERIFICATION.md new file mode 100644 index 0000000..f051ee7 --- /dev/null +++ b/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-VERIFICATION.md @@ -0,0 +1,165 @@ +--- +phase: "06.2-close-v1-sharing-cloud-delete-csv-export-gaps" +verified: "2026-05-31T12:00:00Z" +status: human_needed +score: 5/5 +overrides_applied: 0 +human_verification: + - test: "Security gate — run bandit -r backend/ and confirm zero HIGH severity findings" + expected: "bandit reports zero HIGH severity issues in modified files (shares.py, audit.py, documents.py, storage.py)" + why_human: "bandit requires the full Python environment with all deps installed in a compatible Python 3.10+ environment; local Python is 3.9.6 which hits Google API core version warnings" + - test: "Security gate — run pip audit and npm audit --audit-level=high" + expected: "Zero critical/high CVEs from pip audit; zero high/critical from npm audit" + why_human: "Requires running in the project's Docker environment; cannot verify without network access and correct env setup" + - test: "Cloud delete modal UX flow — delete a cloud-stored document in the browser" + expected: "When cloud delete fails, a modal appears with 'Cloud delete failed' heading and 'Remove from app' / 'Cancel' buttons; clicking 'Remove from app' deletes the DB row and navigates to /" + why_human: "Real-time modal behavior with cloud provider mock requires browser interaction" + - test: "ShareModal permission toggle — open the sharing modal for a shared document" + expected: "Each shared recipient row shows two toggle buttons ('View' / 'Edit'); active button has indigo highlight; clicking inactive button sends PATCH and shows updated state optimistically" + why_human: "Visual state and optimistic-update behavior require browser interaction" + - test: "AuditLogTab daily exports section — open admin audit log panel" + expected: "Daily exports section visible below pagination; when no MinIO exports exist shows 'No daily exports available.'; when exports exist shows date dropdown + Download button" + why_human: "Requires admin account in running app with MinIO populated by Celery daily export task" +--- + +# Phase 06.2: Close v1 Sharing + Cloud-Delete + CSV Export Gaps — Verification Report + +**Phase Goal:** Close remaining v1 gaps — sharing edge cases (SHARE-03/SHARE-05), cloud document deletion propagation to the remote backend, and CSV export + daily export UI for the admin audit log (ADMIN-06). +**Verified:** 2026-05-31T12:00:00Z +**Status:** human_needed +**Re-verification:** No — initial verification + +## Goal Achievement + +### Observable Truths (from ROADMAP.md Success Criteria) + +| # | Truth | Status | Evidence | +|---|-------|--------|---------| +| 1 | Documents shared with others display a "Shared" badge reading `doc.is_shared`, not `doc.share_count` | VERIFIED | `DocumentCard.vue` line 31: `v-if="doc.is_shared"`. `grep "share_count" DocumentCard.vue` returns no match. Backend `list_documents` populates `is_shared` from `Share.document_id` set query. | +| 2 | Owner can set permission to "view" or "edit" at share creation and toggle per-recipient; PATCH /api/shares/{id} enforces IDOR (404 on wrong owner) | VERIFIED | `shares.py`: `ShareCreate.permission` field with `field_validator`; `grant_share` uses `permission=body.permission`; `PATCH /{share_id}` handler at line 246 with `share.owner_id != current_user.id → 404`. `test_share_patch_idor` passes (confirmed by test run: 50 passed, 4 xfailed). | +| 3 | Deleting a cloud document propagates to cloud provider; failure shows warning modal with "Remove from app" fallback; `?remove_only=true` removes only the DB record; cloud docs never affect quota on delete | VERIFIED | `documents.py` lines 638-654: cloud routing block calls `get_storage_backend_for_document` then `backend.delete_object`; on exception returns `JSONResponse(200, {cloud_delete_failed: True})`; `remove_only=true` skips cloud call; `skip_quota=is_cloud` guards quota decrement. `DocumentView.vue` line 114: `v-if="showCloudDeleteWarning"` modal with `confirmRemoveOnly` handler. Three promoted tests pass. | +| 4 | Admin can download filtered audit log CSV via fetch+Blob (not `window.location.href`); audit log entries show user handles; user filter accepts handles (not UUIDs) | VERIFIED | `adminExportAuditLogCsv()` in `client.js` uses raw `fetch()` + `Blob()` + `` click — no `window.location.href`. `grep "window.location.href" AuditLogTab.vue` → no match. `audit.py`: `list_audit_log` accepts `user_handle: Optional[str]`, resolves to UUID internally; returns `user_handle` and `actor_handle` fields in every item via `_audit_to_dict_with_handles`. Five promoted tests pass. | +| 5 | Admin can list and download Celery daily audit export files from a new section in the Audit Log tab | VERIFIED | `audit.py` lines 168-239: two new endpoints `GET /audit-log/daily-exports` and `GET /audit-log/daily-exports/{date}`. `AuditLogTab.vue` lines 129-165: "Daily exports" section with date `