* Audio: check the WAVE behind a RIFF form the probe skips
The probe skips a RIFF form other than WAVE and reads a WAVE behind it, so
the marker search skips it too and the INFO check runs on the WAVE the
probe reads. The mismatched-container test moves to an input that still
mismatches: a hinted FLAC behind a WAVE.
* Dependabot: hold egui, eframe and egui_kittest at 0.36
craft-ui stays on egui 0.36 until storytold/craft-libs#199 lands; the daily
group would otherwise propose 0.37 again.
* Sessions: use resolve_absolute for stored source paths
* CI: check that git dependencies come from storytold
* CI: release only with git dependencies from storytold
* Parse Cargo.lock as TOML when checking git sources
* Use the shared git-sources check in CI
* CI: parse cargo config, including the legacy .cargo/config, in the git-sources check
With /tmp on tmpfs, four autosave and recovery tests failed:
- Autosave folders were named by millisecond and process id, and created
with create_dir_all, so two sessions started in the same millisecond
shared one folder and the second overwrote the first's unsaved work
(recovering_keeps_the_unsaved_work_it_replaces,
closing_the_session_or_quitting_drops_its_autosave). Autosave::create
now creates the folder exclusively and adds a -2, -3... suffix when
the name is taken.
- leftovers() offers an autosave only when it is strictly newer than its
session file. A save and the autosave right after it can share one
coarse mtime, so the tests that save and then autosave now move the
saved file's mtime a second back
(unsaved_work_is_offered_once_its_session_closes,
an_exit_without_quit_offers_unsaved_changes_at_the_next_launch).
* Packaging: pin the Windows signing client and check its hash
* Packaging: pin appimagetool and check its hash
* Reject AppImage download for architectures without a pinned appimagetool
* Pin the AppImage runtime
* Audio: migrate file decoding to symphonia 0.6
Upstream bumped symphonia to 0.6 without adapting crates/audio-io, so
`soundcraft-audio-io` no longer built: the probe module, codec parameters
and decoded-buffer APIs all moved.
- Probe with `default::get_probe().probe(&Hint, …)`. The 0.5 prober's
detection-only path is gone, so scan for the container marker ourselves
before instantiating a reader, and keep running the existing size checks
(CAF/MP4/FLAC/WAV) on the first marker — the damaged-file regression
tests depend on those checks firing before any allocation.
- Skip leading ID3v2 tags with `ReadBytes::ignore_bytes` as before.
- Read `Track::codec_params` (now an `Option`), decode through
`make_audio_decoder` and `GenericAudioBufferRef::copy_to_vecs_planar`,
and read the new public `Packet` fields.
- Keep the per-packet frame limit and the sample sanitisation.
* Playback: migrate the audio device layer to cpal 0.18
- Sample rates are plain `u32` now: drop `.0` and the `cpal::SampleRate`
constructor.
- One `cpal::Error` replaces the build/stream/play/pause error types;
detect `DeviceNotAvailable` through `error.kind()`.
- Stream builders take `StreamConfig` by value, and device names come
from `Device::description()` (the deprecated `name()` is gone).
- The test `Fake` implements the new `StreamTrait::{buffer_size, now}`.
* Deps: pin egui back to 0.36 until craft-ui supports 0.37
The dependabot group bump (#767) moved the workspace to egui, eframe and
egui_kittest 0.37, but the pinned `craft-ui` revision still builds against
egui 0.36, so the graph carries two incompatible egui versions and
`soundcraft-ui-egui` does not compile. craft-libs#199 updates craft-ui to
0.37 and is still open; pin 0.36 and refresh the lockfile meanwhile. The
other version bumps from #767 stay.
Re-land egui 0.37 together with the craft-ui update.
* Audio: refuse a size-checked container the probe did not validate
The dependency-migration review showed the preflight size checks can
validate one container marker while symphonia's probe selects a different
container, so a misleading marker could skip the FLAC/MP4/CAF/WAVE checks.
Probe 0.6 reports the selected format only through the reader it returns,
so `open()` now records which container's checks ran (including the
RF64/BW64 RIFF forms) and refuses the stream when a size-checked reader
was chosen without its checks, before any packet is decoded. Markers found
in audio data of non-container streams (MP3, Ogg, AIFF) do not reject
valid files.
Adds a regression test with a fake RIFF prefix followed by a valid FLAC
stream, and doc comments for the functions touched by the dependency
migration (the review's docstring-coverage warning).
* Use chrono for UTC/calendar conversion instead of hand-rolled copies
Replaces the civil_from_days copy behind the desktop app's log timestamp with
chrono as a workspace dependency. The output format is unchanged and pinned by
the existing test. AGENTS.md now says time goes through chrono.
Follow-up to storytold/cadcraft#1785
* Never panic logging on a clock outside chrono's range
chrono's From<SystemTime> unwraps internally, so a clock past year 262143
panicked inside the logger. timestamp now reads the duration since the epoch
and goes through the fallible DateTime::from_timestamp, reading an
out-of-range clock as the epoch, exactly as the pre-epoch clamp already did.
The year is written with {:04} rather than %Y, which would prefix a '+' past
year 9999. Tests cover the far-future and year-10000 cases.
* 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 old out-of-range log timestamp (no epoch fallback)
`DateTime::from_timestamp(...).unwrap_or_default()` silently reset any clock
past chrono's year range to 1970-01-01, changing output the old hand-rolled
`civil_from_days` converted exactly. Restore the old conversion domain with a
cycle-based helper that routes the calendar through chrono (the calendar
repeats every 400 years = 146_097 days, so month/day/time come from chrono and
only the year is carried in i128, keeping every i64 exact). Keep the pre-1970
epoch clamp; u64 seconds past i64::MAX saturate rather than falling back.
Pinned old outputs in the test (replacing the u64::MAX/2 -> epoch assertion):
u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z
10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z
* Don't build a year-318857 SystemTime unchecked in the logging test
Windows' SystemTime only reaches about year 30828, so
UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before
timestamp() ran and failed the Windows test job. Build it with
checked_add and assert where the platform can hold it. The year-10000
case fits everywhere and stays unconditional.
* 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.
Bash scripts under packaging/ now use [[ ]] for tests, copy positional
parameters into named locals inside functions, and end env.sh helpers with
an explicit return (bare, so each keeps its exit status). shellcheck clean.
0.490_392_64 and 0.49039264 are the same f32 value; the underscored form
reads as a numeric suffix to the analyzer.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
stapler staple/validate fetch the notarization ticket from CloudKit, which
sometimes fails with "The network connection was lost" (Error 68). That
failed this release's macOS job twice in a row. Both steps are idempotent,
so retry up to 5 times with backoff, like hdiutil_retry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Calls storytold/craft-common-actions/.github/workflows/sonarcloud.yml@main
with secrets: inherit. Adds SonarCloud badge to README where missing and
removes the redundant sonar-project.properties (config now passed by the
reusable workflow).
Found while testing crates/model/src/automation.rs:73
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Widen the magnitude and drop-frame label arithmetic to i128 so i64::MIN stays exact and extreme drop-frame counts cannot overflow. Add round-trip regressions for all frame rates and both drop-frame variants.
Adds .coderabbit.yaml so CodeRabbit no longer reviews or comments on PRs opened by dependabot[bot].
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/model/src/automation.rs:190-203
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
`edit.nudge`'s "Plus/Minus" shortcut only matched `Key::Plus`, which a
real keyboard without a dedicated numpad `+` can never send: typing
"+" needs Shift, and Shift+=/- is already a separate, intentional
shortcut (Grid value). So nudge-right was unreachable on most
keyboards, while nudge-left worked fine ("-" needs no shift). Fixes
#736.
Add `Key::Equals` (the bare, unshifted key) to the nudge match arm,
the same way it already works for `Key::Minus`. Also exclude `m.alt`
from the arm, since `Alt+=` is the separate, already-registered
`window.event_list` shortcut that would otherwise be shadowed.
Verified live against a running demo session over the control channel
before writing the fix (plain "-" moved a clip; plain "=" and
Shift+"=" both did nothing), and again after. New regression test
covers bare Equals/Plus/Minus nudging, Shift+Equals still stepping the
Grid value instead, and Alt+Equals still opening the MIDI Event List
instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>