6.6 KiB
Drop verify/checkmarks, merge jobs view into item details
Date: 2026-04-15
Summary
Remove the post-job Jellyfin verification path and its associated verified flag entirely. Delete the standalone /execute jobs page. Surface per-item job info (status, command, log, run/cancel actions) on the item details page instead. Batch queue controls (Run all / Clear) move into the Pipeline column headers.
Rescan becomes the sole source of truth for "is this file still done?" — if a file drifts off-noop, the next scan flips its plan back to pending and the card reappears in Review.
Motivation
The verify feature tried to promote done cards from ✓ to ✓✓ after ffprobe/Jellyfin cross-checked the on-disk file. In practice the Jellyfin refresh path is fragile (times out silently), the ✓/✓✓ distinction adds UI noise without user value, and rescan already catches drift. The separate Jobs page duplicates info that belongs on the item details page and forces users to jump between views to answer "what happened to this file?".
Backend changes
Remove verification path
- Delete
handOffToJellyfin()inserver/api/execute.ts(≈lines 38–98) and both callers at:492and:609. Post-job handling is now just the existingjobs.statusupdate. - Delete
emitPlanUpdate()and theplan_updateSSE event emission. - Delete
POST /api/execute/verify-unverified(≈lines 357–389).
Drop verified column
- Add idempotent migration in
server/db/index.tsfollowing the existing try/catchalter()pattern:Supported on Bun's bundled SQLite (≥3.35).alter("ALTER TABLE review_plans DROP COLUMN verified"); - Remove
verifiedfromserver/db/schema.ts:77in thereview_plansCREATE TABLE. - In
server/services/rescan.ts, removeverifiedfrom the INSERT column list and theverified = CASE ...branch in the ON CONFLICT DO UPDATE clause. - In
server/api/review.ts:- Remove
rp.verifiedfrom the pipeline SELECT (≈line 330). - Remove
verified = 0from the unapprove UPDATE (≈line 773).
- Remove
Remove jobs-list endpoint
- Delete
GET /api/execute(the filtered list used only by the Execute page). - Keep:
/start,/clear,/clear-completed,/job/:id/run,/job/:id/cancel,/stop,/events, SSE eventsjob_update,job_progress,queue_status.
Enrich item details endpoint
- Extend
GET /api/review/:idto include the latest job row for this item (if any):job: { id: number; status: Job["status"]; job_type: "copy" | "transcode"; command: string | null; output: string | null; exit_code: number | null; started_at: string | null; completed_at: string | null; } | null - "Latest" = most recent by
jobs.created_at DESC LIMIT 1for the item. A single additional prepared statement.
Frontend changes
Deletions
src/features/execute/ExecutePage.tsxsrc/routes/execute.tsx- Nav link to
/executeinsrc/routes/__root.tsx plan_updateSSE listener insrc/features/pipeline/PipelinePage.tsx:70-72- Verify button,
verifyUnverified(),unverifiedCount, and the✓/✓✓glyph span insrc/features/pipeline/DoneColumn.tsx - The
verifiedfield onPipelineJobIteminsrc/shared/lib/types.ts:161
DoneColumn simplification
Each card keeps its title link, ← Back to review hover button, and status <Badge> (done or error). The mark glyph and its title attribute go away. Column actions stay: Clear when items exist.
Pipeline column headers (batch controls)
ColumnShell already accepts an actions: ColumnAction[] array. Move existing batch controls off the /execute page into the headers:
- Queued column —
Run all(primary, when at least one pending) +Clear queue(when items exist) - Done column —
Clear(existing) - Processing column — no batch controls
Wire these to the existing endpoints: /api/execute/start, /api/execute/clear, /api/execute/clear-completed.
AudioDetailPage job section
New section rendered only when data.job is non-null. Placement: between the FFmpeg command textarea and the Approve/Skip button row.
Contents:
- Header row: status
<Badge>, job-type badge (Audio Remux/Audio Transcode), started/completed timestamps, exit code badge (only when non-zero) Cmdtoggle button — reveals the job's recorded command (thejobs.commandcolumn)Logtoggle button — revealsjobs.output; auto-expanded whenstatus === "error"- Action buttons based on
job.status:pending→▶ Run(callsPOST /api/execute/job/:id/run),✕ Cancel(callsPOST /api/execute/job/:id/cancel)running→✕ Stop(callsPOST /api/execute/stop)done/error→ no actions
Live updates on details page
The details page gets its own scoped EventSource subscription to /api/execute/events, filtering for events where id === data.job?.id:
job_update→ merge into local state, re-fetch details on terminal (done/error) to pick up the refreshedjobsrowjob_progress→ update a progress bar for the active job- Close on unmount
Data flow after the change
- User approves plan in Review → plan.status = approved
- User clicks
Run allin Queued column header → queued jobs start - Processing column shows the running job with live progress (unchanged)
- Job finishes →
jobs.status = done,review_plans.status = done. No Jellyfin refresh, no verified flip. - Card lands in Done column with a
donebadge. No ✓/✓✓ glyph. - Next scan (automatic or manual) re-analyzes the file. If still
is_noop = 1, plan staysdone; if not, plan returns topendingand the card reappears in Review.
Testing
- Delete
server/services/__tests__/webhook.test.ts:186-240— the "webhook_verified flag" describe block. The remaining webhook tests (status transitions, upserts) stay. - No new tests required: this spec removes features, does not add behavior.
Guided Gates
- GG-1: After deploy, confirm the Done column shows cards with only a
done/errorbadge — no ✓ or ✓✓ glyph. - GG-2: Click an item in Done → details page shows the job section below the FFmpeg command box, with
CmdandLogtoggles. - GG-3: Click an item in Queued → details page shows a pending job with working
▶ Runand✕ Cancelbuttons; running the job updates the badge live. - GG-4:
/executein the browser returns a 404 (route is gone). - GG-5:
Run allandClear queuebuttons appear in the Queued column header;Clearstays in the Done column header. - GG-6:
PRAGMA table_info(review_plans);in the SQLite DB no longer lists averifiedcolumn.