feat(69): Auto-Tune SPA wizard (Configure → Preview → Create) — PR2 #389

Merged
timothy merged 3 commits from feat/69-auto-tune-spa into main 2026-07-17 00:30:53 +02:00
Owner

Closes the SPA half of #69 (auto-tuning). PR1 (#379) shipped the backend; this is PR2: the wizard screen, so fixes #69.

What

A new /app/auto-tune screen (sidebar Auto-Tune, after New Channel) — the automatic-first channel-creation mode. Three steps on the existing PR1 endpoints, additive/non-destructive:

  1. Configure — selectable axis cards (TV Shows / TV Genres / Movie Genres) + defaults (min items, starting number, channel group, channel template preselected from …/channel-templates/default).
  2. Preview (POST /api/v1/channels/auto-tune/preview) — proposals grouped by axis, per-row + tri-state per-group checkboxes, mono allocated number, item counts; already-existing names greyed + disabled + deselected (dedup).
  3. Create (POST /api/v1/channels/auto-tune) — Created/Skipped/Failed Stat tiles + per-channel result list.

Design was iterated in Claude Design and synced to design-system/; visuals follow the approved prototype (design_handoff_auto_tune/).

Scope note — DetailPanel deferred

The design iteration added a per-channel DetailPanel (Configure-per-row editor: image, shuffle/always, template + Advanced overrides, weighted content sources). It needs backend PR1 doesn't have, so per design-first it's deferred to #383 (children #384/#385/#386). This PR is the 3-step wizard only — no per-row Configure button.

Implementation notes

  • Follows spa-conventions.md: discriminated-union state, activeRef + per-call seqRef guards, no synchronous set-state in effect bodies (§3); reuses channelTemplates API (§4); no primaryAction — wizard drives its own in-body buttons (§10). number is echoed as a string, never parsed.
  • New web/src/api/autoTune.ts (+test), web/src/screens/AutoTuneScreen.tsx (+test), route/nav + App nav-smoke test.

Docs (same PR)

  • docs/domain-model.md — /app/auto-tune added to channel routes + nav.
  • docs/blazor-route-parity.md — recorded as a net-new SPA screen (no Blazor ancestor).
  • No OpenAPI change (no new endpoints) — check:api green.

Verification

  • npm test (790 pass) · npm run lint clean · npm run build ok · npm run check:api green (independently re-run).
  • Live-E2E + cold independent review: in progress; results to follow before merge.

🤖 Generated with Claude Code

Closes the SPA half of **#69** (auto-tuning). PR1 (#379) shipped the backend; this is **PR2: the wizard screen**, so **fixes #69**. ## What A new `/app/auto-tune` screen (sidebar **Auto-Tune**, after New Channel) — the automatic-first channel-creation mode. Three steps on the existing PR1 endpoints, additive/non-destructive: 1. **Configure** — selectable axis cards (TV Shows / TV Genres / Movie Genres) + defaults (min items, starting number, channel group, channel template preselected from `…/channel-templates/default`). 2. **Preview** (`POST /api/v1/channels/auto-tune/preview`) — proposals grouped by axis, per-row + tri-state per-group checkboxes, mono allocated number, item counts; already-existing names greyed + disabled + deselected (dedup). 3. **Create** (`POST /api/v1/channels/auto-tune`) — Created/Skipped/Failed `Stat` tiles + per-channel result list. Design was iterated in Claude Design and synced to `design-system/`; visuals follow the approved prototype (`design_handoff_auto_tune/`). ## Scope note — DetailPanel deferred The design iteration added a per-channel **DetailPanel** (Configure-per-row editor: image, shuffle/always, template + Advanced overrides, weighted content sources). It needs backend PR1 doesn't have, so per design-first it's **deferred to #383** (children #384/#385/#386). **This PR is the 3-step wizard only** — no per-row Configure button. ## Implementation notes - Follows `spa-conventions.md`: discriminated-union state, `activeRef` + per-call `seqRef` guards, no synchronous set-state in effect bodies (§3); reuses `channelTemplates` API (§4); no `primaryAction` — wizard drives its own in-body buttons (§10). `number` is echoed as a string, never parsed. - New `web/src/api/autoTune.ts` (+test), `web/src/screens/AutoTuneScreen.tsx` (+test), route/nav + App nav-smoke test. ## Docs (same PR) - `docs/domain-model.md` — `/app/auto-tune` added to channel routes + nav. - `docs/blazor-route-parity.md` — recorded as a net-new SPA screen (no Blazor ancestor). - No OpenAPI change (no new endpoints) — `check:api` green. ## Verification - `npm test` (790 pass) · `npm run lint` clean · `npm run build` ok · `npm run check:api` green (independently re-run). - Live-E2E + cold independent review: in progress; results to follow before merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
timothy added the enhancementfrontend labels 2026-07-17 00:05:19 +02:00
timothy added 2 commits 2026-07-17 00:05:20 +02:00
Iterated Auto-Tune prototype pulled back from the Claude Design working copy
(project 4eb64b16) into design-system/. The 3-step wizard (Configure → Preview
→ Create) is PR2; the per-channel DetailPanel it also contains is deferred to
the #383 arc (#384/#385/#386) per design-first — new endpoints backlogged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(69): Auto-Tune SPA wizard — Configure → Preview → Create (PR2)
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 9s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 9s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m52s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 12s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 8s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m0s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 8m37s
33ba3a0492
New `/app/auto-tune` screen driving the PR1 endpoints: pick metadata axes
(TV Shows / TV Genres / Movie Genres) + defaults, preview proposed channels
grouped by axis with per-row/per-group selection and already-exists dedup,
then bulk-create with a Created/Skipped/Failed summary. Additive/non-destructive.

The per-channel DetailPanel from the design iteration is deferred to #383
(new endpoints backlogged) — this ships the 3-step wizard only.

- web/src/api/autoTune.ts (+test), web/src/screens/AutoTuneScreen.tsx (+test)
- route/nav (routes.tsx, ScreenContent.tsx), api barrel, App nav-smoke test
- docs: domain-model (route), blazor-route-parity (net-new SPA screen)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
timothy added 1 commit 2026-07-17 00:16:04 +02:00
fix(69): key Auto-Tune selection by axis+value; zero-templates UX (review)
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 7s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 7s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 5m43s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 9s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 8s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m3s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 12m10s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
8e82673e6b
Addresses the cold-review findings on PR #389:
- Medium: selection was keyed by proposal name, coupling two same-named
  proposals (e.g. a show titled "Comedy" and the "Comedy" genre) so toggling
  one flipped both. Now keyed by axis+value (unique); adds a regression test.
- Low: zero channel templates now shows a hint on Configure and the Create
  tooltip explains the missing template instead of a misleading positive label.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Owner

Independent review — both passes clean

Base review (cold, review-only agent) → MERGEABLE, no Blocker/High. Two non-blocking findings:

  • Medium — selection keyed by proposal name coupled two same-named proposals (a show titled "Comedy" vs the "Comedy" genre).
  • Low — zero-templates UX (misleading Create tooltip + empty Select).

Fixes (commit 8e82673e): selection now keyed by axis+value (proposalKey), migrated at every site, + a non-vacuous regression test; zero-templates shows a Configure hint and an accurate Create tooltip.

Fix re-review (cold, scoped to the fix diff) confirmed: all selection sites migrated (no .name-keyed logic survives), separator safe, the regression test would fail against the old code, and the zero-templates gate can't POST a bad templateId. One theoretical Low outside scope (backend returning a default while the list is empty — unrealistic).

Pure-SPA leaf (no server-state change — the endpoints shipped in PR1). Live-E2E (real library → preview → create → M3U/XMLTV) running; will report before merge.

Review-verdict: MERGEABLE @ 8e82673e6b

## Independent review — both passes clean **Base review** (cold, review-only agent) → MERGEABLE, no Blocker/High. Two non-blocking findings: - *Medium* — selection keyed by proposal `name` coupled two same-named proposals (a show titled "Comedy" vs the "Comedy" genre). - *Low* — zero-templates UX (misleading Create tooltip + empty Select). **Fixes** (commit `8e82673e`): selection now keyed by `axis+value` (`proposalKey`), migrated at every site, + a non-vacuous regression test; zero-templates shows a Configure hint and an accurate Create tooltip. **Fix re-review** (cold, scoped to the fix diff) confirmed: all selection sites migrated (no `.name`-keyed logic survives), separator safe, the regression test would fail against the old code, and the zero-templates gate can't POST a bad `templateId`. One theoretical Low outside scope (backend returning a default while the list is empty — unrealistic). Pure-SPA leaf (no server-state change — the endpoints shipped in PR1). Live-E2E (real library → preview → create → M3U/XMLTV) running; will report before merge. Review-verdict: MERGEABLE @ 8e82673e6be8ab2d13bf1dfb639142833bae32df
timothy merged commit a5ac63dbd9 into main 2026-07-17 00:30:53 +02:00
timothy deleted branch feat/69-auto-tune-spa 2026-07-17 00:30:53 +02:00
Sign in to join this conversation.