Skip to content

feat(ffmpeg)!: FFmpeg 8.1.2 (MT) + slim/full release variants - #2

Open
asoulliereHT wants to merge 30 commits into
mainfrom
chore/ffmpeg-8.1-upgrade
Open

asoulliereHT wants to merge 30 commits into
mainfrom
chore/ffmpeg-8.1-upgrade

Conversation

@asoulliereHT

@asoulliereHT asoulliereHT commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two changes, shipping together as v0.14.0:

  1. FFmpeg 7.1.5 → 8.1.2 (latest 8.x). Re-based the vendored src/fftools frontend onto n8.1.2 and rebuilt the MT core. Reports libavformat 62.12.102; 12/12 MT suite passes.
  2. Slim / full release variants. A FFMPEG_VARIANT build arg selects the codec set. full = existing lean set (x264, vpx, opus, mp3lame, webp, zimg + native AAC), unchanged. slim = x264 + zlib + native AAC/H.264 only, for H.264/AAC stream-copy + x264 re-encode + image-overlay watermark. live-clipping-poc will vendor slim.

FFmpeg 8.1.2 port

  • Dockerfile: FFMPEG_VERSION n7.1.5n8.1.2
  • Re-vendored src/fftools from 8.1.2 + re-applied wasm patches (ffmpeg.c EM_JS progress/timeout + init_globals + main→ffmpeg; ffprobe.c renames)
  • Dropped objpool.c/.h (8.x thread_queue uses libavutil/container_fifo); added textformat/* (ffprobe writers extracted upstream); graph/graphprint.c is a no-op stub (-print_graphs); dropped -lpostproc

Slim / full variants

  • Codec --enable-* (configure) and -l (link) flags moved from the Dockerfile into build/ffmpeg.sh + build/ffmpeg-wasm.sh, selected by FFMPEG_VARIANT (full default). Unknown variant → hard build error (no silent fallback).
  • make prd-mt-slimpackages/core-mt-slim/dist
  • CI release job builds both and attaches ffmpeg-core-mt-<tag>.tgz (full), ffmpeg-core-mt-slim-<tag>.tgz (slim), ffmpeg-wasm-<tag>.tgz (wrapper). build-and-test stays full-only.

Sizes

full ~25.3 MB · slim ~22.5 MB wasm.

Verification

Both variants build clean. Full: 12/12 MT suite, all six codec libs present. Slim: 12/12 MT suite (via core swap), configure banner shows only --enable-libx264. Reviewed via subagent-driven development (per-task + final whole-branch review, clean).

🤖 Generated with Claude Code

asoulliereHT and others added 22 commits July 4, 2026 01:51
Reproduce the current (pre-upgrade) FFmpeg n5.1.4 WASM build as a
rollback reference before the 7.1 upgrade effort.

- build/ffmpeg.sh: cap FFmpeg `make` parallelism (FFMPEG_JOBS, default 4)
  so individual clang processes aren't OOM-killed when the amd64-only
  emsdk 3.1.40 toolchain runs under x86 emulation on Apple Silicon
- .gitignore: ignore local buildx cache dirs (build-cache-st/-mt)
- BASELINE-n5.1.4.md: record toolchain, external-lib pins, FFmpeg
  configure flags, artifact sizes, test results (ST 23 pass, MT wrapper
  11 pass), and pre-existing harness gaps (broken serve script, MT-core
  main-thread test, Node test path)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the toolchain off the amd64-only emsdk 3.1.40, which forced slow,
OOM-prone x86 emulation on Apple Silicon. Only emscripten/emsdk:latest
is multi-arch, so pin it by manifest-list digest (= Emscripten 6.0.2):
reproducible builds that run native on both arm64 (local) and amd64 (CI).

Drop x265: its x86-centric CMake passes `-march=`, which emsdk 6.0.2's
newer clang rejects for the wasm target. x265 was already slated for
removal (HEVC *encode* only; HEVC decode remains via FFmpeg's native
decoder), so this both unblocks the toolchain bump and advances the trim.

All other vendored libs build cleanly under 6.0.2. FFmpeg itself is not
built here yet; the current n5.1.4 is superseded by the 7.1 upgrade
(the pre-upgrade baseline is captured on chore/phase-0-baseline).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Toolchain + build now produce a statically-linked MT ffmpeg-core.wasm
(~27.5 MB) reporting "version 7.1.5" (libavcodec 61). NOT yet functional
at runtime — MT scheduler hangs during the test suite; re-entrancy,
progress, and timeout bridges are not reimplemented for 7.x yet.

Build/toolchain:
- Dockerfile: FFMPEG_VERSION n5.1.4 -> n7.1.5
- build/ffmpeg.sh: scope -fPIC to FFmpeg configure (emsdk 6.0.2 probe
  fix) and strip *.so from the dep prefix so -l<lib> links statically
  (zlib's CMake emits libz.so despite BUILD_SHARED_LIBS=OFF, which
  emsdk 6.0.2 turned into a runtime dlopen)
- build/ffmpeg-wasm.sh: add 7.x fftools (dec/demux/enc/mux_init/sched +
  objpool/sync_queue/thread_queue); add -I./compat/stdbit (C23 header)
- build/fribidi.sh: serial install (c2man man-page race under -j)

fftools (pristine 7.1.5 + minimal wasm patches):
- vendored all 7.1.5 fftools/*.c/*.h (scheduler-based frontend)
- ffmpeg.c: main() -> ffmpeg(); ffprobe.c: main() -> ffprobe()
- ffprobe.c: rename program_name / program_birth_year /
  show_help_default (+_ffprobe) to avoid link collisions with ffmpeg.c
- bind.js: capture _ffmpeg()/_ffprobe() return value into Module.ret
  (7.x removed exit_program(); main() returns the code directly)

Still TODO: MT runtime hang; re-entrancy (init_globals equivalent);
progress bridge; timeout; ST-core threading decision; lib version
bumps + theora/vorbis trim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The MT core now loads, remuxes, and transcodes (single-instance, incl.
auto codec-threading and re-entrant execs when logging paces them).

- ffmpeg-wasm.sh: bake pthread-worker-URL fix — emsdk 6.0.2 spawns
  pthread workers from _scriptName (the wrapper worker's URL, not the
  core's), hanging load(); post-build sed restores the
  mainScriptUrlOrBlob override so pthreads load the core. Fixes the
  silent load() hang.
- ffmpeg-wasm.sh: PTHREAD_POOL_SIZE 32 -> 64 — FFmpeg 7.x scheduler
  (per-component threads) + codec frame-threading overflowed a 32-pool
  and deadlocked mid-transcode (overflow pthread_create can't spawn
  while the worker main thread is blocked).
- ffmpeg.c: add init_globals() reset at the top of ffmpeg() — 7.x
  ffmpeg_cleanup() frees the input/output/filtergraph/decoder arrays but
  leaves nb_* counts and transcode_init_done/nb_output_dumped set, so a
  2nd exec() ran against stale state and hung.

Known remaining (runtime): (1) rapid successive transcodes have a
timing-sensitive scheduler race (masked by log pacing); (2) the test
suite creates a fresh 64-worker instance per describe block and exhausts
browser workers ("Maximum call stack size"); pool sizing vs multi-
instance needs tuning (likely cap default threads + smaller pool).
Also still TODO: progress bridge, timeout, ST-core decision, lib trim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Single-instance now transcodes repeatedly with no deadlock: -h -> N
sequential transcodes all succeed, without needing log pacing.

- PTHREAD_POOL_SIZE 64 -> 32 (matches the historical footprint; keeps
  per-instance worker count sane).
- Post-build: cap navigator.hardwareConcurrency to 8 so FFmpeg's
  av_cpu_count (sysconf -> hardwareConcurrency) requests at most ~8
  threads/codec. Uncapped, the 7.x scheduler + frame-threading requested
  ~2*cores threads, overflowed the pool, and deadlocked mid-transcode
  (the "race" that log pacing masked). Explicit -threads still overrides.
- Fix -o output extraction in the post-build patch step (printf '%s'
  dropped arg spaces so neither patch applied).

Known remaining: terminate() after a transcode does not reap the MT
core's pthread workers, so creating a fresh instance afterwards fails
("Maximum call stack size"). Single long-lived instance (the common
case) is unaffected. Still TODO: progress bridge, timeout, ST decision.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adopt the single-long-lived-instance pattern (create/load once, reuse
for every operation, terminate once) instead of one instance per
describe block. This mirrors real-world usage and sidesteps the emsdk
6.0.2 pthread-pool leak that surfaces when many instances are created
and terminated in one page (see Phase 3 handoff).

The MT suite now runs cleanly; remaining failures are the not-yet-ported
progress and timeout bridges, not instance lifecycle.

Dropped the brittle "should list root dir" exact-count assertion (a
shared instance pre-writes video.mp4, changing the count); listDir stays
covered by the directory-API tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-implement the two wasm JS bridges against FFmpeg 7.x's scheduler
frontend (bind.js already wired them):

- send_progress (EM_JS -> Module.receiveProgress): computed in
  print_report() from pts / longest input duration, before the -stats
  early return so it always fires; emits 1.0 on the final report.
- is_timeout / get_timeout (EM_JS -> Module.timeout): checked in the
  transcode() scheduler loop. 7.x has no exit_program(), so a timeout
  breaks the loop and forces ret=1 (bind.js captures ffmpeg()'s return).
  The loop's poll period is tightened to the timeout when one is set
  (it otherwise wakes only every stats_period ~0.5s, too coarse for
  short timeouts); print_report self-rate-limits so stats are unaffected.

The MT wrapper suite (tests/ffmpeg.test.js) now passes 10/10: FS/dir
APIs, help, transcode+progress, timeout, abort. The MT-core *direct*
suite still fails — it runs the MT core on the page main thread where
Atomics.wait is illegal; pre-existing, needs a worker-based harness.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the theora, vorbis, and ogg builder stages, their COPYs into
ffmpeg-base, the --enable-libtheora/--enable-libvorbis configure flags,
and the -logg/-ltheora/-lvorbis* link flags. These are dead formats for
a web clip/stitch/H.264 workload; x264 (re-encode), vpx, opus, mp3, aac
(native), and webp remain.

MT core: 27.5 MB -> 24.9 MB; wrapper suite still 10/10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The clip/stitch workload burns in no subtitles or text overlays, so the
entire subtitle/text-shaping stack is removed: libass, freetype, fribidi,
harfbuzz builder stages, their COPY, the --enable-lib{freetype,fribidi,ass}
configure flags, and the -l{freetype,fribidi,harfbuzz,ass} link flags.

This removes three CVE-carrying libraries outright (freetype 2.10.4,
fribidi 1.0.9, harfbuzz 5.2.0) rather than bumping them, and shrinks the
MT core 24.9 MB -> 23.6 MB. Also disables the drawtext filter (shares
freetype) — re-add freetype if in-browser text overlays are needed later.

Wrapper suite still 10/10. (build/{freetype2,fribidi,harfbuzz,libass,
ogg,theora,vorbis}.sh are now unused; left in place, harmless.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `serve` script piped COOP/COEP/CORP headers to `http-server --headers`,
which is a silent no-op (http-server has no such flag) — so pages were
never cross-origin isolated and the MT (SharedArrayBuffer) tests could not
run against it. Add scripts/serve.js, a dependency-free Node static server
that actually sends the isolation headers and correct MIME types (esp.
application/wasm), and point `serve` at it.

MT wrapper suite now runs 10/10 against `npm run serve`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add correctness tests for the browser-side clipping workload: lossless
cut (-ss/-t -c copy) and lossless concat (-f concat -c copy) — both
verified working on the MT 7.1.5 core. Suite now 12/12.

Documents (in a comment) that multi-input filtergraphs (xfade
transitions, concat filter, overlay) deadlock on the MT 7.x core in
wasm and must run server-side; only stream-copy stitching is browser-side.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Supply-chain hardening:
- x264 (was branch `4-cores`) and lame (was branch `master`) are now
  pinned to their branch-HEAD commit SHAs — those floating branches made
  the build non-reproducible.
- zlib 1.2.11 -> 1.3.1 from upstream madler/zlib (pinned by the v1.3.1
  commit SHA; the ffmpegwasm fork has no 1.3.1 tag). Fixes CVE-2018-25032
  and CVE-2022-37434.

Confirmed: buildkit resolves the commit-SHA git refs, zlib 1.3.1 is
embedded in the core, wrapper suite still 12/12.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite .github/workflows/CI.yml for the 7.1.5/emsdk-6.0.2 toolchain:
- checkout@v4, setup-node@v4 (Node 20), buildx v3; add least-privilege
  permissions block (was none; deprecated checkout@v2/setup-node@v2 before).
- Build the MT core via `make prd-mt` with GHA buildx cache (emsdk 6.0.2
  is multi-arch -> native amd64 on the runner). MT-only: no ST core exists
  on 7.x. Chromium comes from the puppeteer bundled by mocha-headless-chrome
  (npm ci), so no separate setup-chrome action.
- Run the MT suite via start-server-and-test against the fixed serve script.
- release job (on v* tags): build + tar core-mt/dist and ffmpeg/dist and
  attach to the GitHub Release, so a Go app can vendor from a targeted release.

Also:
- test:all -> MT-only (npm test now passes 12/12; ST/core-direct targets
  can't run on 7.x).
- scripts/serve.js returns 200 at "/" so start-server-and-test/wait-on's
  health check passes.

Note: CI itself is verified only once pushed to GitHub Actions; the flow
it runs is validated locally (npm test = 12/12).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Team-facing doc: what changed vs upstream (7.1.5, lean codec set,
MT-only), the browser capability map (cut/transcode/concat work;
multi-input filtergraphs run server-side), the required COOP/COEP
headers, single-instance usage, build steps, and how to vendor from a
tagged release.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The committed package-lock.json is out of sync with the apps/* example
workspaces, so strict `npm ci` fails. Those apps aren't needed to build
or test the core; use `npm install` (lenient). Bump Node 20 -> 22 to
clear the EBADENGINE warning from an apps/* devDep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-base the vendored fftools frontend onto FFmpeg n8.1.2 and rebuild the MT
core. Builds clean; 12/12 MT browser suite passes; core reports libavformat
62.12.102 (genuine 8.1.2). The 7.1→8.1 delta is small — same thread-based
scheduler architecture — so the 7.1 port carried over with minor reworking.

- Dockerfile: FFMPEG_VERSION n7.1.5 → n8.1.2
- Re-vendor src/fftools from 8.1.2, re-applying the wasm patches:
  ffmpeg.c (EM_JS progress/timeout bridges, init_globals, main→ffmpeg,
  timeout poll-period) and ffprobe.c (symbol renames, main→ffprobe)
- Drop objpool.c/.h — 8.x thread_queue uses libavutil/container_fifo
- Add textformat/* — ffprobe's writers were extracted upstream into a
  sub-library; needed by both ffmpeg and ffprobe
- graph/graphprint.c: fork no-op stub for -print_graphs (upstream needs the
  resources/resman resource-bundling pipeline this build doesn't reproduce)
- ffmpeg-wasm.sh: refresh fftools file list; drop -lpostproc (8.x fftools no
  longer reference libpostproc, and configure no longer builds it)
- Docs/comments (FORK.md, bind.js, ffmpeg-wasm.sh) updated to 8.1.2

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the codec --enable flags (configure) and -l link libs (emcc) out of the
Dockerfile and into build/ffmpeg.sh + build/ffmpeg-wasm.sh, selected by a new
FFMPEG_VARIANT build arg. Default is full, so the existing MT core is byte-
compatible (12/12 MT suite still passes). Prepares a slim variant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
make prd-mt-slim builds a lean core with only libx264 + zlib enabled (drops
vpx/opus/mp3lame/webp/zimg) into packages/core-mt-slim. Passes the full 12/12
MT suite; sized for live-clipping-poc's stream-copy + x264/AAC re-encode +
image-overlay watermark.

Also harden the FFMPEG_VARIANT case in both build scripts: an unknown variant
now errors (exit 1) instead of silently falling back to full — so a typo can't
ship the wrong core in a release.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Release job now runs make prd-mt and make prd-mt-slim and tars
ffmpeg-core-mt-<tag>.tgz (full) + ffmpeg-core-mt-slim-<tag>.tgz (slim). The
dist-release/*.tgz glob attaches both plus the wrapper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@asoulliereHT asoulliereHT changed the title feat(ffmpeg)!: upgrade FFmpeg 7.1.5 → 8.1.2 feat(ffmpeg)!: FFmpeg 8.1.2 (MT) + slim/full release variants Jul 5, 2026
asoulliereHT and others added 7 commits July 5, 2026 08:31
Redefine the slim variant from "drop external codec libs" (~22.5 MB) to
"--disable-everything + re-enable only the H.264/AAC + mp4/ts + concat
components its consumer uses" (~6 MB, a 76% cut from full). The external-lib
trim alone only saved ~3 MB; the win is dropping FFmpeg's ~400 default decoders
/ 350 demuxers / 130 filters the app never calls.

Slim supports (single-input pipelines only): stream-copy cut/concat, single-
input libx264/AAC re-encode, and re-encode stitch via the concat demuxer
(multiple clips in, one out). Multi-input filtergraphs (overlay watermark,
xfade transitions, concat filter) deadlock on the MT wasm core and run
server-side — verified on 8.1.2, matching the prior 7.1.5 finding.

- build/ffmpeg.sh: slim branch now emits --disable-everything + the allowlist
  (protocols/demuxers/muxers/decoders/encoders/parsers/bsfs/filters)
- tests/ffmpeg-slim.test.js + ffmpeg-slim-mt.test.html: slim app-ops gate
  (copy cut/concat, re-encode, re-encode stitch). The generic MT suite can't
  gate slim — its mp4->avi transcode needs the avi muxer/mpeg4 encoder slim drops.
- package.json: test:browser:ffmpeg:slim
- CI: new parallel slim-build-and-test job (build slim + run slim gate); release
  needs both jobs; per-variant gha cache scopes (core-mt / core-mt-slim)
- FORK.md: slim rationale, ~6 MB, single-vs-multi-input rule, server-side design

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capture why this fork ships an MT-only @ffmpeg/core-mt and rejects the
single-threaded @ffmpeg/core: FFmpeg 8.x's fftools frontend is thread-based,
so "switch to ST" means downgrading the engine to upstream 5.1.4 (losing the
zlib/x264/lame CVE patches and slim allowlist). ST would not fix the
multi-input filtergraph deadlock (a wasm-scheduler limit) and is ~2x+ slower.
Cross-origin isolation is handled at the serving layer instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fftools requires threads from FFmpeg 6.0 onward (ffmpeg_deps gains
threads; pthread_create is unconditional in demux/mux), so 5.1 is the
last single-thread-capable frontend. This adds ST 5.1.10 cores built
from the fork's hardened dep pins, needing no COOP/COEP:

- Resurrect the 5.1-era vendored fftools (pre-scheduler, 8 files) from
  the phase-0 baseline into src/fftools-5.1/.
- Parameterize the build by FFMPEG_VERSION (Makefile, Dockerfile,
  ffmpeg-wasm.sh); the ARG is scoped to ffmpeg-base so the dep-builder
  cache stays shared between MT and ST builds.
- New `copy` variant: --disable-everything + mov/mpegts/concat demuxers,
  mp4 muxer, h264/aac parsers+decoders, copy-path bsfs. No encoders, no
  external libs, no GPL. 2.6 MB wasm (full ST is 22.8 MB), sized to
  clipping-tool-ui's two `-c copy` commands.
- Non-COI test lane: serve.js --no-coi mode on port 3001; the ST page
  asserts crossOriginIsolated === false (COOP/COEP <meta> tags were
  inert and are removed).
- Vetting suites: clipping-tool-ui's runClipExport/runClipStitch argv
  verbatim against synthesized short-GOP H.264+AAC TS segments, with
  stream-presence assertions (a single-keyframe fixture + size-only
  asserts had let an audio-only export pass); multi-input probe showing
  overlay/xfade/concat-filter complete on ST 5.1 (the 8.x deadlock is
  scheduler-specific); perf probe (MT default 5.7s / MT -threads 1
  20.9s / ST 22.9s on a 1080p60 2s x264 re-encode).

Vetting record: ~/vault/docs/ffmpeg.wasm/plans/2026-08-13 st-5.1.10-core.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Dockerfile: move FFMPEG_VERSION ARG/ENV below the embuilder layer so a
  version switch invalidates only the FFmpeg source ADD (Keanu)
- Makefile: fail loudly on empty FFMPEG_VERSION (would silently fetch
  FFmpeg's default branch); ffmpeg-wasm.sh now requires it via :? (Keanu)
- cliptool suite: probe() returns Duration/start and the tests assert the
  clip window (duration within [0.9,1.6] with keyframe pre-roll, start~0)
  and stitch ratio ~2x member (Bruce, Danny)
- add MT A/B lane (ffmpeg-cliptool-mt.test.html, test:cliptool:mt): the
  identical verbatim commands on the 8.1.2 MT core; reuse one instance
  when synth==DUT (two-instance reload trips the emsdk pthread-pool leak)
  and run synth encodes with -threads 1 — the unthrottled synth
  reproduced the production MT deadlock deterministically (Danny)
- perf pages: assert crossOriginIsolated false (ST) / true (MT) (Bruce)
- remove tests/ffmpeg-core-st.test.html + its script: instantiates the
  worker-built core in a document, which cannot work (sync-XHR abort);
  superseded by the ffmpeg-st suite (Keanu)
- soften cliptool header overclaim: synthesized inputs, not real packager
  segments (Bruce)
- FORK.md: title/lede no longer claim MT-only; release-variants notes ST
  is local-build only; vetting references now point in-repo (Jet)
- ADR 0001: status amendment recording the two falsified bullets and the
  re-evaluation, pending a superseding ADR if ST ships (Jet)
- build/ffmpeg.sh: WHY comment for the null muxer/bsf entries (Jet)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.15.0 shipped MT-only bytes under new names because the release job was
never told about ST: the branch added build-st/prd/prd-st-copy but CI.yml
still invoked only prd-mt/prd-mt-slim, so GHA layer caching replayed
v0.14.0's layers and uploaded them under v0.15.0 asset names.

- st-build-and-test job: builds the full ST core (the cliptool suite's
  input synthesizer) + the copy core, runs the ST suite and the verbatim
  clipping-tool-ui commands on the non-COI server, uploads core-copy
- release job: needs st-build-and-test, builds prd-st-copy, and attaches
  ffmpeg-core-st-copy-<tag>.tgz (no "-mt-" in the name)
- FORK.md: release-variants table gains the st-copy row (from v0.15.1);
  the "not built by CI" note is gone

The bad tag stays: v0.15.0 is immutable per the consumer fetch-script
contract (a re-pointed tag is indistinguishable from tampering and the
SHA pin rejects it). Ship the ST asset as v0.15.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The v0.15.0 rerun failed in st-build-and-test: testdata/ is a git
submodule and actions/checkout does not fetch submodules by default, so
the cliptool suite's fetch of audio-1s.wav 404'd and the 404 body fed to
ffmpeg produced a cryptic "TS segment synthesis failed (ret=1)". The MT
jobs never noticed because they use only embedded base64 fixtures.

Also guard the fixture fetches (cliptool + perf) with res.ok so an
unpopulated submodule fails with a named error instead of an ffmpeg exit
code three steps downstream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The st-copy artifact is LGPL: no --enable-gpl, no libx264, no
libpostproc, no external libs at all. Assert it at runtime from the
core's own -version banner in the copy lane (EXPECT_GPL_FREE) so a
future allowlist change can't silently re-introduce a GPL component.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(build): ST 5.1.10 cores (full + 2.6 MB copy) with non-COI vetting lane
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant