docs: queue protocol v2 — state moves to pinned Gitea tracker #237 (parallel-session safe)
Build ErsatzTV Image / Build & test (.NET) (push) Has been cancelled
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Has been cancelled
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
Build ErsatzTV Image / Docs update reminder (push) Has been cancelled

Handoff file reduced to static kickoff prompt + append-only lessons lore;
queue/arc/session log live in pinned tracker ersatztv#237 with in-progress
claim labels and end-of-session triage. Decision recorded in decisions.md;
docs index updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 01:17:26 +02:00
co-authored by Claude Fable 5
parent ef8915f1ca
commit dfd2134cb4
3 changed files with 70 additions and 114 deletions
+3 -2
View File
@@ -36,6 +36,7 @@ Also present in `docs/`:
- **`docs/endpoint-index.md`** — generated REST endpoint index (method/path/operationId/summary per
OpenAPI tag). Do not edit by hand; regenerated by `scripts/generate-endpoint-index.py` /
`scripts/update-openapi.sh`.
- **`docs/handoffs/chicorytv-issue-queue.md`** — living session-to-session handoff: current queue
state, what's next. Check this for what's actively in flight before starting new work.
- **`docs/handoffs/chicorytv-issue-queue.md`** — static session kickoff prompt + workflow lore.
Queue state (goal, arc, what's in flight/next) lives in the pinned Gitea tracker ersatztv#237
— read that, not this file, for current state (protocol: decisions.md 2026-07-11).
- **`docs/handoffs/rest-api.md`** — original handoff prompt for kicking off the REST API work (#2).
+17
View File
@@ -225,3 +225,20 @@ TelevisionShow/TelevisionSeason/Artist per-media-item CollectionTypes and 422s t
"Add All" (query-wide) mirrors Blazor's two-step: materialize ids
via `GET /api/search/all-items`, then reuse the id-list add endpoints — no query-based add
command exists server-side. Issues #208/#209.
## 2026-07-11 — Queue state lives in the pinned Gitea tracker (#237), not in the handoff file
With multiple sessions/agents working the repo in parallel, the old protocol — every session
wholesale-rewrites `docs/handoffs/chicorytv-issue-queue.md` on main (session state + queue +
next-session prompt) — became a last-writer-wins race. New protocol: **volatile queue state
moved to Gitea**, which is concurrency-safe by construction. Pinned tracker issue **#237**
holds the goal + ordered arc in its body (edited rarely, only on arc changes, re-read before
edit) and an append-only session-comment log (fixed template: Closed / Filed / Triage /
Arc change / Recommended next). Milestone `Blazor removal (#91 phase b)` + the `review` and
`in-progress` labels are the machine-queryable view. Sessions **claim** an issue before working
it (`in-progress` label + claim comment; the tiny read→claim race window is accepted, later
claimant backs off; stale claims — no commits/comments ~48h — may be taken over with a comment).
Every new issue gets an explicit end-of-session triage verdict — gate-blocker (milestone + arc
slot) or backlog (label only) — so review findings adjust the queue only through that step and
the arc doesn't drift. The handoff file keeps only the **static kickoff prompt** and the
**append-only Lessons lore** (per-session prompts are gone; task context lives in issue bodies).
+50 -112
View File
@@ -1,59 +1,54 @@
# ChicoryTV issue-queue handoff (living document)
# ChicoryTV issue-queue handoff (static kickoff + workflow lore)
Paste the prompt below into a fresh session to work the next item. Each session ends by
UPDATING THIS FILE in place (rewrite the state section and the queue for the next item) so it
always holds the current handoff. History: created 2026-07-02 after the plan audit (#59 epic);
all backend gap issues (#100#111), all SPA screens (#84#89, #93, #109), the rebrand (#90),
the cutover root-flip (#91 phase a), full scheduling parity (#144/#162), media/troubleshooting
parity (#141/#161/#158/#180), onboarding docs (#185), the collections/API-gap batch
(#155/#151/#152-backend/#184), the multi/rerun/playlist SPA editors (#151/#152 PR #194, #153
PR #195), CI speedups (#190 PR #192), #145 playback troubleshooting (PR #199), #198 OpenAPI
casing (PR #201) + #193 rerun existence-check (PR #200), the first #91-gate batch
#210/#211/#213-partial (PR #214), and the search/media mutation batch #208/#209 (PR #216, shared
Add-to layer) are MERGED. **v26.5.0 in prod; v26.6.0 tagged** (awaiting Komodo pin bump in
server-management).
> **PROTOCOL CHANGED 2026-07-11** (decisions.md entry of same date). Queue state lives in the
> **pinned Gitea tracker [ersatztv#237](http://192.168.1.95:3000/timothy/ersatztv/issues/237)**,
> not in this file. Do **NOT** write session state, queue order, or next-session prompts here —
> sessions ending under the old protocol should append their session comment to #237 instead.
> This file holds only the two stable things: the standing kickoff prompt and the workflow lore.
> Historical per-session state: `git log` of this file (last state-bearing revision: 8b77d5e7).
**Session state (2026-07-10, #208/#209 session)**: **#208 + #209 CLOSED via PR #216** (merged
c63cadb8). New API surface: `POST /api/playlists/{id}/items` (wraps existing `AddItemsToPlaylist`;
handler hardened to reject system playlists — latent Blazor-path gap) and `GET
/api/search/all-items` (wraps `QuerySearchIndexAllItems`; Add-All = materialize-then-add, Blazor
two-step parity — no query-based add command exists). SPA: shared Add-to layer
`web/src/media/addTo/` (Collection dialog w/ inline create, Playlist dialog filtering `isSystem`,
Schedule dialog replicating `AddProgramScheduleItem.ForMediaItem` defaults —
`addTo/scheduleItem.ts`, SaveAsSmartCollectionDialog, AddToMenu popover) + `MediaPosterCard`
`actions` slot; wired into search (card drill-in, per-card add, Select toggle + selection bar,
Add All, Save As Smart Collection) and browse/detail (multi-select + select-all-on-page, all four
detail screens, per-show Quick/Deep scan gated Plex/Jellyfin/Emby, per-episode Media Info +
Troubleshoot, seasons browse kind). **Add-to-schedule is gated to shows/seasons/artists** — the
server validator (`ProgramScheduleItemCommandBase.CollectionTypeMustBeValid`) only accepts those
per-media-item kinds, matching Blazor's ForMediaItem call sites (found by live E2E: movie adds
422'd). Also fixed: portal font bug (nothing set a `body` font; portaled dialogs fell back to
Times — `body` rule added), a mount-debounce race (the 300ms query-debounce commit wiped
in-progress selections; `lastQueryRef` no-change guard in Search+Browse screens), and the
e2e-local.sh stale-asset gotcha (`cp -R` into an existing dir nests + serves the previous run's
assets — script now rm-first). New issues: **#217** (add-items handlers validate only 4/10 kinds
— pre-existing), **#218** (fresh-DB "Playouts 3" badge + "1 failing" chip). Retro/pre-review
issues filed in adversarial-reviewer: #18 (this milestone + #214), #19 (upcoming #207/#212, #202,
#91b, #197). #213 remains OPEN (reduced read-only-convenience scope).
---
**Blazor removal (#91 phase b) remaining MUST-FIX gates, in order:**
1. **#207 (+#212)** — schedules editor full mutation depth (endpoints exist; needs languages
enumeration endpoint shared with #212 channel-editor pickers) ← NEXT (prompt below)
2. **#202** — media-source management (write REST API + SPA screens; largest, riskiest)
3. **#91 phase (b) removal PR** — pattern redirects + catch-all (#204 plan), blazor-final tag
(#205), auth posture note (#206), delete Pages/Shared/ViewModels/Validators + 6 packages
(MudBlazor, MediatR.Courier.DependencyInjection, Blazored.FluentValidation, BlazorSortable,
Heron.MudCalendar, Chronic.Core). Removal recon is DONE (2026-07-09 gate session): Courier
consumers are 100% Blazor (`AddCourier` Startup.cs:394 dies too); the Startup `MapWhen`
"blazor" branch (736795) CO-HOSTS MapControllers/OpenAPI/Scalar — surgical removal only
(MapBlazorHub:775, MapFallbackToPage:776, services 379398); `wwwroot/lib/*`+`css/site.css`
are `_Host`-only; check `Locals/*.resx` consumers before deleting; keep `Extensions/`
except `NavigationManagerExtensions.cs`.
# STANDING KICKOFF PROMPT (paste into a fresh session, unchanged every time)
You are Fable, the ORCHESTRATOR in the main Claude Code session. Fable is EXPENSIVE: delegate
(recon → Explore/haiku; mechanical → sonnet; judgment-heavy → opus; fable forks for review).
FIRST read CLAUDE.md, docs/README.md + the convention docs it indexes, and the Lessons below.
Then work the queue:
1. Read the pinned tracker **ersatztv#237** — body = goal + ordered arc + session protocol —
and its most recent session comments; list open issues in the `Blazor removal (#91 phase b)`
milestone and with the `review` label.
2. Pick the top arc item that is open and NOT labeled `in-progress` (or the item the user names).
3. **Claim it**: add the `in-progress` label + a "claiming" comment on the issue(s).
4. Read the issue bodies (they carry the task context/evidence) and work the item under the
HARD CONSTRAINTS below.
5. Finish by following the session-end protocol in #237: ONE session comment on the tracker
(template in the tracker body, incl. triage verdicts for any new issues), remove your
`in-progress` labels, and complete the per-issue Task Completion Protocol from CLAUDE.md.
HARD CONSTRAINTS:
- Work in worktrees off origin/main. Copy web/node_modules from the main checkout.
- PARALLELIZE BY DEFAULT: split the task into disjoint slices up front and run 34 implementer
agents concurrently (recon agents are free — always fan those out). 34 concurrent
dotnet/npm builds are fine on this Mac (M4, 10 cores, 16 GB); check `uptime` before a big
fan-out — if 1-min load > ~6, drop to 23. Never 5+ builds (the historic crash was an
89-way fan-out). NEVER set ETV_UPDATE_GOLDENS.
- Never two committing agents on ONE worktree — give each parallel slice its own worktree
branched off the feature branch and merge back. Sequence only where a slice genuinely depends
on another's output (backend-first narrow, SPA-wide after worked well for #216).
- Merge consent in-conversation per session (prior pre-approvals do NOT carry over).
- Arm a CI monitor on the PR head sha AT PR-OPEN (commit-status endpoint), not at the end.
- Live-E2E via scripts/e2e-local.sh; NEVER exercise download endpoints via browser tabs (curl
them). Adversarial review fork per PR diff, SCOPED "review only".
---
# Lessons / workflow lore (append-only; conventions live in docs/, this is workflow lore)
**Lessons for all remaining prompts** (conventions live in docs/; this is workflow lore):
- READ docs/README.md → the convention docs FIRST; point recon/implementer agents at specific
doc sections. Only recon the task-specific delta.
- **blazor-route-parity.md now carries mutation-depth verdicts** (2026-07-09 sweep table at the
- **blazor-route-parity.md carries mutation-depth verdicts** (2026-07-09 sweep table at the
top of Section 2). A row is only trustworthy if its cluster verdict is PARITY-OK; the sweep
evidence lives in the issues #207#213. Keep the verdict table updated as gates close.
- "Screen exists" ≠ parity: the root cause of the false SPA-READY rows was same-session
@@ -95,65 +90,8 @@ issues filed in adversarial-reviewer: #18 (this milestone + #214), #19 (upcoming
- Live E2E seeding: the local library isn't API-seedable; the #216 E2E agent generated tiny
ffmpeg testsrc MKVs + inserted LibraryPath rows via SQL then scanned. Recipe not yet in
docs/e2e-local.md — worth adding next time it's needed.
---
# PROMPT — #207 + #212: schedules editor mutation depth + channel-editor gaps
You are Fable, the ORCHESTRATOR in the main Claude Code session. Fable is EXPENSIVE: delegate
(recon → Explore/haiku; mechanical → sonnet; judgment-heavy → opus; fable forks for review).
FIRST read CLAUDE.md, docs/README.md + the convention docs it indexes, and the Lessons above.
HARD CONSTRAINTS:
- Work in worktrees off origin/main. Copy web/node_modules from the main checkout.
- PARALLELIZE BY DEFAULT: split the task into disjoint slices up front and run 34 implementer
agents concurrently (recon agents are free — always fan those out). 34 concurrent
dotnet/npm builds are fine on this Mac (M4, 10 cores, 16 GB); check `uptime` before a big
fan-out — if 1-min load > ~6, drop to 23. Never 5+ builds (the historic crash was an
89-way fan-out). NEVER set ETV_UPDATE_GOLDENS.
- Never two committing agents on ONE worktree — give each parallel slice its own worktree
branched off the feature branch and merge back. Sequence only where a slice genuinely depends
on another's output (backend-first narrow, SPA-wide after worked well for #216).
- Merge consent in-conversation per session (prior pre-approvals do NOT carry over).
- Arm a CI monitor on the PR head sha AT PR-OPEN (commit-status endpoint), not at the end.
- Live-E2E via scripts/e2e-local.sh; NEVER exercise download endpoints via browser tabs (curl
them). Adversarial review fork per PR diff, SCOPED "review only".
## Task — close the #207 + #212 gates
Read issues #207 and #212 (bodies carry the #203-sweep evidence) and adversarial-reviewer #19
(pre-review asks). #207 is the screen the false-parity review made an example of: the SPA
schedules editor is a near read-only viewer — schedule create/edit/delete missing, every
per-item inspector control hard-coded disabled (~35 Blazor fields), add-item is defaults-only —
while the REST endpoints already exist and are unused (`PUT /api/schedules/{id}/items` replace,
`POST .../items` single-add with the FULL ScheduleItemRequest — see `addTo/scheduleItem.ts` for
a working payload builder). Enumerate EVERY Blazor `ScheduleItemsEditor.razor` field and wire it
(per-capability diff is the parity standard). #212 channel editor gaps: external logo URL, bare
create, and pickers — needs a languages enumeration endpoint shared by both editors (preferred
audio/subtitle language pickers); design that endpoint once. Record conventions in
decisions.md; full PR routine (OpenAPI regen for any new endpoint; blazor-route-parity verdict
rows for the schedules + channel-editor clusters). Comment + close #207/#212 per protocol.
## On completion — REQUIRED last step
Update THIS handoff (pop the done items, promote #202 to next with a fresh prompt), commit to
main, print the next prompt in a fenced code block.
---
## Issue queue (work top-down)
1. **#207 (+#212)** schedules editor depth + channel-editor gaps ← PROMPT above.
2. **#202** media-source management (write API + SPA) — largest; pre-review the API design
(adversarial-reviewer #19) before building.
3. **#91 phase (b)** Blazor removal PR (gate cleared once 12 close; #204/#205/#206 fold in;
removal recon in session state of the 2026-07-09 entry; adversarial pass mandatory).
4. **#197** cold API contract+security review — HARD GATE on #58 close/go-live and MANDATORY
before any remote exposure. Inputs: #215, #217, backlog nits.
5. Backlog: #215 (API playout mutations skip EntityLocker gating), #217 (add-items validates
only 4/10 kinds), #218 (fresh-DB Playouts badge / "1 failing" chip), #213 remainder (logs
sort/page-size persistence, block-history page-size/gating, list filters), #99 (TS/HLS-Direct
session tracking), #66 (artwork magic-byte sniffing), CI: unpin MySQL service host port 3306
(concurrent-run collision), nits (unclamped pageSize, 30 MB bare 413, PlayoutController route
Name=/lightweight exists-check, guide 21-include eager-load + fillerKind notes — #85/#102
comments), review nits from #216 (last-used-collection memory, detailBrowseItem cast).
Cross-refs: v26.6.0 deploy = Komodo pin bump (server-management). Real-transcode E2E of the
playback screen: once on the test container (`ersatztv-test` runs `:latest`) — local ffmpeg
8.1.2 lacks subtitles/zscale filters.
- **Parallel sessions (2026-07-11 protocol)**: claim before working (`in-progress` label — the
tiny read→claim race window is accepted; later claimant backs off). Claiming prevents
duplicate pickup, NOT overlapping code changes — check the tracker's dependency notes
("#234 after #231", "coordinate with #215") before touching shared surfaces. Lessons edits
to THIS file: append bullets only, `git pull --rebase` before commit.