Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
1ee7824
fix(ci): include audio quality in JavaScript gates
fperex Sep 28, 2026
1dfa197
test(audio-quality): prove output and sampling safeguards
fperex Sep 28, 2026
e8e61e7
fix(shaper): verify traffic under each active profile
fperex Sep 28, 2026
227faa4
fix(audio): preserve the noise estimate through digital silence
fperex Sep 28, 2026
0667ad6
fix(audio): drain partial runs and park declared pauses
fperex Sep 28, 2026
6e41970
fix(mux): avoid registering a reader for skip diagnostics
fperex Sep 28, 2026
f47fcee
fix(relay): refuse new io_uring sessions during drain
fperex Sep 28, 2026
340f3cd
fix(watch): preserve noise estimate across digital silence
fperex Sep 28, 2026
09941b9
fix(watch): terminate replaced audio worklet processors
fperex Sep 28, 2026
66c3930
fix(watch): retain decoded sample rate during worker fallback
fperex Sep 28, 2026
ee9d7cd
fix(watch): refuse workers that never finish starting
fperex Sep 28, 2026
f8ee37a
fix(watch): wait for timing before building worker rings
fperex Sep 28, 2026
2e8bd11
fix(watch): keep worker ring reports off the page thread
fperex Sep 28, 2026
2c607c9
fix(watch): avoid waking video for submillisecond clock noise
fperex Sep 28, 2026
4c5a1bf
fix(watch): map playback clocks only through valid device time
fperex Sep 28, 2026
6a6ee9d
test(watch): run decoder recovery against virtual time
fperex Sep 28, 2026
10d8751
fix(watch): pace rendition promotion and learn sparse video cadence
fperex Sep 28, 2026
ad40bf8
fix(watch): present frame pairs without accumulating refresh lag
fperex Sep 28, 2026
88bba04
test(shaper): bound forwarding benchmark completion
fperex Sep 28, 2026
26f71e4
fix(net): release expiry guards at FIN and use Old resets
fperex Sep 28, 2026
f3e2f8a
fix(hang): bound paused readers and preserve marker events
fperex Sep 28, 2026
ad1380b
fix(publish): accept reordered capture callbacks after worker ticks
fperex Sep 28, 2026
3f1b754
style(watch): format regression fixes
fperex Sep 28, 2026
d209e30
fix(watch): retain decoded rates only for matching configurations
fperex Sep 28, 2026
c6c842c
fix(net): hold a parked track's warm cache until the upstream confirm…
kixelated Sep 25, 2026
2fe47e4
fix(net): judge spliced staleness against the logical live edge (#4103)
kixelated Sep 25, 2026
294ee03
fix(net): skip a stale warm cache on an IETF rejoin (#4150)
kixelated Sep 25, 2026
b3da592
fix(cli): close the relay connection on SIGINT and SIGTERM (#4287)
kixelated Sep 27, 2026
722f22a
fix(cli): handle SIGTERM during native playback
fperex Sep 28, 2026
9ed7494
fix(audio): preserve media time while draining across holes
fperex Sep 28, 2026
faea8b9
refactor(watch): trim playback code and cover 100 viewers
fperex Sep 28, 2026
6156401
docs(watch): remove stale playback comments
fperex Sep 28, 2026
aa816c7
chore: remove stale fork investigation notes and comments
fperex Sep 28, 2026
057e01b
fix(watch): exclude isolated outages from video cadence
fperex Sep 28, 2026
d836924
perf(audio): remove allocation and backlog scans from playout
fperex Sep 28, 2026
f0dc165
ci: measure native audio playout nightly
fperex Sep 28, 2026
55daa91
style: finish scope guard and test cleanup
fperex Sep 28, 2026
a7dd859
style(audio): keep benchmark manifest tables sorted
fperex Sep 28, 2026
7ab0463
perf(net): skip redundant anchors for an unbounded resumed track
fperex Sep 28, 2026
1071688
test(relay): isolate process-wide shutdown signals
fperex Sep 28, 2026
97d712e
fix(watch): keep stale GOPs out of video decode
fperex Sep 28, 2026
14bd9dd
fix(hang): keep historical loss behind the playhead
fperex Sep 28, 2026
3db9ba7
perf(net): skip drained resume anchor work
fperex Sep 28, 2026
84e98b3
test(net): track live fanout cost in nightly
fperex Sep 28, 2026
33374f1
fix(publish): disconnect each capture edge once
fperex Sep 29, 2026
d23787a
fix(publish): stop retired capture worklets
fperex Sep 29, 2026
ac22867
test(tokio): cover open warm edge rejoin
fperex Sep 29, 2026
57bf824
test(publish): await capture messages without sleeps
fperex Sep 29, 2026
fbec46a
test(tokio): release stale edge ownership directly
fperex Sep 29, 2026
5545dfd
fix(bench): send QUIC closes before exiting
fperex Sep 29, 2026
b0578b0
fix(audio): count playback skips at the ring
fperex Sep 29, 2026
6671646
fix(audio-quality): clear failed analyser replacements
fperex Sep 29, 2026
0251594
test(audio): attest the concrete ring in browser quality runs
fperex Sep 29, 2026
74aaab8
refactor(audio): limit shared ring resizing to growth
fperex Sep 29, 2026
10a1a5b
docs(audio-quality): distinguish isolation from ring transport
fperex Sep 29, 2026
8b2fcea
fix(audio-quality): measure signed stretch counters consistently
fperex Sep 29, 2026
2f010a0
test(browser): release console argument handles
fperex Sep 29, 2026
818c9ae
fix(audio): observe playback jumps at reader commit
fperex Sep 29, 2026
75280d4
fix(watch): keep pause clickable while buffering
fperex Sep 29, 2026
59c6a23
fix(net): expire takeover tails from the serving route
fperex Sep 29, 2026
c7faf59
fix(audio): count only concurrent overflow discards
fperex Sep 29, 2026
f212825
fix(net): wake takeover tails as the live edge advances
fperex Sep 29, 2026
ca48174
fix(cli): drain stdin in pipe-sized reads
fperex Sep 29, 2026
6b25efc
fix(watch): close the audio context once its render processor has sto…
fperex Sep 29, 2026
78923c1
fix(publish): close the capture context once its processor has stopped
fperex Sep 29, 2026
7f02213
test(watch): a reopened track must not step the picture back
fperex Sep 29, 2026
4844bc2
fix(watch): never step a reopened track's picture back
fperex Sep 29, 2026
2e6f92c
test(relay): a returning viewer is never served media from before it …
fperex Sep 29, 2026
bdec360
test(audio-quality): pass a silence breach only when every quiet wind…
fperex Sep 29, 2026
395ec81
test(audio-quality): give the quiet-proof fixture transients and cove…
fperex Sep 29, 2026
c3996e3
test(audio-quality): bracket quiet windows per report interval and fi…
fperex Sep 29, 2026
1f9e5e1
test(audio-quality): prove each analyser-lag segment of a row on its own
fperex Sep 29, 2026
84fac1c
fix(audio-quality): keep the publishers at real time after a stall
fperex Sep 29, 2026
7ede921
test(relay): a returning lite viewer of a paused publisher gets no st…
fperex Sep 29, 2026
9300273
feat(net): let a publisher mark a break where new subscriptions start
fperex Sep 29, 2026
38f75ef
fix(hang,publish): declare the break at every pause marker
fperex Sep 29, 2026
738f9f1
fix(watch): keep the held picture until a reopened track paints
fperex Sep 29, 2026
36bcd4b
test(audio): stop fake worklet processors after close as an engine does
fperex Sep 29, 2026
a17fe6d
fix(mux): declare the break at every discontinuity marker
fperex Sep 29, 2026
6d179bf
refactor(watch): one decode loop for legacy and CMAF video
fperex Sep 29, 2026
15e2e11
refactor(publish): one capture attempt for camera and microphone
fperex Sep 29, 2026
2e68919
test(audio-quality): drop the two metrics nothing measures
fperex Sep 29, 2026
b4addef
ci: nightly calls the audio quality workflow instead of a copy of it
fperex Sep 29, 2026
1064fe2
docs(watch,publish): cut history and repeats from comments
fperex Sep 29, 2026
5f2142a
test(audio-quality): keep the harness provenance in one place
fperex Sep 29, 2026
3534dd3
refactor(mux): drop the diagnostic fields from the slow-group log
fperex Sep 29, 2026
bf5407b
docs(rs): cut history and repeats from audio, cli, relay and tokio co…
fperex Sep 29, 2026
ee65505
refactor(watch): drop Sync's delay effect, which only copied the jitter
fperex Sep 29, 2026
1d45058
refactor(hang): drop the diagnostic fields from the slow-group log
fperex Sep 29, 2026
e62a519
test(hang): stub the stall monitor instead of spinning the CPU
fperex Sep 29, 2026
809faac
docs(watch): keep one copy of the ring transports' shared docs
fperex Sep 29, 2026
58150f5
docs(concept): drop the playout doc's history and fix the renderer claim
fperex Sep 29, 2026
63f82df
test(watch): count report and stretch work instead of timing it
fperex Sep 29, 2026
1203d1f
test(watch,hang): share the duplicated test fakes
fperex Sep 29, 2026
3939288
test(watch): run the rings' shared cases once over both transports
fperex Sep 29, 2026
ecddeb5
refactor(hang,publish): one writer for the legacy endpoint marker
fperex Sep 29, 2026
ee66ffa
perf(watch): no typed-array views on the audio thread in stretch, mer…
fperex Sep 29, 2026
b8a8aae
docs(watch,hang): cut comments that restate the playout doc or narrat…
fperex Sep 29, 2026
a10efb9
ci: run the audio worklet and report benchmarks nightly
fperex Sep 29, 2026
9eb2170
docs(watch): point the report benchmark section at the opt-in script
fperex Sep 29, 2026
e9ce90f
build(js): keep test support and benchmarks out of the published pack…
fperex Sep 29, 2026
ba715a5
perf(net): keep a resumed track's own groups off the anchor refresh
fperex Sep 29, 2026
2e67062
perf(net): shrink the relay's per-subscriber splice state
fperex Sep 29, 2026
a461694
perf(net): skip the drift anchor lock on an unanchored cursor
fperex Sep 29, 2026
492d0d2
perf(net): resolve a plain cursor's drift only for a group it judges
fperex Sep 29, 2026
634f9ab
perf(net): read a splice's live edge from the newest segment down
fperex Sep 29, 2026
769f488
perf(net): skip re-pushing an unchanged anchor onto a segment cursor
fperex Sep 29, 2026
d972fbd
test(net): pin delivery's revalidation of a pushed edge
fperex Sep 29, 2026
b38f8f4
perf(net): pick the drift edge with a match instead of an iterator chain
fperex Sep 29, 2026
2c34a8b
fix(watch): let a declared break re-open video GOP order
fperex Sep 29, 2026
626cfa2
ci(audio-quality): keep nightly and manual runs out of each other's g…
fperex Sep 29, 2026
0df56d5
perf(watch): stop allocating the shown picture record per painted frame
fperex Sep 29, 2026
57bdcc8
docs(watch): state the audio worklet CSP requirement
fperex Oct 1, 2026
ba8cc67
fix(hang): preserve burst cadence when timers are rationed
fperex Oct 1, 2026
6656034
docs: explain receiver gap heuristic limits
fperex Oct 1, 2026
bc26802
fix(test): pace the fixed audio quality control
fperex Oct 1, 2026
a3f1a74
fix(net): keep finished warm confirmations reusable by other fronts
fperex Oct 1, 2026
75584b8
fix(deps): replace the yanked yoke-derive lock entry
fperex Oct 1, 2026
df3d2b4
test(signals): measure retention of owned spawn tasks
fperex Oct 1, 2026
2a78f74
test(relay): advance auth expiry outside network waits
fperex Oct 1, 2026
ab075bb
test(relay): keep media active between auth clock steps
fperex Oct 1, 2026
a99d83d
test: retain audio quality traces after page close
fperex Oct 1, 2026
ac6461a
test: mock time for catalog completion selection
fperex Oct 1, 2026
19bcd9d
test: identify retained worker sockets by owned inode
fperex Oct 1, 2026
09a783a
test: report browser cleanup failures in media checks
fperex Oct 1, 2026
9b2d051
Merge pull request #57 from fperex/codex/fork-only-unslop
fperex Oct 1, 2026
c11d5cb
ci: bind Rust target caches to Nix inputs
fperex Oct 1, 2026
d5937f5
test(auth): await session end requests
fperex Oct 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set fallback
# Lint GitHub Actions workflows.
check:
python3 {{ source_directory() }}/scripts/install-formula.test.py
bun test {{ source_directory() }}/scripts/rust-cache.test.ts
{{ source_directory() }}/scripts/release.test.sh
@if command -v nfpm >/dev/null 2>&1 && command -v envsubst >/dev/null 2>&1 && command -v dpkg-deb >/dev/null 2>&1 && command -v rpm >/dev/null 2>&1; then python3 {{ source_directory() }}/../rs/scripts/package-rename.test.py; fi
@if command -v actionlint >/dev/null 2>&1; then actionlint; fi
Expand Down
65 changes: 65 additions & 0 deletions .github/scripts/rust-cache.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { describe, expect, test } from "bun:test";
import path from "node:path";

type Mapping = { [key: string]: unknown };

const workflows = [
{ file: "interop.yml", job: "interop" },
{ file: "nightly.yml", job: "nightly" },
];

const github = path.resolve(import.meta.dir, "..");

function mapping(value: unknown, label: string): Mapping {
if (value === null || typeof value !== "object" || Array.isArray(value)) {
throw new TypeError(`${label} is not a YAML mapping`);
}

return { ...value };
}

function sequence(value: unknown, label: string): unknown[] {
if (!Array.isArray(value)) throw new TypeError(`${label} is not a YAML sequence`);
return value;
}

async function yaml(file: string): Promise<Mapping> {
return mapping(Bun.YAML.parse(await Bun.file(file).text()), file);
}

describe("Rust cache boundary", () => {
for (const workflow of workflows) {
test(`${workflow.file} uses the flake-keyed local cache action`, async () => {
const file = path.join(github, "workflows", workflow.file);
const document = await yaml(file);
const jobs = mapping(document.jobs, `${file}: jobs`);
const job = mapping(jobs[workflow.job], `${file}: jobs.${workflow.job}`);
const steps = sequence(job.steps, `${file}: jobs.${workflow.job}.steps`).map((step, index) =>
mapping(step, `${file}: jobs.${workflow.job}.steps[${index}]`),
);
const cache = steps.find((step) => step.name === "Rust cache" || step.name === "Rust Cache");

expect(
steps.some((step) => typeof step.uses === "string" && step.uses.startsWith("Swatinem/rust-cache@")),
).toBeFalse();
expect(cache?.uses).toBe("./.github/actions/rust-cache");
});
}

test("cache and restore keys bind the runner and pinned Nix toolchain", async () => {
const file = path.join(github, "actions/rust-cache/action.yml");
const document = await yaml(file);
const runs = mapping(document.runs, `${file}: runs`);
const steps = sequence(runs.steps, `${file}: runs.steps`).map((step, index) =>
mapping(step, `${file}: runs.steps[${index}]`),
);
const cache = steps.find(
(step) => typeof step.uses === "string" && step.uses.startsWith("jdx/mr-boxington-action@"),
);
const inputs = mapping(cache?.with, `${file}: cache inputs`);
const prefix = `\${{ runner.os }}-\${{ runner.arch }}-mbx-target-\${{ inputs.scope }}-\${{ hashFiles('flake.nix', 'flake.lock', 'rust-toolchain.toml') }}-`;

expect(inputs["restore-keys"]).toBe(prefix);
expect(typeof inputs["cache-key"] === "string" && inputs["cache-key"].startsWith(prefix)).toBeTrue();
});
});
27 changes: 18 additions & 9 deletions .github/workflows/audio-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ name: Audio quality
# playout engine on a simulated clock: seconds, no browser, and the same numbers
# on every run, so its budgets are exact and a breach is always the change under
# review. The Chromium matrix is half an hour of real-time playback whose every
# number moves with the runner's load, which is why nightly.yml owns it; the
# `workflow_dispatch` button runs the same job for a branch that wants its
# answer before tomorrow.
# number moves with the runner's load, which is why nightly.yml calls it rather
# than a pull request; the `workflow_dispatch` button runs it for a branch that
# wants its answer before tomorrow.
#
# The filter is the code the replay lane runs, the player (js/watch) and the
# estimator it replays through (js/hang), plus the harness with its budgets and
Expand All @@ -31,10 +31,14 @@ on:
- "rs/moq-audio/**"
- "test/audio-quality/**"
- ".github/workflows/audio-quality.yml"
workflow_call:
workflow_dispatch:

# A called workflow sees its caller's workflow and event, so the nightly run
# (scheduled or dispatched) and this workflow's own dispatch never share a
# group, while a repeat of the same kind still supersedes the last.
concurrency:
group: audio-quality-${{ github.ref }}
group: audio-quality-${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true

jobs:
Expand Down Expand Up @@ -88,13 +92,13 @@ jobs:
retention-days: 7
if-no-files-found: ignore

# The nightly job, on demand: the replay lane, then the Chromium matrix over
# the seeded UDP shaper, graded against `test/audio-quality/budgets.json`.
# Keep the two in step.
# The replay lane, then the Chromium matrix over the seeded UDP shaper, graded
# against `test/audio-quality/budgets.json`. A called workflow sees its
# caller's event, so this runs for the nightly schedule too.
matrix:
name: Audio quality
if: github.event_name == 'workflow_dispatch'
# x64 for Playwright's Chromium, as in nightly.yml.
if: github.event_name != 'pull_request'
# x64 for Playwright's Chromium, as interop.yml and wasm.yml run theirs.
runs-on: ubuntu-latest
# Two dozen rows at a minute each is about half an hour on its own, and the
# relay, the CLI, and the shaper build from source ahead of them.
Expand Down Expand Up @@ -143,6 +147,11 @@ jobs:
run: nix develop --command just test audio-quality --enforce
shell: bash -leo pipefail {0}

- name: Shared audio ring quality
if: ${{ !cancelled() }}
run: nix develop --command just test audio-quality --profiles fixed-250 --rings isolated --offload false --enforce
shell: bash -leo pipefail {0}

# The run directory is the whole diagnosis: each process's log, the
# shaper's counters, the raw ndjson, the per-row summaries, and a
# Playwright trace of any page that failed. The harness keeps it on a
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/interop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,7 @@ jobs:
# Reuse ~/.cargo + ./target across nightly runs so only changed crates
# recompile (the relay, cli, moq-ffi, and libmoq all build from source).
- name: Rust cache
uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
with:
cache-on-failure: true
uses: ./.github/actions/rust-cache

# Playwright's Chromium isn't in the nix devShell. Install it plus its apt
# runtime libs (`--with-deps` needs the host package manager) so the
Expand Down
106 changes: 35 additions & 71 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,8 @@ jobs:
with:
determinate: false

- name: Rust Cache
uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
with:
cache-on-failure: true
- name: Rust cache
uses: ./.github/actions/rust-cache

# Runs even if the audit fails: a fresh advisory shouldn't hide a
# compile break in the feature permutations.
Expand All @@ -86,10 +84,42 @@ jobs:
nix develop --command bun install --frozen-lockfile
nix develop --command bun js/net/bench/broadcasts.ts

# Timing sweeps for the audio worklet's render quantum and the page's worker reports. They
# print tables and assert nothing about time; the unit tests hold the counts.
- name: JS audio benchmarks
if: ${{ !cancelled() }}
run: |
nix develop --command bun js/watch/src/audio/stretch.bench.ts
nix develop --command bun js/watch/src/audio/worker/remote.bench.ts

- name: Audit
if: ${{ !cancelled() }}
run: nix develop --command just rs audit

- name: Shaper forwarding benchmark
if: ${{ !cancelled() }}
run: >-
nix develop --command cargo bench --locked -p moq-shaper --bench forward --
--warm-up-time 1 --measurement-time 2 --sample-size 10

- name: Resumed track delivery benchmark
if: ${{ !cancelled() }}
run: >-
nix develop --command cargo bench --locked -p moq-net --bench resume --
--warm-up-time 1 --measurement-time 2 --sample-size 10 --noplot

- name: Track fanout benchmark
if: ${{ !cancelled() }}
run: >-
nix develop --command cargo bench --locked -p moq-net --bench track --
track_fanout_group --warm-up-time 1 --measurement-time 2 --sample-size 10 --noplot

- name: Audio playout benchmark
if: ${{ !cancelled() }}
run: >-
nix develop --command cargo bench --locked -p moq-audio --bench playout --
--warm-up-time 1 --measurement-time 2 --sample-size 30 --noplot

# The OBS plugin and libmoq's C fixtures are the only things that link
# libmoq.a from outside cargo, so a stale `rs/libmoq/native-libs/*.txt`
# breaks them and nothing else. obs.yml lists the inputs that can do that,
Expand Down Expand Up @@ -153,75 +183,9 @@ jobs:

# Plays the matrix in headless Chromium over the seeded UDP shaper and grades
# what the listener would have heard against `test/audio-quality/budgets.json`.
#
# `ubuntu-latest` rather than the arm runner the jobs above use: this is the
# third lane in the repository to launch Playwright's Chromium, and interop.yml
# and wasm.yml both run theirs on x86 for that reason.
#
# audio-quality.yml runs this same job on demand. Keep the two in step.
audio-quality:
name: Audio quality
runs-on: ubuntu-latest
# Two dozen rows at a minute each is about half an hour on its own, and the
# relay, the CLI, and the shaper build from source ahead of them.
timeout-minutes: 90

env:
# Somewhere `upload-artifact` can reach. The harness's default is under
# $TMPDIR, which is outside the workspace.
MOQ_TEST_RUNS: ${{ github.workspace }}/test-runs

steps:
- name: Free disk space
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # main
with:
tool-cache: false

- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: DeterminateSystems/nix-installer-action@1d87d45818068401a10cf16bdc5f00b24994a83f # main
with:
determinate: false

- name: Rust cache
uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
with:
cache-on-failure: true

# Playwright's Chromium isn't in the nix devShell. Install it plus its apt
# runtime libs (`--with-deps` needs the host package manager); run.sh's own
# `playwright install` then no-ops.
- name: Playwright Chromium
run: nix develop --command bash -c "bun install --frozen-lockfile && bunx playwright install --with-deps chromium"
shell: bash -leo pipefail {0}

# Seconds, and no browser or relay involved, so it runs ahead of the matrix
# and a regression in the ring or the estimator is reported without waiting
# half an hour for the rows that need Chromium.
- name: Audio quality (replay)
run: nix develop --command just test audio-quality --runtime replay --enforce
shell: bash -leo pipefail {0}

- name: Audio quality
run: nix develop --command just test audio-quality --enforce
shell: bash -leo pipefail {0}

# The run directory is the whole diagnosis: each process's log, the
# shaper's counters, the raw ndjson, the per-row summaries, and a
# Playwright trace of any page that failed. The harness keeps it on a
# failure without being asked. Short retention because it is large and
# only useful while the failure is fresh.
- name: Keep the failing run
if: ${{ failure() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: audio-quality-run
path: test-runs/
retention-days: 7
if-no-files-found: ignore
uses: ./.github/workflows/audio-quality.yml

# Every tag-driven release workflow, run against main with publishing skipped:
# the workflows gate their publish jobs on `github.ref_type == 'tag'`, and no
Expand Down
6 changes: 4 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading