docs(release): record the v26.14.0 release notes #738

Closed
timothy wants to merge 4 commits from release/v26.14.0 into main
Owner

Docs-only. Adds the v26.14.0 row to the release table in docs/ci-cd.md. This is the commit the v26.14.0 tag will be pushed onto, matching how v26.13.0 was cut (#716).

Head: 7b20dbd36 (rebased on a2b3a56d9 — see the rebase comment below).

What's in v26.14.0

Everything on main since v26.13.0:

Change Kind
#726 — embedded bitmap-subtitle -readrate starvation (0.53x realtime → stalling Live TV) app fix
#674 / #688 — decisions validator cross-checks frontmatter against PyYAML tooling
#706 / #707 / #711 — H10 review-verdict gate raced-sentinel false-open CI/process
CliWrap 3.10.4 dependency
JetBrains.ReSharper.GlobalTools 2025.3.5 dev dependency

The headline is #726. -readrate paces an input off its furthest-behind stream, and an embedded PGS/DVD subtitle is read through the video's own -i — sparse enough to drag the whole process to 0.53x realtime against the 1.0x a client consumes, draining the buffer until the channel stalls. Fixed with a capability-gated -readrate_catchup (ffmpeg 8.0+), which keeps -readrate on the frame-producing path so ffmpeg.qsv-extra-hw-frames-floor is untouched. Measured 0.533x → 1.067x on QSV and software libx264, with a 240s QSV soak clean of allocation errors.

Affected population is 3,182 of 24,646 media versions on prod, and it's a property of the item, not the channel — which is why the stall presented as random.

Release preflight (phase 1 of docs/ci-cd.md → "Cutting a release")

Re-run on the rebased head 7b20dbd36:

  • decisions_validate — OK, and now genuinely PyYAML-aware (via #725)
  • build_decisions_catalog — no drift
  • pytest scripts/tests432 passed
  • dotnet list package --vulnerable --include-transitive — clean across all 17 projects
  • No [skip ci] token in any commit on this branch (Gitea reads skip-ci from the tagged commit and would suppress the release build)

After this merges

  1. Push v26.14.0 on the merge commit (detaching at origin/main first, per the H11 tag-push guard — #719).
  2. Wait for tag CI to build :prod + immutable :26.14.0 + :<sha>. Do not overlap with an in-flight main build — they share :buildcache and the smoke container name.
  3. Run scripts/security-scan.sh on jazz against the immutable :26.14.0, and triage.
  4. Only then, manually DeployStack jazz-media (media-servers is the dead stack on bumblebee — confirm with /read ListStacks). Promotion is manual; auto_update: false.
Docs-only. Adds the `v26.14.0` row to the release table in `docs/ci-cd.md`. This is the commit the `v26.14.0` tag will be pushed onto, matching how `v26.13.0` was cut (#716). **Head: `7b20dbd36`** (rebased on `a2b3a56d9` — see the rebase comment below). ## What's in v26.14.0 Everything on `main` since `v26.13.0`: | Change | Kind | |---|---| | **#726** — embedded bitmap-subtitle `-readrate` starvation (0.53x realtime → stalling Live TV) | app fix | | **#674 / #688** — decisions validator cross-checks frontmatter against PyYAML | tooling | | **#706 / #707 / #711** — H10 review-verdict gate raced-sentinel false-open | CI/process | | CliWrap 3.10.4 | dependency | | JetBrains.ReSharper.GlobalTools 2025.3.5 | dev dependency | The headline is #726. `-readrate` paces an input off its *furthest-behind* stream, and an embedded PGS/DVD subtitle is read through the video's own `-i` — sparse enough to drag the whole process to **0.53x realtime** against the 1.0x a client consumes, draining the buffer until the channel stalls. Fixed with a capability-gated `-readrate_catchup` (ffmpeg 8.0+), which keeps `-readrate` on the frame-producing path so `ffmpeg.qsv-extra-hw-frames-floor` is untouched. Measured 0.533x → 1.067x on QSV and software libx264, with a 240s QSV soak clean of allocation errors. Affected population is **3,182 of 24,646 media versions** on prod, and it's a property of the *item*, not the channel — which is why the stall presented as random. ## Release preflight (phase 1 of `docs/ci-cd.md` → "Cutting a release") Re-run on the rebased head `7b20dbd36`: - `decisions_validate` — OK, and now genuinely PyYAML-aware (via #725) - `build_decisions_catalog` — no drift - `pytest scripts/tests` — **432 passed** - `dotnet list package --vulnerable --include-transitive` — clean across all 17 projects - No `[skip ci]` token in any commit on this branch (Gitea reads skip-ci from the *tagged* commit and would suppress the release build) ## After this merges 1. Push `v26.14.0` on the merge commit (detaching at `origin/main` first, per the H11 tag-push guard — #719). 2. Wait for tag CI to build `:prod` + immutable `:26.14.0` + `:<sha>`. **Do not overlap with an in-flight `main` build** — they share `:buildcache` and the smoke container name. 3. Run `scripts/security-scan.sh` on jazz against the **immutable `:26.14.0`**, and triage. 4. Only then, manually `DeployStack jazz-media` (`media-servers` is the dead stack on bumblebee — confirm with `/read ListStacks`). Promotion is manual; `auto_update: false`.
timothy added 1 commit 2026-08-04 23:34:08 +02:00
Headline is ersatztv#726: Live TV stalled on any item carrying an embedded
PGS/DVD bitmap subtitle, because -readrate paces an input off its furthest-behind
stream and that sparse subtitle shares the video's own -i. Fixed with a
capability-gated -readrate_catchup rather than the structural change the issue
originally proposed, which would have removed the throttle from the
frame-producing path that ffmpeg.qsv-extra-hw-frames-floor depends on.

Also in this release: the H10 review-verdict gate's raced-sentinel false-open
(#706/#707/#711), and two Renovate dependency bumps.

No [skip ci] token -- this commit is the release tag target, and Gitea reads
skip-ci from the TAGGED commit, which would suppress the release build.

Refs #726
timothy added 1 commit 2026-08-04 23:41:18 +02:00
The release/v26.14.0 push produced NO ActionRun records at all -- verified
against 7168 listed tasks, zero for b48f44dc7. This is the documented Gitea
behavior in docs/ci-cd.md: several pushes fired back-to-back (the #737 merge,
then this branch, then the PR open) and the later events were silently dropped.
The remedy is a spaced-out re-push, not assuming the runner is broken.

No [skip ci] token -- this branch's merge commit is the v26.14.0 tag target.
timothy added 1 commit 2026-08-04 23:46:03 +02:00
Merge branch 'main' into release/v26.14.0
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 17s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 19s
PR Gates / Docs update reminder (pull_request) Successful in 20s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 27s
Review verdict / Set review-verdict status (pull_request_target) Successful in 6s
PR Gates / decisions lifecycle (pull_request) Successful in 23s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 9s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 8s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 20s
PR Gates / Script tests (pytest) (pull_request) Successful in 1m52s
0ac25e1447
timothy force-pushed release/v26.14.0 from 0ac25e1447 to 7b20dbd360 2026-08-04 23:50:21 +02:00 Compare
Author
Owner

Rebased — scope of v26.14.0 grew

main moved while this PR was open: #725 (fix/674-688-decisions-validator) merged as a2b3a56d9. Two consequences, both handled:

  1. The release notes were incomplete — v26.14.0 now also carries #674/#688. Row updated.
  2. The branch had a merge commit on it. 0ac25e144 "Merge branch 'main' into release/v26.14.0" had been pushed onto this branch (Gitea's Update branch button does this). That contradicts release.format-as-you-touch-rebasekeep a PR branch current by rebasing, never merge main in — so I rebased onto a2b3a56d9 and force-pushed over it.

Before forcing I verified nothing would be lost, three ways:

$ git diff --stat 0ac25e144 HEAD          # only my own added line
 docs/ci-cd.md | 2 +-
$ git diff --stat origin/main HEAD        # +1 line, ZERO deletions -> reverts nothing from main
 docs/ci-cd.md | 1 +
$ git log --format='%h %an %s' 0ac25e144 --not a2b3a56d9
 0ac25e144 Timothy Merge branch 'main' into release/v26.14.0
 21f0e50fd Timothy chore(release): re-trigger CI ...
 b48f44dc7 Timothy docs(release): record the v26.14.0 release notes

No other session's commits were on the branch. Head is now 7b20dbd36, linear on a2b3a56d9.

The added claim is verified, not assumed

The new row says #674/#688 makes a PyYAML-truncating record fail the local gate. Checked by re-breaking this release's own readrate-catchup-sparse-streams record (unquoting rule: so the #350 truncates) and re-running the validator:

decisions-validate: .../readrate-catchup-sparse-streams.md: frontmatter key 'rule' means different
things to the two parsers — reader="...(mirroring #350's exclusion) and NOT a concat input..."
but PyYAML='...(mirroring'. PyYAML wrote this file, so its reading is the real value and the record
is silently corrupt. Common cause: an unquoted ` #`, which YAML treats as a comment and truncates there.

That is exactly the defect that reached CI earlier in this session on 57ad5efb3, because the pre-#725 validator structurally could not see it. It is now caught before a push. scripts/tests: 432 passed (was 411 before #725 added its cases).

Why this PR had no CI runs before

For the record, since it looked like a runner problem and wasn't: the original push produced zero ActionRun records (checked against 7,168 listed tasks, none for b48f44dc7). docs/ci-cd.md documents this — several pushes fired back-to-back get silently dropped. Compounded here by main moving underneath the branch. The rebase + force-push is the re-trigger.

Preflight re-run on the rebased head

  • decisions_validate — OK, and now genuinely PyYAML-aware
  • build_decisions_catalog — no drift
  • pytest scripts/tests — 432 passed
  • No [skip ci] token in any commit on this branch (it is the tag target)
  • dotnet list package --vulnerable --include-transitive — clean across all 17 projects (re-verified pre-rebase; no dependency changed in the rebase)
## Rebased — scope of v26.14.0 grew `main` moved while this PR was open: **#725** (`fix/674-688-decisions-validator`) merged as `a2b3a56d9`. Two consequences, both handled: 1. **The release notes were incomplete** — v26.14.0 now also carries #674/#688. Row updated. 2. **The branch had a merge commit on it.** `0ac25e144 "Merge branch 'main' into release/v26.14.0"` had been pushed onto this branch (Gitea's *Update branch* button does this). That contradicts `release.format-as-you-touch-rebase` — *keep a PR branch current by rebasing, never merge main in* — so I rebased onto `a2b3a56d9` and force-pushed over it. Before forcing I verified nothing would be lost, three ways: ``` $ git diff --stat 0ac25e144 HEAD # only my own added line docs/ci-cd.md | 2 +- $ git diff --stat origin/main HEAD # +1 line, ZERO deletions -> reverts nothing from main docs/ci-cd.md | 1 + $ git log --format='%h %an %s' 0ac25e144 --not a2b3a56d9 0ac25e144 Timothy Merge branch 'main' into release/v26.14.0 21f0e50fd Timothy chore(release): re-trigger CI ... b48f44dc7 Timothy docs(release): record the v26.14.0 release notes ``` No other session's commits were on the branch. Head is now `7b20dbd36`, linear on `a2b3a56d9`. ## The added claim is verified, not assumed The new row says #674/#688 makes a PyYAML-truncating record fail the **local** gate. Checked by re-breaking *this release's own* `readrate-catchup-sparse-streams` record (unquoting `rule:` so the ` #350` truncates) and re-running the validator: ``` decisions-validate: .../readrate-catchup-sparse-streams.md: frontmatter key 'rule' means different things to the two parsers — reader="...(mirroring #350's exclusion) and NOT a concat input..." but PyYAML='...(mirroring'. PyYAML wrote this file, so its reading is the real value and the record is silently corrupt. Common cause: an unquoted ` #`, which YAML treats as a comment and truncates there. ``` That is **exactly** the defect that reached CI earlier in this session on `57ad5efb3`, because the pre-#725 validator structurally could not see it. It is now caught before a push. `scripts/tests`: **432 passed** (was 411 before #725 added its cases). ## Why this PR had no CI runs before For the record, since it looked like a runner problem and wasn't: the original push produced **zero** `ActionRun` records (checked against 7,168 listed tasks, none for `b48f44dc7`). `docs/ci-cd.md` documents this — several pushes fired back-to-back get silently dropped. Compounded here by `main` moving underneath the branch. The rebase + force-push is the re-trigger. ## Preflight re-run on the rebased head - `decisions_validate` — OK, and now genuinely PyYAML-aware - `build_decisions_catalog` — no drift - `pytest scripts/tests` — 432 passed - No `[skip ci]` token in any commit on this branch (it is the tag target) - `dotnet list package --vulnerable --include-transitive` — clean across all 17 projects (re-verified pre-rebase; no dependency changed in the rebase)
timothy closed this pull request 2026-08-04 23:55:33 +02:00
timothy reopened this pull request 2026-08-04 23:55:36 +02:00
timothy added 1 commit 2026-08-05 00:30:58 +02:00
Prior attempts on this branch produced no pull_request run: two CLI pushes, a
close/reopen, and a body edit all yielded nothing, and the workflow_dispatch I
fired (run 1564) went green but writes NO commit statuses, so it cannot satisfy
the required Build & test / review-verdict/h10 checks.

The queue is now idle (run 1560, main @ a2b3a56d9, completed success), which is
the condition docs/ci-cd.md says matters -- back-to-back pushes get silently
dropped, a spaced-out one does not.

No [skip ci] token -- this branch's merge commit is the v26.14.0 tag target.
Author
Owner

Superseded by #739 — same content (the v26.14.0 release-notes row), rebuilt as a single clean commit off a2b3a56d9.

Closing this one to avoid two open release-notes PRs. Note #739 has the same problem, so the branch here isn't at fault: Gitea has not created a run from any git event repo-wide since ~21:46 UTC. A brand-new PR with a fresh opened event also produced nothing, which rules out anything branch- or PR-state-specific.

Full evidence is in #739's description.

Superseded by **#739** — same content (the `v26.14.0` release-notes row), rebuilt as a single clean commit off `a2b3a56d9`. Closing this one to avoid two open release-notes PRs. Note #739 has the *same* problem, so the branch here isn't at fault: **Gitea has not created a run from any git event repo-wide since ~21:46 UTC.** A brand-new PR with a fresh `opened` event also produced nothing, which rules out anything branch- or PR-state-specific. Full evidence is in #739's description.
timothy closed this pull request 2026-08-05 00:48:50 +02:00

Pull request closed

Please reopen this pull request to perform a merge.
Sign in to join this conversation.