feat: zero-touch library mode with pending review queue#2
feat: zero-touch library mode with pending review queue#2jeffcrouse merged 15 commits intomasterfrom
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove metadata file writer, organizer execution, import file operations, in-place flac remux, streaming source mutation, destructive dedup execute, and proposed changes file-write scopes. All enrichment is now DB-only. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New tracks enter PENDING_REVIEW status instead of ACTIVE. Adds duplicate detection service, pending review API with approve/skip/replace actions, and ACTIVE-only filters on track listing endpoints. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove import modal, drop zone, and file-write UI controls. Add PendingReviewBrowser with grouped track review, bulk approve/skip, queue-analysis toggle, per-track and group metadata editors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PostgreSQL requires ALTER TYPE ADD VALUE to be committed before the new value can be referenced. Without this, the partial index creation fails with UnsafeNewEnumValueUsageError. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The DB stores these as JSON arrays but the Pydantic model only accepted dict, causing a 500 on GET /spotify/import and hiding the sidebar item. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The sidebar linked to /library/pending-review but no route existed, causing a redirect to the artists view via the catch-all route. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend: smoke-test GET endpoints with seeded data to catch Pydantic response model mismatches (e.g. dict vs list). Includes regression test for the Spotify import serialization bug. Frontend: verify sidebar items, routes, and browser registrations stay in sync. Extracts route/sidebar data to routes.ts to enable testing without importing the React component tree. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cast scope to text in remove_file_write_scopes migration so it works on fresh DBs where the old enum values never existed - Add one-way comment to bare downgrade pass - Remove unused TrackStatus imports flagged by ruff - Fix noqa placement in preview.py Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7511b152b1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| track.status = TrackStatus.PENDING_REVIEW | ||
| # Run duplicate detection for review_info | ||
| from app.services.duplicate_detection import detect_duplicate_for_track | ||
| track.review_info = await detect_duplicate_for_track(self.db, track) | ||
| results["pending_review"] += 1 |
There was a problem hiding this comment.
Keep new review-queue tracks out of the analysis queue
Marking newly discovered files as PENDING_REVIEW here does not actually prevent analysis from running: backend/app/services/tasks/analysis_queue.py:39-51 still selects every track without a TrackAnalysis row, regardless of status. A normal library sync will therefore analyze unapproved (and even later-skipped) tracks before the user reviews them, which defeats the zero-touch workflow and spends analysis capacity on files that may never be imported.
Useful? React with 👍 / 👎.
| prefix = folder_path.rstrip("/") + "/" | ||
| result = await db.execute( | ||
| select(Track).where( | ||
| Track.status == TrackStatus.PENDING_REVIEW, | ||
| Track.file_path.like(prefix + "%"), | ||
| # Ensure we only get files directly in this folder (not subfolders) | ||
| ~Track.file_path.like(prefix + "%/%"), |
There was a problem hiding this comment.
Escape folder names before using LIKE in group review actions
folder_path is interpolated directly into two SQL LIKE patterns, so real directory names containing % or _ are treated as wildcards. In folders such as AC_DC or 100% Hits, the group approve/skip/replace endpoints can match tracks from sibling folders that were never selected in the UI and change their status as part of the bulk action.
Useful? React with 👍 / 👎.
| { path: '/library/proposed-changes', label: 'Changes' }, | ||
| { path: '/library/pending-review', label: 'Review' }, |
There was a problem hiding this comment.
Expose the review queue in mobile navigation too
This adds /library/pending-review to the shared route list, but I checked packages/frontend/src/components/MobileNav/MobileMoreSheet.tsx:28-35 and the mobile library menu still hard-codes the old entries. On the iOS/Capacitor build that means the new review queue has no navigation path, so newly discovered tracks cannot be approved from the mobile UI without manually entering the URL.
Useful? React with 👍 / 👎.
…ation sync - Stage all ruff auto-fixes (unused imports across 13 files) - Update scanner tests: new tracks are pending_review, not new - Fix scanner: mark PENDING_REVIEW tracks as missing when file disappears - Fix scanner: recover to PENDING_REVIEW or ACTIVE based on review_info - Fix test_track_by_id_serializes: /tracks/batch is POST, not GET - Add ix_tracks_pending_review to manual indexes in env.py - Remove test for deleted /library/imports/recent endpoint - Add test_contract_happy_path.py to Core test suite ignore list Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mport
- Fix mypy: type-ignore rowcount on Result, inline save_upload_to_temp
- Fix scanner: set review_info to {} (not None) for non-duplicate pending
tracks so recovery heuristic works correctly
- Fix scanner test: recovered pending track stays PENDING_REVIEW
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Profile contract lint requires all POST/PATCH endpoints to include the RequiredProfile dependency. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Filter analysis queue to ACTIVE tracks only, preventing PENDING_REVIEW tracks from consuming analysis capacity before approval - Escape SQL LIKE wildcards in folder paths to prevent matching unrelated folders with % or _ characters (e.g. AC_DC, 100% Hits) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dexie !: property declarations don't create runtime properties until the DB opens. Use db.tables.map(t => t.name) which reflects the declared schema regardless of open state. Fixes pre-existing CI failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
PENDING_REVIEWstatus instead of appearing directly in the library. Users review, edit metadata, and approve/skip/replace from a new PendingReviewBrowser UI.Test plan
🤖 Generated with Claude Code