wip: phase-4 paused at plan 04-09 — UX redesign + test suite + bug fixes complete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+41
-26
@@ -1,36 +1,51 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"timestamp": "2026-05-25T09:45:00Z",
|
"timestamp": "2026-05-28T15:02:40Z",
|
||||||
"phase": "3",
|
"phase": "4",
|
||||||
"phase_name": "03-document-migration-multi-user-isolation",
|
"phase_name": "Folders, Sharing, Quotas & Document UX",
|
||||||
"plan": "UAT",
|
"phase_dir": ".planning/phases/04-folders-sharing-quotas-document-ux",
|
||||||
"task": "UAT-3",
|
"plan": 9,
|
||||||
"total_tasks": 10,
|
"task": null,
|
||||||
|
"total_tasks": null,
|
||||||
"status": "paused",
|
"status": "paused",
|
||||||
"completed_tasks": [
|
"completed_tasks": [
|
||||||
{"id": "UAT-1", "name": "Cold Start Smoke Test", "status": "pass"},
|
{"id": 1, "name": "File manager UX redesign — FileManagerView.vue", "status": "done"},
|
||||||
{"id": "UAT-2", "name": "Upload with XHR progress bar", "status": "pass"},
|
{"id": 2, "name": "AppSidebar: Folders as clickable router-link to /", "status": "done"},
|
||||||
{"id": "UAT-4", "name": "Quota rejection error block", "status": "pass"},
|
{"id": 3, "name": "Root / shows root folders (loadFolder(null) fetches them)", "status": "done"},
|
||||||
{"id": "UAT-5", "name": "Quota decrements on document delete", "status": "pass"},
|
{"id": 4, "name": "Frontend test suite (Vitest) — folders store, FolderBreadcrumb, FolderTreeItem, FileManagerView", "status": "done"},
|
||||||
{"id": "UAT-6", "name": "Cross-user document isolation", "status": "pass"},
|
{"id": 5, "name": "Bug fix: @click.stop on folder name div blocked navigation", "status": "done"},
|
||||||
{"id": "UAT-7", "name": "Admin blocked from document content", "status": "pass"},
|
{"id": 6, "name": "Backend test suite — test_folders.py (35 tests)", "status": "done"},
|
||||||
{"id": "UAT-8", "name": "Topics namespace-scoped", "status": "pass"},
|
{"id": 7, "name": "Bug fix: duplicate folder name 409 for NULL parent_id (explicit ORM check)", "status": "done"},
|
||||||
{"id": "UAT-9", "name": "Settings page static placeholder", "status": "pass"},
|
{"id": 8, "name": "Bug fix: delete_folder CTE UUID format mismatch (.hex fix)", "status": "done"},
|
||||||
{"id": "UAT-10", "name": "AI classification uses per-user provider", "status": "pass"},
|
{"id": 9, "name": "Bug fix: quota UPDATE UUID format mismatch (.hex fix)", "status": "done"}
|
||||||
{"id": "BUG-3", "name": "MinIO hostname, CORS, admin commit, auth refresh race", "status": "done", "commit": "a5f202b"}
|
|
||||||
],
|
],
|
||||||
"remaining_tasks": [
|
"remaining_tasks": [
|
||||||
{"id": "UAT-3", "name": "QuotaBar displays in sidebar", "status": "pending_browser_confirmation"}
|
{"id": 10, "name": "Commit all uncommitted changes", "status": "not_started"},
|
||||||
|
{"id": 11, "name": "Phase 4 verification / UAT", "status": "not_started"},
|
||||||
|
{"id": 12, "name": "Phase 5 (pluggable cloud storage backends)", "status": "not_started"}
|
||||||
],
|
],
|
||||||
"blockers": [],
|
"blockers": [],
|
||||||
"human_actions_pending": [
|
"human_actions_pending": [],
|
||||||
{
|
"decisions": [
|
||||||
"action": "Log in as testuser, verify QuotaBar widget is visible in left sidebar showing used/total storage with an indigo fill bar.",
|
{"decision": "Remove @click.stop from folder name column wrapper div in FileManagerView", "rationale": "Parent @click handler returns null when renaming — stop is unnecessary and blocked navigation", "phase": "4"},
|
||||||
"context": "UAT-3: the only remaining test. All 9 others passed. Once confirmed, Phase 3 UAT is complete.",
|
{"decision": "Explicit ORM duplicate check before folder insert/rename instead of relying on IntegrityError", "rationale": "UniqueConstraint(user_id, parent_id, name) doesn't enforce uniqueness when parent_id IS NULL — SQL NULL != NULL semantics", "phase": "4"},
|
||||||
"blocking": true
|
{"decision": "Use uuid.hex (no dashes) in raw SQL CTE parameters", "rationale": "SQLite stores UUID as 32-char hex without dashes; str(uuid) gives dashes — CTE WHERE clause never matched", "phase": "4"}
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"uncommitted_files": [],
|
"uncommitted_files": [
|
||||||
"next_action": "Confirm UAT-3 (QuotaBar in sidebar). If pass, run /gsd:verify-work 3 or proceed to Phase 4.",
|
"backend/api/folders.py",
|
||||||
"context_notes": "9/10 UAT tests passed. All bugs fixed and committed (a5f202b). Only UAT-3 browser visual check remains."
|
"backend/tests/test_folders.py",
|
||||||
|
"frontend/src/views/FileManagerView.vue",
|
||||||
|
"frontend/src/components/layout/AppSidebar.vue",
|
||||||
|
"frontend/src/stores/folders.js",
|
||||||
|
"frontend/src/stores/documents.js",
|
||||||
|
"frontend/src/router/index.js",
|
||||||
|
"frontend/src/components/folders/FolderTreeItem.vue",
|
||||||
|
"frontend/src/components/folders/__tests__/",
|
||||||
|
"frontend/src/stores/__tests__/",
|
||||||
|
"frontend/src/views/__tests__/",
|
||||||
|
"frontend/vitest.config.js",
|
||||||
|
"frontend/package.json"
|
||||||
|
],
|
||||||
|
"next_action": "Run: git add -A && git commit. Then /gsd:verify-work 4 to validate phase 4 completion.",
|
||||||
|
"context_notes": "Phase 4 plans 04-01 through 04-09 all complete. File manager UX redesign done. Test suite created and all passing (55 frontend, 35 backend). Three bugs fixed. Ready to commit and verify phase."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
context: phase
|
||||||
|
phase: 04-folders-sharing-quotas-document-ux
|
||||||
|
task: null
|
||||||
|
total_tasks: null
|
||||||
|
status: paused
|
||||||
|
last_updated: 2026-05-28T15:02:40Z
|
||||||
|
---
|
||||||
|
|
||||||
|
# BLOCKING CONSTRAINTS — Read Before Anything Else
|
||||||
|
|
||||||
|
_No blocking constraints identified this session._
|
||||||
|
|
||||||
|
## Critical Anti-Patterns
|
||||||
|
|
||||||
|
| Pattern | Description | Severity | Prevention Mechanism |
|
||||||
|
|---------|-------------|----------|---------------------|
|
||||||
|
| UniqueConstraint + NULL parent | SQL UNIQUE constraints do not fire when a nullable column is NULL — duplicate root folder names were allowed | advisory | Always add explicit ORM duplicate check before insert/rename for nullable-column uniqueness |
|
||||||
|
| Raw SQL UUID format in SQLite | SQLite stores UUID as 32-char hex (no dashes); `str(uuid)` gives 36-char string with dashes — raw SQL WHERE comparisons silently fail | advisory | Use `uuid.hex` in raw SQL parameters; SQLAlchemy ORM handles this automatically |
|
||||||
|
|
||||||
|
<current_state>
|
||||||
|
Phase 4 plans 04-01 through 04-09 are all complete. The file manager UX redesign is done (unified FileManagerView, collapsible sidebar folder tree, breadcrumb, drag-drop move, root folder view). A full test suite was created and all tests pass. Three bugs were found and fixed this session. All changes are uncommitted and staged for a single commit.
|
||||||
|
</current_state>
|
||||||
|
|
||||||
|
<completed_work>
|
||||||
|
|
||||||
|
- Plans 04-01 through 04-09: all previously completed (see SUMMARY files)
|
||||||
|
- **This session:**
|
||||||
|
- File manager UX redesign: `FileManagerView.vue` (unified Explorer-style), `AppSidebar.vue` (Folders as clickable link to `/`), `FolderTreeItem.vue` (recursive sidebar tree), root view now fetches and displays root folders
|
||||||
|
- **Frontend test suite** (Vitest + @vue/test-utils): 55 tests across 4 files — `stores/__tests__/folders.test.js`, `components/folders/__tests__/FolderBreadcrumb.test.js`, `components/folders/__tests__/FolderTreeItem.test.js`, `views/__tests__/FileManagerView.test.js`
|
||||||
|
- **Bug fix**: `@click.stop` on folder name wrapper div in `FileManagerView.vue` was blocking all folder name click navigation — removed
|
||||||
|
- **Backend test suite**: `backend/tests/test_folders.py` — 35 tests covering FOLD-01 through FOLD-05 + security invariants
|
||||||
|
- **Bug fix**: Duplicate folder name 409 not firing for root folders (NULL parent_id) — added explicit ORM check in `create_folder` and `rename_folder`
|
||||||
|
- **Bug fix**: `delete_folder` CTE and quota UPDATE used `str(uuid)` (dashes) instead of `uuid.hex` — SQLite UUID mismatch caused cascade delete and quota decrement to silently do nothing
|
||||||
|
</completed_work>
|
||||||
|
|
||||||
|
<remaining_work>
|
||||||
|
|
||||||
|
- Commit all uncommitted changes (see files list below)
|
||||||
|
- Run `/gsd:verify-work 4` to validate phase 4 against requirements
|
||||||
|
- Phase 5: pluggable cloud storage backends
|
||||||
|
</remaining_work>
|
||||||
|
|
||||||
|
<decisions_made>
|
||||||
|
|
||||||
|
- Removed `@click.stop` from the folder name column div in `FileManagerView.vue` — parent's `@click` handler already returns null when renaming, so stop was only blocking navigation
|
||||||
|
- Explicit ORM duplicate check added before folder insert/rename — SQL UNIQUE constraints with NULL columns don't enforce uniqueness (NULL ≠ NULL in SQL)
|
||||||
|
- `uuid.hex` used in all raw SQL parameters — SQLite stores UUID without dashes, `str(uuid)` includes dashes; SQLAlchemy ORM handles format transparently but raw `text()` queries do not
|
||||||
|
- Vitest + @vue/test-utils + happy-dom chosen for frontend tests — matches the Vite build setup, no additional transpilation needed
|
||||||
|
</decisions_made>
|
||||||
|
|
||||||
|
<blockers>
|
||||||
|
None.
|
||||||
|
</blockers>
|
||||||
|
|
||||||
|
## Required Reading (in order)
|
||||||
|
1. `.planning/phases/04-folders-sharing-quotas-document-ux/04-09-SUMMARY.md` — last completed plan
|
||||||
|
2. `frontend/src/views/FileManagerView.vue` — unified file manager view
|
||||||
|
3. `backend/api/folders.py` — fixed folder endpoints
|
||||||
|
|
||||||
|
## Infrastructure State
|
||||||
|
- Docker Compose: all services running (backend :8000, frontend :5173, postgres, minio, redis)
|
||||||
|
- Frontend tests: `cd frontend && npx vitest run` → 55 passed
|
||||||
|
- Backend tests: `docker exec document_scanner-backend-1 python -m pytest tests/test_folders.py -v` → 35 passed
|
||||||
|
|
||||||
|
## Uncommitted Files
|
||||||
|
```
|
||||||
|
M backend/api/folders.py
|
||||||
|
M backend/tests/test_folders.py
|
||||||
|
M frontend/src/components/layout/AppSidebar.vue
|
||||||
|
M frontend/src/router/index.js
|
||||||
|
M frontend/src/stores/documents.js
|
||||||
|
M frontend/src/stores/folders.js
|
||||||
|
?? frontend/src/components/folders/FolderTreeItem.vue
|
||||||
|
?? frontend/src/components/folders/__tests__/
|
||||||
|
?? frontend/src/stores/__tests__/
|
||||||
|
?? frontend/src/views/FileManagerView.vue
|
||||||
|
?? frontend/src/views/__tests__/
|
||||||
|
?? frontend/vitest.config.js
|
||||||
|
?? frontend/package.json (vitest + @vue/test-utils added)
|
||||||
|
```
|
||||||
|
|
||||||
|
<context>
|
||||||
|
All Phase 4 plans are done. The session focused on UX polish (file manager redesign) and quality (test suite + bug fixes). The navigation bug was caused by an accidental @click.stop that blocked clicking folder names. Two backend data bugs were caused by SQL/SQLite UUID format differences. Everything is clean and ready to commit + verify.
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<next_action>
|
||||||
|
Start with: commit all changes with `git add -A && git commit -m "..."`, then run `/gsd:verify-work 4`.
|
||||||
|
</next_action>
|
||||||
Reference in New Issue
Block a user