030a7c19e9945aa0af8d3c5e4bcf8bddcb85bf8a
2948
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
030a7c19e9 |
chore: widen the BOM-guard remedy to staged + dirty files (review nit)
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 5s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 5s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 6s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m18s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 14s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 3m9s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 13m30s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Cold review (MERGEABLE-WITH-NITS) flagged that the printed fix snippet and the docs by-hand check used `git diff origin/main...HEAD`, which excludes staged-but-uncommitted files. A brand-new staged .cs is exactly what fires the pre-commit deny yet is absent from that diff, so the copy-paste remedy would strip nothing → deny again → loop. (The deny still names the file, so manual stripping always worked; this just makes the printed remedy match the guard's own detection set: branch diff ∪ staged ∪ dirty.) Verified on a fresh fixture: a brand-new staged BOM file is denied, and the widened snippet strips it. Left as accepted (fail-open, Medium, CI backstops; shared with sibling hooks): `git -C <path>` isn't parsed for the target tree (only `cd` is — matches the `cd <wt> && git` usage here), and the heredoc-body false-deny vector that bash-guard/worktree-guard also carry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9e1fce1a3a |
chore: PreToolUse guard that blocks a commit/push with a BOM'd touched .cs
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 16s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 16s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 9m56s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 13m58s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 14m16s
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 5s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 4s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 4s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
The #311 fix-as-you-touch rule is already enforced by a pre-commit hook and a blocking CI job, and both missed the same mistake twice on 2026-07-17: PR #405 shipped 6 BOM'd files, PR #402 shipped 19. The gap is that the pre-commit hook is routinely skipped — worktree hook friction makes `git commit --no-verify` the normal path here — which leaves CI, a ~10-minute round trip, as the first thing that tells you. This guard fires on Bash before git runs, so --no-verify can't skip it. It denies `git commit`/`git push` when a .cs the branch touches still carries a BOM, names the files, and gives the strip command. Why a hook rather than a note: a memory describing this exact trap did not prevent either failure — the PR #402 session re-added a BOM an hour after writing that memory down, because the usual ways of touching a legacy file re-add it silently (Python io.open(..., encoding='utf-8-sig') WRITES a BOM back; sed/perl round-trips keep it). A check that runs beats one you have to remember. Follows the existing gate pattern: a fail-open script in .claude/hooks/ registered on the PreToolUse/Bash matcher alongside bash-guard and worktree-guard. Any parse or lookup trouble → allow; this must never be the reason a commit can't happen, and CI remains the backstop. Generated *.Designer.cs / TvContextModelSnapshot.cs are exempt, matching what dotnet format itself skips. Scoped to this repo, and it resolves the target tree from an in-command `cd` because commits here run as `cd <worktree> && git ...` and the harness resets the shell cwd between calls. Verified against a throwaway fixture: denies commit and push for a BOM'd touched file (naming it); allows a clean tree, a BOM in generated files only, `git status` with a BOM present, and `echo 'run git push later'` (no false-trip on the words). Proved live via a sentinel — it fires on the Bash matcher — sentinel removed. docs/contributing.md updated: three enforcement layers, why the third exists, the utf-8-sig re-add trap, a by-hand check, and the bash-not-zsh caveat for `dotnet format --include` (mapfile is bash-only; under zsh the file list is empty and the tool looks like it silently did nothing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
76c710dfa5 |
Merge pull request 'ci: move api-docs and format back to ubuntu-latest (refs #406)' (#407) from ci/604-lane-rebalance into main
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 3m35s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / CI image pin matches docker/ci (push) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 14m23s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 15m7s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m54s
|
||
|
|
5ac49e553e |
Merge pull request 'fix(264): record library-level LastScan after a successful local scan' (#408) from fix/264-library-lastscan into main
Build ErsatzTV Image / CI image pin matches docker/ci (push) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Has been cancelled
Build ErsatzTV Image / Build & test (.NET) (push) Has been cancelled
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Has been cancelled
|
||
|
|
71708633f5 |
test(264): pin the path-scan-error log; correct testing.md accuracy
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 7s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 8s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 6s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 38s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 10m49s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 15m6s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 15m9s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Re-review of the previous fix commit (
|
||
|
|
986ccfaf6c |
fix(264): log local path scan errors (review finding) + map Scanner.Tests
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 7s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 6s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 8s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 47s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m28s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 3m29s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m3s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Adversarial review (MERGEABLE, no blockers) raised two items worth folding in rather than deferring: Medium — ScanLocalLibraryHandler silently swallowed path scan errors, while the three remote scanners it mirrors all log result.LeftToSeq(). That mattered less when a failed path only skipped the path-level LastScan, but the previous commit makes a failed path suppress the library-level scan time too — so the user would see exactly the #264 symptom ("Never scanned") with nothing in the log explaining why. That is a diagnosis dead-end of the same class as the bug being fixed, so log it here rather than file a follow-up. No test: the sibling Synchronize*LibraryByIdHandlerTests don't assert on logging either, and LogError is an extension method that NSubstitute can't cleanly verify. Low — docs/testing.md bills itself as the authoritative map of what each test project covers but omitted ErsatzTV.Scanner.Tests entirely (1471 pre-existing tests). This PR adds a file to that project, so add the row and include it in the per-PR verification gate. |
||
|
|
5c8dd64acf |
fix(264): record library-level LastScan after a successful local scan
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 6s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 7s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 8s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 40s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 6m10s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 5m36s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 11m11s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Local libraries permanently showed "Never scanned" in the SPA libraries hub regardless of successful scans, even as item counts updated. Root cause: ScanLocalLibraryHandler wrote only the path-level LibraryPath.LastScan (which gates the per-path refresh interval) and never the library-level Library.LastScan. The read API (GetAllMediaSourcesForApiHandler) populates the hub's scan-time badge from Library.LastScan, so that value stayed null forever. The three remote scanners (Jellyfin/Emby/Plex) already set the library-level value; only the local scanner did not. Both sides predate #202 — the SPA hub merely made the missing value visible. Mirror the remote scanners' semantics: record the library-level scan time only when the scan actually ran and every path that ran succeeded, so a skipped (unforced, interval not elapsed) or partially-failed scan does not claim a successful scan time. Also de-BOM the touched handler per the fix-as-you-touch charset gate (#311). fixes #264 |
||
|
|
17d65909b6 |
ci: move api-docs and format back to ubuntu-latest (refs #406)
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 6s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 5s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 6s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m29s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 11s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 11s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 5m11s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 10m14s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
#390 moved these two onto the `small` lane to dodge a ~29 min queue in the contended `ubuntu-latest` lane. The queue was real, but the lane was the wrong place to fix it, and #390's own rationale flagged why: Caveat: on an API-touching PR this job does a full `dotnet build`, so it is not always a "small" job; capacity 4 absorbs that. "Capacity 4 absorbs that" held only because nothing enforced the SUM of the runners' per-job memory caps. Each job container is correctly capped at --memory=10g, but 6 slots x 10g = 60 GiB sat on a 25 GiB host that also runs prod media. On 2026-07-17 it went to load 713 with 21 GiB swapped, and telegraf could no longer get scheduled to report it. server-management#604 fixes that at the source rather than by shuffling lanes: the `ubuntu-latest` lane now has 5 slots (ci-runner grown to 48 GiB at capacity 4, plus a bumblebee overflow slot) against this repo's 5 ubuntu-latest jobs, and the `small` lane is sized for genuinely-tiny jobs. So the queue #390 was dodging is gone, and these two dotnet jobs go back where they can actually be given memory. Note this does NOT by itself shrink the `small` lane's caps: `build` (Build & push image) still runs there and is the very job #570 measured pegging 5.999/6 GiB, so that lane keeps a 10g-capable slot either way. The real unlock is disabling Roslyn's persistent VBCSCompiler in CI (#406) — 7.8 GB RSS observed — which would let every cap drop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ed4b0d45b1 |
Merge pull request 'feat(384): auto-tune DetailPanel content-source member read endpoint' (#397) from feat/384-smartcollection-members into main
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / CI image pin matches docker/ci (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 3m57s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m4s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 8m12s
Build ErsatzTV Image / Build & push image (amd64) (push) Failing after 29m57s
|
||
|
|
23a615ecd5 |
Merge pull request 'ci(390): shared CI toolchain image + drop 110s apt-ffmpeg + rebalance runner lanes' (#399) from feat/390-ci-toolchain-image into main
Build ErsatzTV Image / CI image pin matches docker/ci (push) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build CI Toolchain Image / Build & push CI image (push) Successful in 20s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Has been cancelled
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Has been cancelled
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
Build ErsatzTV Image / Build & test (.NET) (push) Has been cancelled
|
||
|
|
1be45fbe88 |
fix(384): guard out-of-range axis (L1 review) + tidy page clamp
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 11s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 11s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 6m6s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 7m8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 1m17s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 10m7s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 11m20s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Cold adversarial review found a crafted numeric `?axis=5` binds past [ApiController]'s auto-400 (ModelState valid), then AutoTuneAxisMap.GenerateQuery's `_ => throw` surfaces as a 500 (no global exception filter). Short-circuit an undefined axis to an empty result in the handler — matching #69's EnumerateAxis `_ => []` graceful-empty pattern. Adds a regression test asserting no search runs. Also simplifies the redundant pageSize lower clamp (review N4): the `<= 0 ? 100` guard already floors it, so `Math.Clamp(_, 1, 200)` -> `Math.Min(_, 200)`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0009607a09 |
feat(384): auto-tune DetailPanel content-source member read endpoint
GET /api/v1/channels/auto-tune/members lists the distinct content sources a proposed auto-tune channel's server-generated SmartCollection resolves to — parent shows for the TV axes (ItemCount = query-matching episodes), movies for the movie-genre axis — reusing the existing PagedLibraryBrowseItemsResponseModel (no new schema). The handler runs the server-owned AutoTuneAxisMap.GenerateQuery through ISearchIndex (client never sends Lucene, per #69 PR1) and rolls matching leaf items up to their distinct sources, mirroring GetSmartCollectionItems so the DetailPanel preview matches what the built playout will contain. Backend child of #383 (Auto-Tune DetailPanel milestone); read-only, cold review acceptable. Handler + controller tests (9 new). OpenAPI + endpoint-index regenerated; d.ts unchanged (reuses existing schema). Docs: api-conventions §5, decisions.md 2026-07-17, domain-model. fixes #384 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ed52892d68 |
ci(390): bump pin to ersatztv-ci:07048b8; compare full shas in pin guard
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 11s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 12s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 13s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 7s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 6m13s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m32s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 4m34s
Pin bump for the image rebuilt from the review-fix commit (the two-step working as designed — the new ci-image-pin guard demanded it). Also fixes a real bug in that guard, caught locally before CI: it compared abbreviated shas, but git auto-scales abbreviation length with object count, so the tag ci-image.yml builds from a fetch-depth:1 shallow clone is 7 chars while %h on the guard's full clone is 8 — it would have failed on every run. Now resolves both sides to full shas via rev-parse, which also catches a pin that names no commit at all. Verified non-vacuous locally: PASS on the real pin, FAIL on a stale pin, FAIL on an unresolvable one. Refs #390 |
||
|
|
07048b8c96 |
ci(390): address cold review — pin-drift guard, honest cron, doc fixes
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Failing after 13s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 15s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 16s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 15s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 13s
Build CI Toolchain Image / Build & push CI image (push) Successful in 29s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m7s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m11s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 6m6s
Cold adversarial review findings: - MEDIUM (Renovate generates pin drift): add a blocking ci-image-pin job. Renovate manages docker/ci/Dockerfile's base pins but cannot bump an opaque :<sha> in container.image, so a base bump would publish a new image, test the OLD one, and merge with the Dockerfile disagreeing with the pin. The guard fails when the pin isn't the last commit to touch docker/ci, or when the 5 jobs pin different tags — making the documented two-step enforced rather than remembered. - MEDIUM (cron was a no-op): the weekly rebuild updated nothing (jobs pin :<sha>) and buildcache would have restored the apt layer verbatim, collecting none of the base updates it existed for. Added no-cache on the schedule path and rewrote the comment to state what it actually is: a build canary + a fresh :latest for the next bump. - LOW: FFMPEG_TAG was referenced in the Dockerfile + docs but never existed (the FROM is hardcoded); reworded both. - LOW: paths: filtered the exact file while the docs claimed docker/ci/** — use **. - NIT: docs oversold ENTRYPOINT reset as a gotcha; act overrides it anyway. Marked defensive. Refs #390 |
||
|
|
84756eebab |
ci(390): default run steps to bash — act_runner uses sh inside container:
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 17s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 17s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 18s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 19s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 6m9s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m59s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 8m0s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
The migrations job died in 0.13s with 'set: Illegal option -o pipefail'. Root
cause: inside a container: act_runner does not default to bash, it falls back to
'sh -e {0}' (dash), since it cannot assume bash exists in an arbitrary image.
Every multi-line script in this workflow is bash (set -o pipefail, arrays,
shopt, mapfile), so dash fails them immediately.
The format job passed only because it already declared shell: bash explicitly.
Declare it once at the workflow level instead of per step; non-container jobs
already defaulted to bash, so nothing changes for them.
Documented in docs/ci-cd.md as a container gotcha — it surfaces only once a job
is containerized and does not look like a shell problem.
Refs #390
|
||
|
|
d38181cba4 |
docs(lore): verify the cheap selector's deps + priority, not just the winner
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m25s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 6m37s
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 7m16s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 6m24s
The #73 selector packet reported #385/#386 as deps:clear when both were blocked, and missed #383 (the only priority:medium in tier 1) by ranking on issue number. Either error alone produces a bad pickup, and the kickoff's winner-only recheck cannot catch a wrong winner. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
62c15da490 |
docs(73): document seasonal / date-conditional scheduling (already implemented)
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
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 / Functional E2E (curl contracts) (push) Has been cancelled
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been cancelled
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been cancelled
fixes #73 #73's premise ("ErsatzTV has no native date-conditional scheduling today") is false. The predicate (IAlternateScheduleItem) is shipped on both engines, evaluated by AlternateScheduleSelector, reachable from the SPA, and already unit-tested — so the only real gap was discoverability, and it was a docs gap: the mechanism was documented as a mechanism, never as a task. - channels.md: task-shaped "Recipe: seasonal / holiday programming" for both engines, plus the gotchas — chiefly that blank StartYear/EndYear is what makes a range repeat every year (AlternateScheduleSelector.cs:32-40), that the override branch needs BOTH years, and that explicit years disable wrap-around detection. - domain-model.md: glossary row (mirrors the #77 "already exists" row). - decisions.md: entry recording the verdict and the rejected asks — notably that "prioritize collection X during a date range" is #70's weighting problem, not a second primitive built here while #70 is mid-flight. No production code changed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
afa42b0b6f |
ci(390): run toolchain jobs in the shared CI image; drop 110s apt-ffmpeg; rebalance lanes
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 9s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 10s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 13s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 13s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Failing after 4m31s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m22s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Failing after 6m8s
Converts test/migrations/functional-e2e/api-docs/format to run inside ersatztv-ci:6a9e27c via container:, removing per-run setup-dotnet, setup-node, apt-get ffmpeg and dotnet tool install. NuGet/npm caches stay (project deps); setup-node's implicit npm cache is replaced with an explicit actions/cache. Rolled-up wins found while auditing every job's steps (evidence in #390): - Drop the apt-ffmpeg step (110s/run). It was never load-bearing: the app's only startup ffmpeg touch is a LogWarning from FFmpegLocatorService, and the harness drives curl-only contracts that never transcode. The image ships prod-identical ffmpeg 8.1.2 anyway, which #299's seeded-media follow-ups will need. - Bake dotnet-ef + reportgenerator into the image (2 per-run tool installs gone; versions now live in one tracked place). - Move api-docs (5s) and format (37s) to the idle 'small' lane — they were each queueing ~29min in the saturated ubuntu-latest lane. Also drops that lane from 5 jobs to 3. Only possible because container: makes them self-contained. Measured, not assumed: the image itself is worth only ~15-40s/job. The dominant costs are queue wait (server-management#604) and 742s of redundant compilation (#398) — both filed rather than silently bundled here. Refs #390 |
||
|
|
6a9e27c6b8 |
ci(390): fix CI-image smoke test — reportgenerator --version exits 1
Build CI Toolchain Image / Build & push CI image (push) Successful in 2m9s
The build-time smoke test failed the image build even though the toolchain was
healthy: reportgenerator prints its version banner then exits 1 ("No report
files specified"), so it has no clean version probe. Probe the dotnet-tool shim
on PATH instead. dotnet --info confirmed the SDK + .NET 10.0.10 runtimes resolve
correctly on the ffmpeg base (ICU/glibc fine).
Refs #390
|
||
|
|
f27ff876d0 |
ci(390): add shared CI toolchain image (.NET 10 SDK + Node 22 + prod ffmpeg)
Build CI Toolchain Image / Build & push CI image (push) Failing after 1m42s
Layers the .NET 10 SDK + Node 22 onto our own ersatztv-ffmpeg base (the same COPY --from=.../usr/share/dotnet pattern docker/Dockerfile uses for prod), and bakes the dotnet-ef + reportgenerator global tools the jobs installed per run. Builds/pushes 192.168.1.95:3000/timothy/ersatztv-ci:<sha> via a separate workflow so a ~2GB toolchain image isn't rebuilt on every commit. Refs #390 |
||
|
|
5701f93625 |
Merge pull request 'feat(380): extract shuffle-source construction to ShuffleSourceBuilder (kill PlaylistEnumerator's cross-engine reach-in)' (#391) from feat/380-enumerator-seam into main
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 5m23s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 10m5s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 11m52s
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
|
||
|
|
f5cc27e902 |
test(380): avoid channel-number collision with #77 golden fixture
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 10s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 4m56s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m36s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 10m35s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 13m44s
Post-rebase onto main (which merged #77's Classic_clock_padded golden): both fixtures seed the shared in-memory DB and independently picked Channel Number "3"/GUID ...0003, tripping a UNIQUE constraint. Move the shuffle fixture to "4"/...0004. Golden output is unchanged (it snapshots times/titles; shuffle determinism comes from playout.Seed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
8e7cba4c44 |
test(380): pin the keepMultiPartEpisodesTogether=true shuffle-source branch
Addresses the one substantive nit from the cold adversarial review of PR #391: the true branch of ShuffleSourceBuilder.GetGroupedMediaItemsForShuffle (routing through MultiPartEpisodeGrouper) had no direct regression net — the goldens and existing unit tests all use false. Add a test that observably distinguishes the branches: four episodes where two are a "(1)"/"(2)" multi-part pair collapse to 3 groups under keepMultiPartEpisodesTogether=true vs 4 groups under false. Test-only; no production change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
764cd3c31b |
feat(380): extract shuffle-source construction to ShuffleSourceBuilder
Eliminate the one cross-engine reach-in in the scheduler: PlaylistEnumerator called
PlayoutBuilder.GetGroupedMediaItemsForShuffle / GetCollectionItemsForShuffleInOrder as
statics (one engine reaching into another engine's class). Move both helpers verbatim to
a new public static ShuffleSourceBuilder in ErsatzTV.Core/Scheduling (sibling to the
also-static MultiCollectionGrouper / MultiPartEpisodeGrouper; deps passed as parameters,
not DI). Classic (PlayoutBuilder) and Playlist (PlaylistEnumerator) now share this one
place to build shuffle sources.
One intentional signature change: GetGroupedMediaItemsForShuffle takes
(bool keepMultiPartEpisodesTogether, bool treatCollectionsAsShows) instead of a
ProgramSchedule (verified those are the only two properties it read). This deletes
PlaylistEnumerator's fake `new ProgramSchedule { KeepMultiPartEpisodesTogether = false }`
(its TODO becomes an honest false, false) and gives callers without a ProgramSchedule
(#176, #70) a schedule-entity-free entry point.
Scope is deliberately (a)-only: engine separation preserved, no god-factory. Block stays
its own family; the Scripted/YAML construction duplication is a separate follow-up gated
on #381. See docs/decisions.md 2026-07-17.
Behavior-preserving: the characterization net added in the previous commit (classic-shuffle
golden byte-identical, PlaylistEnumerator reach-in sequence unchanged) plus new direct
ShuffleSourceBuilder unit tests (multi-collection vs fake-multi-collection lookup;
multi-part grouping on/off) all green. Full Core.Tests: 546 passed. PlayoutBuilder.cs
also de-BOM'd + whitespace-normalized per the fix-as-you-touch convention (#311).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
604ea96c9f |
test(380): characterize shuffle paths before enumerator-seam extraction
The #163 golden net (PlayoutBuildGoldenTests) only pins PlaybackOrder.Chronological on Classic + Block. The paths #380 refactors — the two PlayoutBuilder shuffle-source statics and PlaylistEnumerator's cross-engine reach-in into them — had zero coverage, so "goldens green before & after" would be a false safety signal. Extend the net to exactly the moved paths: - PlayoutBuildGoldenTests.Classic_shuffle: Classic builder + PlaybackOrder.Shuffle, pinned playout.Seed via Continue mode (Reset randomizes the seed) so the shuffle is deterministic and TZ-independent. Exercises GetGroupedMediaItemsForShuffle + the ShuffledMediaCollectionEnumerator wiring. - PlaylistEnumeratorTests.ReachIn_Shuffle_And_ShuffleInOrder_Items_Are_Characterized: a playlist with a Shuffle item and a ShuffleInOrder item, pinned seed, asserting the emitted id sequence. Covers both PlaylistEnumerator.Create reach-in call sites (:174 GetGroupedMediaItemsForShuffle, :185 GetCollectionItemsForShuffleInOrder) that the extraction rewires. No production changes. Verified deterministic across repeated runs and under TZ=UTC / Asia/Kolkata. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
e1d77ebeb9 |
design(388): mirror full ChicoryTV design system to prod + design-sync reminder hook
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 4m46s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 11m26s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 13m34s
Build ErsatzTV Image / Build & push image (amd64) (push) Failing after 16s
Bulk mirror sweep (#388): author/rebuild a prototype mockup for every shipped SPA screen so the Claude Design project (eb3b6122 / local design-system/) faithfully reflects prod. - 40 screens authored (mockups: visual/layout parity, no logic) via a fan-out workflow; 8 stale prototypes rebuilt (Dashboard, ChannelBuilder, Guide, Schedules, Playouts, Settings, …), 32 net-new (media/sources/system/auth). Retired Epg/ScheduleEditor/ScheduleLibrary. - New screens.js = single source of truth for the inventory; Shell.jsx nav + app.html screen map both build from it (nav groups mirror web/src/app/routes.tsx). app.html is now hash-routed. - All 42 views verified rendering headlessly across the 3 themes (126/126, 0 real errors). - design-sync-reminder.sh hook (+ settings.json): mechanical, fail-open, once/session nudge to pull-first (PreToolUse Write|Edit on web/src/**.{tsx,css}) and mirror/push-back (Stop) — keeps the design system from drifting from prod going forward. - docs/design-sync.md: document the bulk-sweep structure + the reminder hook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
33d6c6071b |
Merge pull request 'test(77): characterize clock-boundary schedule padding + docs' (#393) from feat/77-clock-align-verify into main
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Failing after 1s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
Build ErsatzTV Image / Build & test (.NET) (push) Has been cancelled
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Has been cancelled
|
||
|
|
17746c442e |
test(77): address cold-review nits — TZ-invariant guard comment + DecoDefault
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 10s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m59s
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 41s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 8m59s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 10m47s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Follow-up to the MERGEABLE cold review: - Golden: document that TZ-independence relies on PadToNearestMinute=15 dividing every IANA offset; warn against regenerating with a non-15 divisor. - decisions.md: include DecoDefault in the ProjectFlood coalesce list. [decisions-edit] corrects a factual omission in the same-PR entry added this session. Refs #77 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7e6b9d2747 |
test(77): characterize clock-boundary schedule padding; docs
#77's core (pad/snap schedules to :00/:15/:30 via filler) already exists — FillerMode.Pad + PadToNearestMinute (Classic), pad_to_next/pad_until (Sequential); Block is inherently time-anchored. No production code change; this locks the behaviour end-to-end and documents that it exists. - PlayoutBuildGoldenTests.Classic_clock_padded: a PostRoll FillerMode.Pad(15) preset through the real PlayoutBuilder snaps content to :15 (golden + explicit quarter-hour assertion). Splits Verify -> CompareGolden for reuse. - ChannelGuideProjectorClockPadTests: the guide projection coalesces trailing filler so programmes STOP on the padded boundary (XMLTV half). - docs: decisions.md (2026-07-17 entry), domain-model.md (clock-boundary row), testing.md (test map + count 540->542). Deferred (UI, blocked on #388): one-click per-channel/schedule clock-align toggle + 60-min increment option. Refs #77 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
88287bcdb3 |
docs(queue): Gitea milestone-name issue filter silently no-ops on ':'/'+' names
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m15s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 6m26s
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 6m2s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 6m34s
The `?milestones=<name>` filter returns the whole open-issue list (not a filtered set) when the milestone title contains special chars, mis-tiering issues during queue selection. Recipe: fetch open issues once, filter locally on `.milestone.title`. Also notes the dependencies-API owner/repo/index form. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a5ac63dbd9 |
Merge pull request 'feat(69): Auto-Tune SPA wizard (Configure → Preview → Create) — PR2' (#389) from feat/69-auto-tune-spa into main
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 4m13s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m51s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 7m38s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 5m47s
|
||
|
|
9741ce7d6a |
docs(queue): dependency-aware selection + milestones-as-bundles grooming conventions
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Has been cancelled
Build ErsatzTV Image / Build & test (.NET) (push) Has been cancelled
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Has been cancelled
Backlog grooming (2026-07-17): the kickoff cascade now excludes any issue with an
open blocked-by dependency (Gitea GET /issues/{n}/dependencies; auto-clears when the
blocker closes) so a blocked issue is never picked ahead of its blocker. Adds the
parked->closed convention and the bundle model: related work is grouped via active-arc
milestones or cross-reference comments and detected on pickup via milestone + backlinks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
7ef7ef25e8 |
Merge pull request 'feat(71): per-playout reshuffle + shuffle-state surfacing' (#387) from feat/71-reshuffle-playout into main
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been cancelled
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been cancelled
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 / Functional E2E (curl contracts) (push) Has been cancelled
Merge pull request '#387' (feat(71): per-playout reshuffle + shuffle-state surfacing) from feat/71-reshuffle-playout into main fixes #71 |
||
|
|
8e82673e6b |
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
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> |
||
|
|
33ba3a0492 |
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
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> |
||
|
|
181eabdded |
Merge pull request 'test(163): golden characterization tests for playout building (Classic + Block)' (#382) from feat/163-playout-goldens into main
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 5m40s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 3m55s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 10m3s
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
|
||
|
|
1131ecacfb |
test(163): apply review nits — instant-accurate Block TZ guard + determinism notes
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 9s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 10s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m9s
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 40s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 6m11s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 7m33s
Follow-up to the cold adversarial review of PR #382 (MERGEABLE-WITH-NITS): - Block TZ guard: BaseUtcOffset -> GetUtcOffset(Start). BaseUtcOffset is zero year-round for DST zones like Europe/London, so it would pass in a summer-dated fixture where London != UTC; GetUtcOffset pins the actual build instant and is correct regardless of fixture date. (Safe today — mid-Jan fixture — but removes the latent fixture-date dependency the reviewer flagged.) - Document the determinism invariants the fixture relies on: golden captures raw builder output (pre-trim AddedItems), Classic is TZ-independent for the captured fields (no guard needed), and ResetPlayout's random Seed can't perturb the Chronological fixture (RandomStartPoint/ShuffleScheduleItems false, distinct release dates). Re-verified: TZ=UTC both goldens pass; TZ=America/New_York Block skips, Classic passes; no golden drift. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
81684411bd |
fix(71): reseed all playout kinds on reshuffle (erase-history + rebuild); docs + minors
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 7s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 7s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 4m54s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m24s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 6m58s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 11m14s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 16m11s
Reset only reseeds Playout.Seed for Classic playouts (PlayoutBuilder); Block/ Sequential/Scripted rebuild deterministically from the existing seed, so reshuffle was a silent no-op for 3 of the 4 supported kinds (C1). Fix: ReshufflePlayoutHandler now sends ErasePlayoutHistory (reseed + clear anchors/ history, the only primitive that reseeds all four kinds) before enqueueing BuildPlayout(Reset). Also: correct docs/decisions.md's false "Reset already reseeds..." claim, fix the SPA reshuffle test mock to return 202 (matches the real endpoint), and gate the seed-help text to the resettable kinds (was showing even for ExternalJson/None where no Reshuffle button exists). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
11d3ba3823 |
design(69): sync Auto-Tune prototype + handoff (incl. deferred DetailPanel → #383)
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> |
||
|
|
dc5d889ccb |
test(163): golden characterization tests for playout building (Classic + Block)
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 9s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
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 44s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 3m56s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m41s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 11m33s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Add ErsatzTV.Core.Tests/Scheduling/Goldens/PlayoutBuildGoldenTests.cs — a golden-file net that snapshots the PlayoutItems each builder produces over a pinned build window; the scheduling counterpart to the M3U (#11) / XMLTV (#28) goldens. This is the regression net that de-risks the scheduling-refactor chain (#380 -> #70/#71/#176). Coverage this slice: - Classic (PlaybackOrder.Chronological) - Block (TimeZoneInfo.Local-guarded to UTC via Assume: runs in CI, skips gracefully under a non-UTC TZ; a real TZ seam is #380's scope) Determinism: the builders read no wall clock (time enters only via the caller-supplied start), so a pinned start is fully deterministic. Snapshots the raw UTC Start/Finish, not the *Offset properties (which .ToLocalTime()). Regen via ETV_UPDATE_PLAYOUT_GOLDENS (deliberately separate from ETV_UPDATE_GOLDENS). Both goldens proven non-vacuous. Sequential (YAML) + Scripted goldens tracked as a follow-up in #381. Docs: docs/testing.md updated (third golden net + env var + Block TZ guard). fixes #163 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6a4d81f862 |
docs(71): reshuffle action + seed surfacing (parity, domain-model, decisions)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
13584f5daf |
feat(71): SPA display play-order seed + persistence help text
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7a01ad62bd |
feat(71): SPA per-playout Reshuffle button + client fn
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
238937fa5b |
chore(71): regenerate OpenAPI + client for reshuffle endpoint + seed field
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e7072e49a8 |
feat(71): surface Playout.Seed on playout list + detail DTOs
Task 3 of ersatztv#71 (reshuffle-playout). Appends Seed as the last positional field on PlayoutNameViewModel, threading it through the Mapper list projection, GetPlayoutByIdHandler detail projection, and the four Update*PlayoutHandler VM constructions, then exposing it on PlayoutListItemResponseModel/PlayoutResponseModel via the controller's ToResponse/ToListItemResponse mappers. ChannelControllerTests.MakePlayout was an additional construction site not listed in the task brief (positional-record break); updated to append 0 for Seed alongside the brief's ErsatzTV.Tests changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a9d9fc8afb |
feat(71): POST /api/v1/playouts/{id}/reshuffle action (202/404/409/422)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
89c7d08cde |
feat(71): ReshufflePlayout command + handler (enqueue per-playout Reset build)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1f4341a347 |
docs(71): implementation plan for per-playout reshuffle + seed surfacing
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9a7ac28f9f |
docs(71): design spec for per-playout reshuffle + shuffle-state surfacing
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
331a427040 |
Merge pull request 'feat(69): auto-tuning backend — enumerate library metadata, preview + bulk-create channels (PR1)' (#379) from feat/69-auto-tuning into main
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Failing after 13s
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 5m7s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 8m44s
Build ErsatzTV Image / Build & push image (amd64) (push) Has been skipped
|