2208 Commits
Author SHA1 Message Date
aiongg fa550ce2d0 Build: use craft-media for bitstream, isobmff and matroska (#2435)
* Build: use craft-media for bitstream, isobmff and matroska

filmcraft-bitstream, filmcraft-isobmff and filmcraft-matroska re-export
craft_media::{bitstream, isobmff, matroska} from craft-libs. The packages
and their public paths stay, so dependent crates build unchanged. The
integration tests in crates/isobmff/tests and crates/matroska/tests run
against the re-exports.

* Build: pin craft-media at 9db732e

craft-media spells the Matroska colour types in US English: Colour is
Color and VideoInfo::colour is VideoInfo::color. The spec names
element_ids::COLOUR, COLOUR_SPACE and VpcConfig::colour_primaries stay.

* matroska: the oracle skips the duration of a track's last frame

Ported from craft-media: the last audio or video frame of a track cut by
-t is shorter than a codec frame. ffmpeg 8 writes a shortened
BlockDuration and reports the codec frame duration; a remux by our writer
stores DefaultDuration and ffprobe reports the shortened one. Subtitle
cue durations are still compared. This fixes oracle::mjpeg_ac3 with
ffmpeg 8. The HDR test is named hevc_hdr_color, as in craft-media.

* matroska: laced_remux laces every audio track and checks timestamps

craft-media laces only tracks with a DefaultDuration. The remux gives
each audio track without one its mean packet spacing, so Vorbis, FLAC
and PCM lace too, and compares every packet's timestamp with the
source's. Ported from craft-media's oracle_matroska.rs.

* codecs: the Matroska audio timing test laces its Xiph case

craft-media laces only tracks with a DefaultDuration. The test's
Matroska helper sets the AAC access unit's duration (1024 samples,
rounded to the nanosecond), and the test checks that the Xiph-laced file
has laced blocks and the unlaced one has none.

* Build: repin craft-media to f97c84d27 (NAL length-size checks)

craft-libs#245 is on storytold/craft-libs main, so the dependency points there.

* Build: repin craft-media to f0ad6c1e1 (QuickTime v2 PCM entry limits)
2026-10-23 17:51:38 -04:00
Dylan M. Taylor e45ff58346 ci(sonar): don't cancel superseded runs on main, so coverage is published (#3026) 2026-10-23 17:00:18 -04:00
aiongg 93a845ce41 i18n: missing translations never fail; xtask ci prints the missing list (#3012) 2026-10-23 15:39:16 -04:00
Dylan M. Taylor 20f02b9aa1 release: the git-sources gate moves into craft-common-actions (storytold/craft-common-actions#78) (#3014)
The check is shared at release.yml@main now; keep only the reusable call.
2026-10-23 14:41:15 -04:00
aiongg f46a1a8436 CI: run the gate through craft-common-actions' shared gate.yml; FreeBSD nightly and on release (#2937)
ci.yml calls the shared workflow (cached apt packages, nextest, main-only cache saves, Cargo.lock check). FreeBSD and Windows ARM64 run nightly and on pushes to release instead of on pull requests. Depends on storytold/craft-common-actions#69.
2026-10-23 14:21:15 -04:00
aiongg c3b4c4deb0 Packaging: pin the signing client and appimagetool, and check their hashes (#2610)
* Packaging: pin the Windows signing client and check its hash

* Packaging: pin appimagetool and check its hash

* Packaging: an architecture without a pinned appimagetool hash fails early

* Packaging: pin the AppImage runtime (type2-runtime 20251108) and pass it with --runtime-file

* Packaging: assign the empty hashes with '' (shellcheck SC1007)
2026-10-23 14:00:44 -04:00
aiongg fbeccbe9bc Fonts: pin craft-fonts bfcbf71; CRAFT_FONTS_REQUIRED fails without CRAFT_FONTS_DIR (#2646)
* Fonts: pin craft-fonts bfcbf71; CRAFT_FONTS_REQUIRED fails without CRAFT_FONTS_DIR

craft-fonts main adds Noto Sans CJK SC, Noto Sans Arabic, Noto Sans Thai and
Noto Sans Georgian. FilmCraft registers every CRAFT_FONTS entry, so releases
pick them up with the pin bump. ATTRIBUTION.md and NOTICE list them.

With CRAFT_FONTS_REQUIRED set, crates/text/build.rs errors when
CRAFT_FONTS_DIR is unset or empty, not only when it is not a checkout.

Refs storytold/craft-libs#197

* Fonts: only CRAFT_FONTS_REQUIRED=1 enables required mode

* text: the CRAFT_FONTS_REQUIRED message says unset or empty
2026-10-23 13:38:11 -04:00
aiongg 4743cfb939 fix(engine): resolve stored media paths with craft-docpath's resolve_absolute (#2609)
* fix(engine): use resolve_absolute for stored media paths

* fix(engine): give the reason when a scratch disk folder is refused

* CI: check that git dependencies come from storytold

* CI: release only with git dependencies from storytold

* doc_path: a foreign drive path without a document is refused, not document-relative

* Workflows: one Git-source check: lockfile present and current, no source replacement, storytold hosts only

* Workflows: the Git-source check reads the legacy .cargo/config too

* Workflows: the Git-source check parses cargo config with tomllib
2026-10-23 13:37:55 -04:00
aiongg 0a881266c8 CI: xtask ci in the dev profile, one test binary per crate, nextest (#2860)
* Tests: one integration-test binary per crate

Each crate with several tests/*.rs files keeps them as modules of tests/it/,
so cargo links one test executable per crate instead of one per file. Files
that change process-wide state or time a workload stay binaries of their own.

* Profiles: line tables for workspace crates, no debug info for dependencies

* Windows ARM64: nightly and on release pushes instead of every push to main

* xtask ci: clippy and tests in the dev profile

The release profile's thin LTO and single codegen unit made linking the test
binaries take most of the gate. The codec and audio crates stay at opt-level 3
in the dev profile, so their tests run at release speed.

* xtask ci: nextest, doctests, no examples, one wasm check

Tests run with cargo nextest when it is installed (cargo test otherwise) and
build with --lib --bins --tests, so examples aren't linked; doctests run
separately. The wasm check keeps going past the first failing crate. VP8
fixture generation locks across processes, since nextest runs each test in
its own.

* VP9 synthetic fixtures: write aside and rename

A test in another process could read a fixture ffmpeg was still writing.

* Fuzz tests: a third of each sweep by default, all of it with FILMCRAFT_FUZZ_ITERS

Every seed, mutation kind and thread count still runs. Header bytes each get
one overwrite value and two bit flips in rotation, so every value and bit
index still occurs across the region. FILMCRAFT_FUZZ_ITERS=N runs the whole
sweep plus N random rounds per seed, as before.

* Platform fallback tests: one failure point per hybrid path

The failure-anywhere test injected a failure at four points around every
restart point; the hybrid's paths differ only in the first GOP, around the
second and last restart points, at the end and never. The replay test needs
the first two GOPs.

* Export tests: shorter ranges with the same checks

The bitrate test measures 0.5–1.5 s, where each preset reaches its own
target (on 0–3 s the simple demo left two presets at the same rate). The
every-preset test exports three frames per preset.

* Audio tests: effects in parallel, a table-driven FFT for the Opus quality metric

The extreme-settings test keeps both settings and the 40 s tail for every
effect, one thread each. The Opus quality metric's FFT builds its twiddles
once and allocates nothing per transform (same algorithm and summation order).

* CI: dev-profile gate with nextest

xtask ci no longer builds with --release, so the release-profile overrides
go; nextest is installed for the test step.

* CI: apt packages through the cached install-apt-packages action

* Review fixes: fuzz tests with a panic hook in their own binaries, loud fixture lock

The fuzz tests of av1, h264, hevc, mpeg2v, vp8 and vp9 count panics through
a process-wide hook, so a panic in another test of the same process would
fail them; they are binaries of their own again. The VP8 fixture lock fails
the test instead of skipping it silently, and the full-sweep doc comments say
how many truncation points the full run samples.

* Fuzz tests: rotate the default subset per run (FILMCRAFT_FUZZ_SEED replays one)

* platform tests: decode-switch tests in their own binaries, one NVENC lock
2026-10-23 13:34:16 -04:00
Dylan M. Taylor 20e159dd68 tools: quit the super-linear regex scans in the spec table extractors (SonarCloud S8786) (#3007)
* packaging: use [[ for bash conditional tests (SonarCloud S7688)

SonarCloud flags every bare [ conditional in bash scripts under packaging/ as a reliability smell (rule S7688). Convert the flagged [ ... ] tests to [[ ... ]]: safer word-splitting and no glob surprises, same semantics for these string and file tests.

* tools: quit the super-linear regex scans in the spec table extractors (SonarCloud S8786)

SonarCloud flags five regular expressions in the AV1/HEVC extract_tables.py tools as super-linear: a lazy (.*) under re.S that rescans the whole remaining spec text at every position, a \s*([^|]+?)\s* sandwich, and \s+(.*) tails that can absorb the same whitespace repeatedly.

- the fence block reader now walks the ~~~~ c / tilde-run pairs with two linear searches instead of the lazy .*? findall; block-pair and 7+ tilde-run semantics are unchanged
- the symbol-row capture restricts its first and last characters away from [|] so the lazy group's backtracking jumps are gone (identical for every non-blank cell; a cell of only spaces is no longer captured, which the table rows never contain)
- the empty-comma cell splitter no longer lets \s* swallow the newline it anchors on; consecutive line breaks inside a cell no longer split (spec cells are single-line rows)
- initValue / ScalingList value captures start on a non-space so \\s+ and .* no longer share a split point (captured text is unchanged for all real values)

Verified old vs new run on synthetic spec markdown: byte-identical spé tables output and stderr.
2026-10-23 13:08:41 -04:00
Dylan M. Taylor 4d089668a8 packaging: use [[ for bash conditional tests (SonarCloud S7688) (#3003)
SonarCloud flags every bare [ conditional in bash scripts under packaging/ as a reliability smell (rule S7688). Convert the flagged [ ... ] tests to [[ ... ]]: safer word-splitting and no glob surprises, same semantics for these string and file tests.
2026-10-23 12:53:57 -04:00
Dylan M. Taylor c5c1693989 Use chrono instead of hand-rolled UTC conversion (#2662)
* Use chrono for UTC/calendar conversion instead of hand-rolled copies

Replaces the hand-rolled civil_from_days / days_from_civil copies (app log
timestamp, Media Browser date column, auto-save file-name stamp, MXF header
timestamp, golden-image attribution date) with chrono as a workspace
dependency. The existing chrono entry on apps/filmcraft (clock feature) was
lifted to the workspace. Output formats are unchanged and pinned by tests.
AGENTS.md now says time goes through chrono.

Follow-up to storytold/cadcraft#1785

* Never panic on a clock outside chrono's range; keep 5-digit years unprefixed

chrono's From<SystemTime> unwraps internally, so a clock past year 262143
panicked. logging::timestamp and golden::today now read the duration since the
epoch and go through the fallible DateTime::from_timestamp, reading an
out-of-range or pre-epoch clock as the epoch as the old saturating code did.
timestamp, today and media_browser::format_date write the year with {:04}
rather than %Y, which prefixes a '+' past year 9999.

Tests cover the far-future and year-10000 cases for timestamp and format_date.

* Don't build an unrepresentable SystemTime in the far-future test

UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows'
SystemTime and panicked before timestamp() ran, failing the Windows
test job. Build it with checked_add and only assert where it exists.

* time: preserve out-of-range years instead of the epoch (FredApps review)

`DateTime::from_timestamp(..).unwrap_or_default()` changed the existing output:
inputs the old hand-rolled civil-from-days converted (e.g. `10_000_000_000_000`
s → 318857-05-20 17:46:40) collapsed to `1970-01-01 00:00:00`. Restore the old
conversion domain with a cycle-based helper: reduce by whole 400-year Gregorian
cycles (146_097 days), convert the in-range remainder with chrono, and carry the
year back as i128 (saturating to the output type). Only the year leaves chrono's
range; month, day and time of day are always exact.

Sites (all UTC):
- apps/filmcraft logging.rs `timestamp` (SystemTime, u64 secs): pre-1970 still
  clamps to the epoch; a far-future clock keeps its year. u64::MAX/2 s →
  "292277026596-12-04T15:30:07.000Z", 10e12 s → "318857-05-20T17:46:40.000Z"
  (was "1970-01-01..."). SystemTime cases built with a checked add (Windows-safe).
- crates/engine media_browser.rs `format_date` (u64): 10e12 s → "318857-05-20
  17:46", u64::MAX → "584554051223-11-09 07:00" (was "1970-01-01 00:00").
- crates/format autosave.rs `civil` (i64 + i32 offset): keeps years, e.g.
  i64::MAX → 292277026596-12-04 15:30:07, i64::MIN → -292277022657-01-27. The
  separate `unix + offset_secs` overflow guard (checked_add) is kept: only an
  overflowing shift (old code wrapped it to garbage) reads as the epoch.
- crates/mxf from_unix (i64): keeps month/day/time, clamps only `year` to the
  u16 field as before. 10e12 s → year 65535, May 20 17:46:40; -10e12 s → year 0,
  Aug 13 06:13:20.
- crates/testkit golden.rs `today` (u64): same helper; pinned via `utc_parts`.

chrono stays the only calendar logic (NaiveDate::from_num_days_from_ce_opt on the
in-cycle remainder). No new chrono features; still default-features = false, std.

* Name the 400-year range extension as the one exception to the time rule

The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.

* Say the library computes month and day; time-of-day is plain arithmetic

Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
2026-10-23 11:18:37 -04:00
6f87c0e381 security(exr): validate chunks before frame allocation (#2913)
* security(exr): validate chunks before frame allocation

* exr: raise the decoded-buffer cap to 2 GiB so 8K float plates open

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:54:49 -04:00
920ad641a8 security(prores): bound decoded plane storage (#2931)
* security(prores): bound decoded plane storage

* prores: raise the decoded-plane cap to 1 GiB so 8K 4444 with alpha decodes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:54:45 -04:00
99ec34de30 security(vp9): bound frame allocation (#2930)
* security(vp9): bound frame allocation

* vp9: raise the frame-size cap to 2^26 luma samples so level 6.x 8K frames decode

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* vp9: keep the frame-size cap below the imports

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:54:40 -04:00
34848b3964 security(vp8): bound frame allocation (#2929)
* security(vp8): bound frame allocation

* vp8: raise the frame-size cap to 2^26 luma samples so 8K frames still decode

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:54:36 -04:00
b28db6c4db fix(bitstream): reject invalid length-prefixed NAL widths (#2953)
* fix(bitstream): validate NAL prefix size

* bitstream: accept every NAL length width the avcC/hvcC field can declare (1 to 4)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:54:32 -04:00
Earl Co 10c2e0255c security(bitstream): cache RBSP stop-bit lookup (#2880) 2026-10-23 10:54:27 -04:00
Earl Co b1effb9ab1 security(export): confine metadata-derived names (#2903) 2026-10-23 10:54:22 -04:00
Earl Co 9a0096a979 security(automation): isolate bridge screenshots (#2919) 2026-10-23 10:54:17 -04:00
Earl Co e1d5597477 security(av1): require unique complete tile coverage (#2917) 2026-10-23 10:54:12 -04:00
Earl Co f5a9a6f5e7 security(codecs): deduplicate hardware parameter-set checks (#2890) 2026-10-23 10:54:07 -04:00
Earl Co f554fdcb4f fix(interchange): confine embedded audio output names (#2956) 2026-10-23 10:54:02 -04:00
Earl Co 16d93a8c3d security(videotoolbox): bound reorder buffering (#2908) 2026-10-23 10:53:57 -04:00
Earl Co 7e980f31c9 security(project): validate graphic templates in linear time (#2886) 2026-10-23 10:53:52 -04:00
Earl Co 21b56ac090 security(apv): validate tile payload before allocation (#2916) 2026-10-23 10:53:47 -04:00
Earl Co 8119b36c21 security(mpeg2v): index picture extensions linearly (#2904) 2026-10-23 10:53:42 -04:00
Earl Co 70d0260b6e security(dnx): validate fields before frame allocation (#2914) 2026-10-23 10:53:38 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b12336cf73 ci(deps): bump Swatinem/rust-cache (#2971)
Bumps the actions group with 1 update: [Swatinem/rust-cache](https://github.com/swatinem/rust-cache).


Updates `Swatinem/rust-cache` from 2.9.2 to 2.9.3
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/swatinem/rust-cache/compare/6323deb102c322ba6fcbdcafc7e3dddab59af2b6...d1527c8f67ef9af357290475d4af4b0fab8f12e0)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-version: 2.9.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-23 10:53:33 -04:00
7f8c9921a1 security(engine): confine stored media filesystem access (#2869)
* security(engine): confine stored media filesystem access

* relink: index symlinked files; skip only symlinked folders

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:53:29 -04:00
875eec90e9 security(render): bound expanded frame plans (#2942)
* security(render): bound expanded frame plans

* render: count plan layers as they are pushed instead of re-summing every step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:53:24 -04:00
1c40c48289 security(mxf): share resolved descriptor metadata (#2949)
* security(mxf): share resolved descriptor metadata

* mxf: keep every source-package track in the no-material-package fallback

Two packages may reuse a track id and number; comparing only those dropped a real track.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:53:19 -04:00
Marco Arnulfo 0b99bafcc4 Project panel: a clip dropped on New Item makes a sequence from it (fixes #2855) (#2940) 2026-10-23 10:53:14 -04:00
Earl Co 66da0e5270 security(avi): bound OpenDML index work (#2925) 2026-10-23 10:53:09 -04:00
Earl Co a6ed3ecd14 security(text): bound transformed glyph masks (#2900) 2026-10-23 10:53:05 -04:00
Earl Co 71d5f52b4d security(opus): validate direct multistream headers (#2905) 2026-10-23 10:53:00 -04:00
980c388325 codecs: walk open-GOP leading pictures back in a loop, not by recursion (#2920)
* codecs: walk open-GOP leading pictures back in a loop, not by recursion

MxfVideo::sync_before and MpegVideo::sync_before recursed one GOP back
per leading picture, so a crafted index whose every GOP leads its key
frame recursed once per GOP: tens of thousands of frames for a small
file, a plausible stack overflow in dev builds.

Both now use gop::open_gop_sync, a loop over the same steps. It stops
when the index's sync sample lies past the picture instead of looping,
and reads pts and keys with get().

Fixes #2803

* gop: keep decode_start's doc comment on decode_start

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:52:55 -04:00
68cfb0b52d security(text): keep font scans within configured folders (#2876)
* security(text): keep font scans within configured folders

* fonts: still find symlinked font files; skip only symlinked folders

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fonts: rustfmt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:52:49 -04:00
d3ec344691 security(ui): bound export folder elision work (#2902)
* security(ui): bound export folder elision work

* export mode: keep location_link's doc comment on the function

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:52:45 -04:00
441e80b7a6 security(tts): make malformed-token scanning linear (#2871)
* security(tts): make malformed-token scanning linear

* tts-text: find a pause marker's ] by byte, so a multi-byte character at byte 64 doesn't hide it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:52:40 -04:00
599516dc1c fix(control): bound synthetic pointer events (#2868)
* fix(control): bound synthetic pointer events

* control: check ui.click's count before queuing any input events

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:52:35 -04:00
9722d77aa5 security(engine): bound graphic template thumbnails (#2887)
* security(engine): bound graphic template thumbnails

* graphic templates: scale a capped thumbnail to fit both sides of the canvas

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:52:30 -04:00
Earl Co ebe15de141 security(bench): isolate temporary export outputs (#2928)
* security(bench): isolate temporary export outputs

* chore(bench): record tempfile dependency
2026-10-23 10:52:26 -04:00
Earl Co 73265622b2 security(h264enc): cache second-pass plan sums (#2906) 2026-10-23 10:52:21 -04:00
Earl Co cbeb748ecc security(matroska): compose stripped private data once (#2911) 2026-10-23 10:52:17 -04:00
Earl Co 370019bdfc security(isobmff): bound metadata expansion (#2933) 2026-10-23 10:52:13 -04:00
Earl Co ccb80b2e9d security(platform): bound hybrid replay output (#2938) 2026-10-23 10:52:08 -04:00
Earl Co 8fa72cf18d security(codecs): avoid quadratic MPEG timestamp lookup (#2883) 2026-10-23 10:52:03 -04:00
Earl Co 19ff23cba3 security(hevc): validate slice payload before allocation (#2939) 2026-10-23 10:51:59 -04:00
04a4a7bc72 security(proxies): reserve outputs atomically (#2922)
* security(proxies): reserve outputs atomically

* proxies: ReservedOutput::file returns an Option instead of expect()

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:51:54 -04:00