Skip to content

Bump vite from 6.4.3 to 8.0.16 in /web - #10

Closed
dependabot[bot] wants to merge 2 commits into
mainfrom
dependabot/npm_and_yarn/web/vite-8.0.16
Closed

Bump vite from 6.4.3 to 8.0.16 in /web#10
dependabot[bot] wants to merge 2 commits into
mainfrom
dependabot/npm_and_yarn/web/vite-8.0.16

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jun 4, 2026

Copy link
Copy Markdown
Contributor

Bumps vite from 6.4.3 to 8.0.16.

Release notes

Sourced from vite's releases.

v8.0.16

Please refer to CHANGELOG.md for details.

v8.0.15

Please refer to CHANGELOG.md for details.

v8.0.14

Please refer to CHANGELOG.md for details.

v8.0.13

Please refer to CHANGELOG.md for details.

v8.0.12

Please refer to CHANGELOG.md for details.

v8.0.11

Please refer to CHANGELOG.md for details.

v8.0.10

Please refer to CHANGELOG.md for details.

v8.0.9

Please refer to CHANGELOG.md for details.

v8.0.8

Please refer to CHANGELOG.md for details.

v8.0.7

Please refer to CHANGELOG.md for details.

v8.0.6

Please refer to CHANGELOG.md for details.

v8.0.5

Please refer to CHANGELOG.md for details.

v8.0.4

Please refer to CHANGELOG.md for details.

create-vite@8.0.3

Please refer to CHANGELOG.md for details.

v8.0.3

Please refer to CHANGELOG.md for details.

create-vite@8.0.2

Please refer to CHANGELOG.md for details.

v8.0.2

Please refer to CHANGELOG.md for details.

... (truncated)

Changelog

Sourced from vite's changelog.

8.0.16 (2026-06-01)

Bug Fixes

8.0.15 (2026-06-01)

Features

Bug Fixes

  • capitalize error messages and remove spurious space in parse error (#22488) (85a0eff)
  • deps: update all non-major dependencies (#22511) (2686d7d)
  • dev: fix html-proxy cache key mismatch for /@fs/ HTML paths (#21762) (47c4213)
  • glob: error on relative glob in virtual module when no files match (#22497) (5c8e98f)
  • optimizer: close the rolldown bundle when write() rejects (#22528) (e3cfb9d)
  • resolve: provide onWarn for viteResolvePlugin in JS plugin containers (#22509) (40985f1)

Miscellaneous Chores

Code Refactoring

8.0.14 (2026-05-21)

Features

Bug Fixes

  • deps: update all non-major dependencies (#22471) (98b8163)
  • dev: handle errors when sending messages to vite server (#22450) (e8e9a34)
  • html: handle trailing slash paths in transformIndexHtml (#22480) (5d94d1b)
  • optimizer: pass oxc jsx options to transformSync in dependency scan (#22342) (b3132da)

Miscellaneous Chores

  • deps: update rolldown-related dependencies (#22470) (7cb728e)
  • remove irrelevant commits from changelog (2c69495)

Code Refactoring

  • glob: do not rewrite import path for absolute base (#22310) (0ae2844)

... (truncated)

Commits

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

troykelly and others added 2 commits June 4, 2026 11:01
Multiview — an efficient, hardware-accelerated Rust live video multiview/mosaic
generator. It ingests many live sources (RTSP/HLS/SRT/RTMP/NDI/file/synthetic),
composites them into a templated grid on CPU or GPU, and writes HLS/file output;
the encode-once-mux-many design fans one encode to many transports.

Highlights:
- Fixed-cadence output clock — one valid frame per tick, never stalls; inputs are
  sampled, never pacing (the cardinal continuous-output invariant).
- Custom CPU/GPU compositor with a fixed linear-light colour pipeline (NV12
  throughout); per-tile overlays, captions (HLS WebVTT + DVB-sub), analog/wall
  clocks, audio meters, and fault badges.
- 16-crate Rust workspace under strict typing + TDD + adversarial-review
  guardrails; LGPL-clean default build (GPL codecs + NDI opt-in).
- Multi-arch Docker images, docker compose examples, and SemVer release automation.

Status: early stage — the engine ingests, composites, encodes, and writes HLS/file
output today; the web UI, control API, and live RTSP/NDI/RTMP output servers are
built as libraries and on the near-term roadmap (see ROADMAP.md). Dual-licensed
MIT OR Apache-2.0.

Developed iteratively with AI assistance; the full internal development history is
retained privately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.4.3 to 8.0.16.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.16/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.16
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Jun 4, 2026
@troykelly troykelly closed this Jun 4, 2026
@dependabot @github

dependabot Bot commented on behalf of github Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting @dependabot ignore this major version or @dependabot ignore this minor version. You can also ignore all major, minor, or patch releases for a dependency by adding an ignore condition with the desired update_types to your config file.

If you change your mind, just re-open this PR and I'll resolve any conflicts on it.

@dependabot
dependabot Bot deleted the dependabot/npm_and_yarn/web/vite-8.0.16 branch June 4, 2026 11:17
troykelly pushed a commit that referenced this pull request Jun 7, 2026
…-L1 §2/§3)

Complete the safe NDI handle set on the NdiV6 resolver:

- NdiReceiver (recv.rs): non-blocking video sampling (inv #1/#2/#10 —
  sampled, never pacing). capture_video returns the latest frame or None
  on timeout/non-video; audio + metadata are null sinks so only video is
  allocated. RecvVideoFrame owns the SDK buffer and frees it
  exactly-once on Drop, borrowing the receiver (&'r) so the free target
  is alive by construction (no dangling free is expressible). UYVY_BGRA
  color format, highest bandwidth.
- NdiFinder (find.rs): source discovery. current_sources copies each
  name into an owned NdiSourceName before returning — callers never hold
  a pointer into the finder's transient array.
- Runtime init (table.rs): NdiV6::ensure_initialized() calls
  NDIlib_initialize (idempotent); every handle calls it on construct.
  Sending works without it, but discovery (advertise/browse) needs it.

All unsafe stays in this crate; consumers stay forbid(unsafe_code).

Hardware-validated on the SDK-equipped x86_64 box
(tests/live_loopback.rs): one process sends a UYVY luma gradient,
discovers its own source via the finder, connects a receiver, and
captures the frame back — 64x64 UYVY, mean luma round-trips at 127.0
(the sent gradient mean). NDI carries video over SpeedHQ (visually
lossless, not bit-exact), so the assertion is structural (geometry +
mean-luma band), matching the GPU/codec SSIM/PSNR testing tier. (The
headless box has no mDNS, so discovery is backed by the SDK's
ndi-discovery-server via ndi-config.v1.json — a host-side test harness
concern, nothing in the repo.)

clippy -D warnings green default AND --features bindings; fmt clean;
default build stays LGPL-clean (recv/find gated on `bindings`).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 7, 2026
Wire the production NdiReceiver: `SdkNdiReceiver` delegates the receive
seam (receive → sample) to the safe NdiReceiver from multiview-ndi-sys.
multiview-input stays forbid(unsafe_code). NdiProducer is unchanged
(generic over dyn NdiReceiver), so swapping FakeNdiReceiver for
SdkNdiReceiver is the only difference between a unit test and live
ingest.

- Sampled, never pacing (inv #1/#2/#10): each receive() is a bounded
  non-blocking capture on the ingest thread; no frame this instant →
  ReceivedFrame::None (last-good held, tile rides its state machine).
- Copies pixels out of SDK-owned memory before the RecvVideoFrame drops
  (free-exactly-once); negative NDI timecode → genpts fallback; a
  malformed geometry is a typed skip, never a panic.
- Drop order: receiver before capability (the receiver holds fn pointers
  into the capability's still-mapped Library).
- New opt-in feature `ndi-bindings` = ndi + multiview-ndi-sys/bindings
  (build-time bindgen over the licensed header); plain `--features ndi`
  stays SDK-free and CI-buildable. Default build stays LGPL-clean.
- ndi-sys: NdiRuntime is now Send (the SDK table is process-global +
  immutable, the Library is Send) so a live receiver can move onto the
  ingest thread; deliberately not Sync (owned, never shared by ref).

Hardware-validated on the SDK-equipped x86_64 box (tests/ndi_live.rs,
live_ingest): a sys NdiSender publishes a UYVY gradient, the finder
discovers it, and the production NdiProducer over SdkNdiReceiver yields
a 64x64 NV12 ProducedFrame (pixels=6144 = w*h*3/2) — proving the whole
ingest path: recv_capture → recv_free → ReceivedVideoFrame → UYVY→NV12
→ ProducedFrame. The CI-safe probe test is retained.

clippy -D warnings green: default, --features ndi (SDK-free), and
--features ndi-bindings (on box); fmt clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 7, 2026
…loudflare first)

Design for automatic TLS on the multiview-control plane, from the
acme-tls-research ultracode workflow (4 grounded lanes + synthesis):

- DNS-01 challenge ONLY (HTTP-01/TLS-ALPN-01 rejected — need wildcards +
  no inbound :80/:443 reachability).
- rustls, no openssl: instant-acme 0.8.5 (Apache-2.0, DNS-01 first-class);
  rustls-acme rejected (TLS-ALPN-01-only trap); axum-server tls-rustls for
  hot cert reload (RustlsConfig::reload_from_pem, atomic swap, no restart).
- Pluggable async DnsProvider trait (object-safe, opaque TxtRecordHandle
  newtype — not dyn Any); Cloudflare the first impl, hand-written over
  reqwest+rustls (the official `cloudflare` crate is BSD-3 + native-tls →
  rejected). Route53/GCP/RFC2136 drop in as one impl + one config variant.
- Renewal = detached fail-soft background tokio task (⅓-lifetime + jitter,
  keep serving the existing cert on failure) — cannot violate inv #10.
- Least-privilege Cloudflare token (Zone.DNS:Edit, single zone) via
  1Password/env, never committed; CAA + RFC 8657 account binding;
  staging-first; authoritative-NS propagation polling.
- Tiered: TLS-0 static cert → TLS-1 ACME core → TLS-2 DnsProvider+CF →
  TLS-3 config+hot-reload renewal → TLS-4 hardening/isolation soak.
  Off-by-default `tls`/`acme` features; default build stays plain-HTTP +
  LGPL-clean.

Adds docs/research/acme-tls.md + ADR-0029 (Proposed) + the TLS-0..4
backlog in work-schedule.md; indexes both READMEs. Also indexes ADR-0028
(NDI), which was unlisted. Docs only — no code. (Backlog task #70.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 7, 2026
…UX, backend)

The control plane required a token for every privileged route + the
realtime WS, but the SPA had no way to discover that or to disable it —
so an un-authenticated browser just looped "reconnecting". Backend half
of the fix (task #71):

- AppState.auth_disabled (default false — secure) + with_auth_disabled()
  builder + auth_required(). When set, BOTH auth paths (the REST
  `Principal` extractor and the realtime `resolve_principal`)
  short-circuit to Principal::local_admin() (Role::Admin, unscoped) so
  the whole API + WS/SSE are open with no token.
- New UNAUTHENTICATED GET /api/v1/auth/status → { auth_required,
  authenticated }: the SPA reads it to decide whether to show a login
  gate, and validates an entered key by calling it with the token
  (authenticated reflects the presented credential). Leaks nothing else.
- CLI: MULTIVIEW_CONTROL_AUTH=disabled|off|none|0 turns auth off (env
  wins; anything else keeps it ON), with a loud WARN that the listener
  is open — use only on a trusted/local network.

Secure by default: unset/any-other value = auth required. Inv #10
untouched (no engine coupling).

Tests (tests/auth_disabled.rs): auth-on → protected route 401 without a
token, /auth/status reports {required:true, authenticated:false}, and
{authenticated:true} with a valid token; auth-off → protected route 200
with no token, status {required:false, authenticated:true}. Existing
auth (7) + openapi (9) tests unaffected. fmt + clippy -D warnings clean;
cargo check --workspace green.

Next: the SPA login gate (show a key-entry page when auth_required and
unauthenticated; skip it when not required).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 7, 2026
…ision)

Replace the "passthrough = sanctioned exception to invariant #1" framing
with an invariant-#1-PRESERVING **guarded passthrough**, from an
adversarially-verified fan-out (12 load-bearing claims, 8 held / 4 refined):

- A passthrough is a degenerate, compositor-less `EngineRuntime` ticking at
  the program cadence (never `.await`s the demuxer); the byte copy runs on a
  separate egress thread. Source-paced for DATA, clock-paced for the LIVENESS
  decision via a wait-free `PacketLiveness` watchdog (fails safe).
- On loss it splices a **pre-baked, param-matched, IDR-led slate** (black /
  SMPTE bars + 1 kHz tone / silence), encoded ONCE (no held NVENC session),
  replayed through the existing encoder-less `PacketMuxSink` — so it meets the
  operator's BLACK/SMPTE+tone rule at packet-copy steady-state cost.
- Robustness ladder (cheapest valid rung first): matched-slate-splice →
  container-discontinuity → full-transcode; per-program `robustness_floor`
  (default `SlateOnLoss`; `PassthroughFallback`/Reject removed).

Folds the four refinements: recovery gates on a strict-IDR classifier
(`is_idr`, not `is_key` — CRA/recovery-point mis-splice); the per-stream
monotonic clamp+offset (not FFmpeg flags) is the non-monotonic-DTS abort
guard; explicit dedicated egress thread + drop-oldest/SINK_WEDGE_GRACE detach
for isolation (#10); three prerequisite code gaps (Demuxer interrupt+rw_timeout,
is_idr, in-band-PS/Annex-B BSF). Adds the GP-0..12 backlog; supersedes MP-3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 7, 2026
…(topic `system`)

Keystone for the cpu/gpu/encoder-decoder dashboard. Per operator guidance, LIVE
high-rate metrics are PUSHED over the realtime stream (conflated + drop-oldest,
like `audio.meters`), never polled via REST — only cold historic windows (the
data decisions are made from) are RESTful.

- `SystemMetrics` + `GpuMetrics` + `GpuVendor` wire types (numeric only) and a new
  `Event::SystemMetrics` variant (`t="system.metrics"`), internally-tagged.
- `Topic::System` is now classified high-rate: latest-only, excluded from the
  lossless replay ring; the engine never back-pressures a slow client (inv #10).
- AsyncAPI generator documents the new message + schemas.
- Round-trip + high-rate + GPU-free-host contract tests. clippy/fmt/test green;
  `cargo check --workspace` green (no broken exhaustive matches).

Next: an off-hot-path poller (CPU /proc/stat + NVML) publishing on `system`, then
a desktop footer with sparklines (+ a System detail tab).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 7, 2026
…e engine event stream

Add an off-hot-path system-metrics task (~1.3 Hz) that samples whole-system
CPU (/proc/stat), host memory (/proc/meminfo), and per-GPU load, assembles a
multiview_events::SystemMetrics, and PUSHES Event::SystemMetrics onto the
engine's drop-oldest event broadcast so the WebUI footer lights up with live
data (pushed over the realtime stream, never polled — inv #10).

- multiview-hal: object-safe `LoadSource` seam + always-compiled
  `NullLoadPoller` (no-GPU) and `cuda`-gated `NvmlLoadPoller` over the existing
  NVML probe; graceful init-or-null fallback, no GPU/toolchain needed to build.
- multiview-cli: `system_metrics` module with a pure, unit-testable
  `assemble_metrics` (DeviceLoad -> GpuMetrics, Vendor -> GpuVendor, optional
  NVENC fields, GPU-free host -> empty gpus, honest-None unknowns) and the
  tokio task spawned in the run path next to the engine publisher; self-stops
  on the run's StopSignal. New cli `cuda` feature selects the NVML poller
  (implied by `nvidia`), else the null poller; default build stays pure-Rust,
  GPU-free, LGPL-clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 7, 2026
…0..14 backlog)

From a verification-hardened fan-out (20 agents; 10/12 load-bearing claims held,
2 refined). Answers the operator's delivery concerns BEFORE implementation:

- Serving model bifurcated by latency tier, not file type: master playlist +
  closed segments + init.mp4 are STATIC-frontable (nginx/traefik/CDN/object
  store with the right headers); the LL-HLS live playlist + blocking-reload
  (_HLS_msn/_HLS_part) + byte-range parts are served by Multiview's OWN async
  axum origin (a static server has no "wait until it exists" primitive). The
  origin is a client of an engine-published drop-oldest snapshot — hard-capped +
  time-bounded held GETs, can never back-pressure the engine (inv #10).
- CMAF/fMP4 default container (Safari-native LL-HLS needs it; TS opt-in legacy);
  byte-range parts into one growing .m4s, not discrete per-part files.
- Configurable locations: base_url (no-op when unset), segment_dir split,
  init_name; rolling-playlist + DVR + atomic-publish (temp+fsync+rename) +
  grace-period deferred-unlink pruning foundation; drop the unbounded program.ts.
- Header contract (explicit Content-Type incl. the .m4s gap, AWS-style
  Cache-Control tiers, Origin-reflecting CORS + Vary, Accept-Ranges) + reference
  nginx/traefik/CDN configs operators only honor, not invent.

Folds the two review corrections: fMP4 is NOT a muxer-name swap (needs movflags
AVDictionary plumbing + per-part fragment flush + shared init); stock nginx
already serves .m3u8/.ts correctly, so the present bugs are no-Cache-Control /
no-CORS / no-Accept-Ranges (the .m4s MIME gap is post-CMAF). HLS-0..14 backlog,
encode-once (#7) preserved. Supersedes the parked live-playlist 404 fix (now HLS-0/1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 8, 2026
…ilover (ADR-0030)

Assemble the merged guarded-passthrough primitives (GP-1 is_idr, GP-4 BakedSlate,
GP-5 PacketLiveness, GP-6 RestampAccumulator) into the live copy-vs-slate failover
splice seam: a GuardedPacketSource that implements PacketSource and is the sole
producer feeding PacketMuxSink::run_av.

- Copies the live input while healthy; on loss flips a wait-free AtomicU8 decision
  flag (the GP-5 watchdog should_splice, Release-written by evaluate, Acquire-read
  by next_packet) to SLATE and emits the pre-baked slate, looped by advancing the
  GP-6 restamp offset per wrap.
- Re-stamp via one RestampAccumulator per stream (video + audio) across BOTH seams;
  the monotonic clamp (last_dts+1) is the abort guard, raw deltas (B-frame reorder)
  preserved; rebase at input->slate, each slate loop wrap, and slate->input recovery.
- Recovery is is_idr-gated (GP-1 is_idr, NOT is_key): discard input until a true
  strict-IDR video AU before resuming copy — an is_key recovery-point I-frame never
  re-enters.
- Threading: per-call decision self-driven from an injected MonotonicClock in the
  pull model, equivalently driveable by a clock thread (GP-8) via decision_flag();
  fail-safe (a stale liveness read biases to SLATE, never false-LIVE). next_packet
  never blocks on the input; the drop-oldest + SINK_WEDGE_GRACE detach posture
  wrapping the mux thread sheds a wedged push peer (#1/#10).

Additive only: a new guarded module + GuardedPacketSource (ffmpeg-gated) + a
non-optional pure-Rust multiview-framestore dep; the existing SegmentState /
PacketMuxSink batch paths are untouched. No unsafe, no FFI (forbid(unsafe_code));
calls multiview-ffmpeg safe wrappers only.

Tests (RED->GREEN): healthy copy-through restamped; input->slate splice past the
threshold with strictly-increasing DTS; is_idr-gated recovery (+ never on is_key);
slate loop across many wraps with no DTS discontinuity; fail-safe to slate; a
proptest over arbitrary live/slate sequences; and an end-to-end drive through the
REAL strict-DTS MP4 muxer (av_interleaved_write_frame aborts on non-monotonic DTS)
confirming the seam muxes a decodable container.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 8, 2026
…ms realtime (ADR-0034)

Surface each input's StreamInventory (RT-2) as read-only discovery so the
API/UI can show every elementary stream an input offers.

- events: Event::InputStreams{input_id, inventory: StreamInventory} on the
  existing Topic::Inputs (internally-tagged input.streams, never untagged);
  round-trip + envelope tests; AsyncAPI message + schema.
- cli: probe each path-backed source's StreamInventory once at build time
  (off the output-clock thread), fold it into the conflated EngineStateSnapshot
  under inputs.<id>.streams, and emit exactly one input.streams delta per
  probed input at run start (inv #10 — never on the hot loop).
- control: GET /api/v1/inputs/{id}/streams reads the off-engine cached snapshot
  (never touches the output-clock thread); BOLA authorize_object; 404 unknown/
  unprobed; RFC 9457 problem; utoipa StreamInventoryDoc mirror + pin test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 8, 2026
…the placement loop (ADR-0035)

From a verification-hardened fan-out (21 agents; 11/14 claims held, 3 refined).
Key finding: the placement engine is ~90% BUILT but never WIRED — the cost model
(cost.rs), the DRF placement policy (select.rs select_device), the engine
controller (placement.rs observe), and the degradation loop (degrade.rs) all exist,
pure + tested, but are NEVER called with live loads in the run; the LoadSource feeds
only the UI footer. And the silent GPU→CPU fallback is computed-then-discarded:
EngineRuntime::backend_kind() already knows it fell back, but that fact reaches
NOTHING — not an event, not a warning, not a hardware cross-check.

ADR-0035 defines the sense→detect→warn→plan→apply subsystem on the EXISTING parts:
- DETECT: probe actually-usable backends (wgpu adapter device_type, NVDEC/NVENC
  via libav) + cross-check vs NVML-discovered hardware.
- WARN: an actionable HealthWarning catalog (gpu-present-no-vulkan-adapter w/ the
  libvulkan/graphics remediation, software-decode/encode-on-gpu-host, nvenc-ceiling,
  vram-pressure, cpu-saturation, degradation-active), LATCHED/debounced, with a
  load-bearing NO-FALSE-POSITIVE rule (fires only on hardware-present AND
  software-tier-resolved → zero warnings on a CPU-only/software host).
- PLAN+APPLY: wire LoadSource→select_device/PlacementController into the run, net of
  co-tenant (ours-vs-total) load, affinity-preserving (never fragment a pipeline,
  GPU-placement principle), inv #1/#9/#10 — the runtime re-plan runs OFF the
  output-clock thread (NOT the per-tick hook; folded from the adversarial review).

SA-0..N backlog; SA-0 = the smallest win: detect+warn the compositor mismatch so the
silent fallback that burned 5 CPU cores becomes a clear banner + /api/v1/health.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 8, 2026
…tor fallback (ADR-0035)

DETECT (multiview-hal): composite_probe — classify the wgpu adapter (device_type
!= Cpu, with the llvmpipe/lavapipe/swiftshader driver guard, Gl never a blanket
exclusion) and the both-halves MISMATCH cross-check (hardware discovered via
DeviceLoad>=1 OR EnvProbe present, AND composite resolved software/CPU). The
no-false-positive rule is structural: a GPU-free or software-only host trips
neither half.

WARN (multiview-events): HealthWarning { code, severity, subsystem, message,
remediation, since, active } as a richer sibling of Alert (never mutating Alert),
+ Event::HealthWarningRaised/Cleared routed on Topic::Alerts, latched code
gpu-present-no-vulkan-adapter, registered in AsyncAPI.

SURFACE (multiview-control): WarningRepository + warning_ingest (copy of
alarm_ingest: lagged-skip, swallow-and-skip; inv #10) + GET /api/v1/health (RBAC
read, RFC 9457) + emit_capability_warnings helper (the catalog copy/remediation),
registered in OpenAPI.

UI (web): useHealth hook + HealthBanner (severity icon+text, message, code,
remediation; colour-independent WCAG; renders nothing when clean) mounted
globally; Lingui strings extracted+compiled.

The cli build path makes one thin gpu-gated call (capability_warn::probe_and_emit
+ control's run_warning_ingest wiring) — build-time, off the output-clock thread
(inv #1), emitting only through the drop-oldest publisher (inv #10). CPU fallback
preserved; the silent info-only fallback becomes a loud warn log + banner +
/api/v1/health entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 8, 2026
…no per-frame alloc churn) (EFF)

GREEN for EFF-0 (efficiency-budget §2.4 / safety rule §5: "frame buffers come
from per-device pools allocated at start, never per-frame").

Before, the wgpu compositor created its full transient GPU surface set on every
`composite()` call — the Rgba16Float linear canvas (~16.6 MB @1080p, ~66 MB
@4k), the tile texture arrays, the NV12 y_out/uv_out planes, two padded readback
buffers, the composite/encode uniform + tile-params buffers, and (overlay) a
second overlaid canvas + overlay uniform/prim buffers + atlas — then freed them
via Drop. That is ~26 MB/tick no-overlay (~42 MB/tick overlay), ~2.6 GB/s @4K25
of allocate-then-free GPU churn and a 4K throughput limiter.

Now a `SurfacePool` (Mutex on GpuCompositor, interior mutability under the
existing `&self` signature) holds every one of those surfaces, allocated once at
first-frame sizing and REUSED every tick:
- canvas-sized surfaces (canvas_lin, overlaid, y_out, uv_out, both readback
  buffers) are exact-keyed on (canvas_w, canvas_h) — a resize is rare, not
  per-tick;
- tile arrays are grow-only on the max tile extent, always MAX_TILES layers;
- uniform/tile-params/overlay-prim buffers are allocated once at their bounded
  max (COPY_DST) and refilled in place with queue.write_buffer; the 1x1 atlas
  placeholder is allocated once.

Per-tick GPU allocations: BEFORE ~9 (no-overlay) / ~13 (overlay) create_* calls
per composite → AFTER 0 in steady state (one-time pool fill; a genuine resize
reallocates only the affected surfaces, then reuse resumes).

Output is byte/SSIM-identical: the shaders read only the freshly-written region
of each (possibly oversized) surface — tile arrays via textureLoad clamped to
src_w/src_h, storage buffers via the `count`-bounded loop — so an oversized
pooled surface samples exactly the same texels as a tight one. Inv #5 (NV12
throughout; the linear canvas is full-canvas, not per-tile RGBA) and #1/#10 (the
pool lock is synchronous, never held across an .await, never back-pressures the
engine) hold.

Tests: GPU-free `gpu::pool` unit tests prove the allocate-once-then-reuse /
reallocate-on-resize-then-reuse / grow-only-reuse counter logic end-to-end via
`ensure_cached` (no adapter needed); the GPU-gated `gpu_surface_pool` integration
test drives N ticks and asserts the allocation count is bounded (not ∝ N) +
resize-then-reuse, skipping gracefully where no adapter exists. All existing
compositor tests (incl. the gpu_compositor SSIM/byte-identity + RT-6
scale-at-composite suites) stay green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 8, 2026
…gmented (ADR-0037)

From a verification-hardened fan-out (21 agents; 10/14 claims held, 4 refined).
Industry ISO/Program terminology per operator: ISO = isolated per-SOURCE faithful
recording of EVERY elementary stream (video/audio/subtitle/data/SCTE-35/timecode);
Program/PGM = the composited output. Key as-built findings: ISO taps
Demuxer::read_packet() (all streams) BEFORE the video-only best_stream selection
that today DISCARDS non-video (libav.rs:172); the tee is a ref-counted packet.clone
(av_packet_ref, no payload copy) into an off-hot-path bounded DROP-OLDEST queue —
the exact GuardedPacketSource/fanout never-block posture (inv #1/#10). v1 = faithful
copy-remux of all streams via the safe Muxer (stream-copy, no re-encode); byte-exact
TS/SRT dump deferred (needs a raw-AVIO tee). Program = a rotating file sink in the
encode-once fan-out (RT-12 sink-mover, inv #7). Segmentation + time(30m/1h/7d)/size
retention REUSE the ADR-0032 HLS rolling-window + atomic-publish (<seg>.tmp→rename).
Disk-pressure (statvfs) disarms at a threshold + warns; write-failure → ADR-0035
HealthWarning + capped-backoff retry + auto-resume; never panics, never unbounded-
buffers, never stalls. CRITICAL: ISO PRESERVES original source timestamps/timebase
(archive as-is) — it does NOT re-stamp from the output tick (unlike the live path);
RestampAccumulator is used only to keep the muxer from aborting at a segment seam.
Folded refinements: a StreamCodecParameters::from_parameters constructor + a
dedicated IsoMuxSink keyed by stream_index (not the program-side Video|Audio
StreamKind). REC-0..N backlog; REC-0 = the bulletproof bounded-drop-oldest +
backoff write path (everything builds on the never-take-out-the-engine guarantee).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 8, 2026
…ifier + /routing endpoints (ADR-0034)

Make the already-built in-program routing crosspoints reachable + applied
live (the RT-6 video rebind_cell, RT-8a/RT-9 audio repoint/repoint_crossfade,
RT-10a subtitle repoint primitives), end-to-end:

* Commands (multiview-control): additive `Command::RouteVideo/RouteAudio/
  RouteSubtitle` (non_exhaustive); `SwapSource` becomes the desugared alias of
  `RouteVideo{..., Video, Best}` (back-compat, same engine intent).
  `Command::route_intent()` is the control->engine desugar bridge (control
  depends on engine, not the reverse).

* Engine apply (multiview-engine::route): `RouteApplier` drains a batch of
  engine-native `RouteIntent`s at the frame boundary and re-points the LIVE
  crosspoint via the existing O(1) primitives — rebind_cell / repoint_crossfade
  (cross-fade by default, pop-free) / SubtitleLayer::repoint. Resolves the
  StreamSelector (Index/Language/Best/StreamId) against the input's
  StreamInventory; coalesces a batch to <=1 re-point per destination/tick;
  never blocks the output clock (inv #1/#10).

* #11 classifier + /routing/plan (multiview-control::routing): inspects the
  destination's pinned params and returns {class1 | reset_lite | class2} + a
  coerced-degradation flag. Honest at the edges: in-program re-point onto an
  existing destination is Class-1; a cold-target video spin-up is Reset-lite;
  an audio breakaway onto a discrete track whose pinned layout differs is
  Class-2 (or operator-confirmed coerced Class-1).

* /routing/{video|audio|subtitle}/take + /routing/plan endpoints: resolve the
  class, submit the Route command via submit_accepted (Idempotency-Key, RFC
  9457, BOLA authorize_object, shed-503); 200 {class, applied} for a hot
  Class-1/Reset-lite re-point vs 202 {operation_id} for a Class-2 migration.
  OpenAPI regenerated (additive routes + StreamRefDoc/RouteTargetDoc mirrors).

No cli change required (the bus forwards Command::* generically via the drain's
non_exhaustive wildcard arm). No web change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly pushed a commit that referenced this pull request Jun 8, 2026
…gh one MultiviewProgram, no behaviour change (ADR-0030)

Introduce the `Program` abstraction (ADR-0030 / MP-0) and route the existing
single-program run path through ONE `MultiviewProgram`, a behaviour-preserving
MOVE of `Pipeline::drive_streaming`'s per-program clock/drive/run-loop core —
proven by every existing invariant/pipeline test passing UNCHANGED.

- multiview-config: new `program` module — `ProgramId` (validated newtype,
  reserved `"main"`), internally-tagged `ProgramKind` (#[non_exhaustive], only
  `Multiview` populated — passthrough/transcode land in MP-3/MP-4, no stub
  variant), and `ProgramSpec` (+ `main_multiview` desugaring). No `programs:`
  schema root yet (MP-5).
- multiview-engine: new `MultiviewProgram` owns its `ProgramId` + per-program
  `EngineRuntime` (clock + compositor drive + time source + pacer) + its own
  `StopSignal`, and drives the protected per-tick loop via thin delegations to
  `run_with_control`/`run_for_with_control`. Built from a `ProgramSpec` (cadence
  contract enforced; wrong kind → typed `Error::WrongProgramKind`, never a panic).
- multiview-cli: `Pipeline::drive_streaming` now builds ONE `MultiviewProgram`
  from a `ProgramSpec` desugared from the legacy config block (stored on
  `Pipeline`) and drives the loop through it instead of an inline `EngineRuntime`.
  The audio-bus tick, subtitle/overlay bake, and the per-frame command-drain
  `control` hook are preserved verbatim. `PipelineError::Program { program, .. }`
  carries `ProgramId` context.

Acceptance (unchanged through the move): #1 output-clock, #7 encode-once, #10
isolation. Protected engine soak/chaos (runtime.rs/isolation.rs) + the cli
streaming/pipeline tests pass byte-identical, none modified.

Gates: cargo fmt --check clean; cargo clippy --workspace --all-targets -D warnings
+ cargo clippy -p multiview-cli --features ffmpeg -D warnings clean;
cargo test -p multiview-engine -p multiview-config (335) + -p multiview-cli
--features ffmpeg (112) all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly added a commit that referenced this pull request Jun 8, 2026
Fixed-cadence output clock + EngineRuntime drive loop (out_pts=f(tick)),
arc-swap/broadcast isolation (inv #10), supervisor, admission/degradation loop;
X.733 alarm state machine + content probes; tally arbiter/salvo/scheduler/multi-head;
PTS servo (gated ptp); HA failover model. Inv #1/#10 adversarially verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly added a commit that referenced this pull request Jun 8, 2026
…ass, meters, subtitles

Render overlays to pixels efficiently per ADR-0016 (off-by-default `overlay` feature):
- Text engine: cosmic-text + swash shaping into an etagere shelf-packed, byte-capped, LRU
  glyph atlas (per-glyph cache → unchanged strings upload nothing); bundled OFL fonts
  (JetBrains Mono + Noto Sans) via rust-embed; one rasterizer feeds both the GPU quads and
  the CPU-reference coverage-bitmap blit.
- Overlay compositing sub-pass: premultiplied source-over into the Rgba16Float LINEAR canvas
  between composite and NV12 encode (inv #5/#8); GPU compute (behind +wgpu, naga-validated)
  + CPU reference; in-shader primitives (meter bars, safe-area/center-cross, tally borders,
  rounded alert cards), batched.
- Meters/scopes draw-data (dBFS→bar deflection, conflated ~30 Hz); subtitle SRT/VTT burn-in
  via the text engine + ASS via the off-by-default `libass` feature; overlays wired into the
  engine drive loop + cli run path, off the hot path (inv #1/#10).
Efficiency proven by criterion benches (T2 zero re-raster on unchanged frame; T3 zero
per-frame heap alloc; T4 bounded atlas; T5 batched per-label cost). OFL fonts attributed in
NOTICE; cargo-deny clean incl. the overlay feature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly added a commit that referenced this pull request Jun 8, 2026
…er + subtitle wiring

Rework the run pipeline so each source decodes on its own thread, scales to its tile,
and publishes NV12 into the per-tile framestore as frames arrive — replacing the
buffer-all-to-EOF path that hung on live streams (so live HLS/RTSP now ingest under a
bounded --duration) AND fixing the full-frame PiP green chroma cast (verified by eye:
the full-canvas tile now renders natural color). Feed the dB meter real per-tick
program-audio loudness (R128) instead of a constant, and wire subtitle cues into the
overlay burn-in. New tests: streaming_ingest, subtitle_meter_pipeline; compositor
pipeline regression. Inputs sampled into last-good stores, never pacing the output
clock (inv #1/#10).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
troykelly added a commit that referenced this pull request Jun 8, 2026
…rm decoders (ADR-0019)

Pure CaptionCue model (text + premultiplied-RGBA bitmap cues, regions, ASS markup
stripper) always compiled + native-dep-free; CaptionDecoder behind the ffmpeg feature
wraps the linked libav subtitle decoders — dvb_teletext (libzvbi, page-selectable),
dvb_subtitle (dvbsub bitmaps), CEA-608/708 (cc_dec), WebVTT, SubRip, mov_text, ASS —
emitting cues rebased to the ns timeline. 'No cue right now' is normal (empty Vec,
never an error/stall — inv #1/#10). Fixed: the ass_text_field comma-count (libav emits
the 8-field event form, so the Text field is after the 8th comma — keeps commas inside
\pos(x,y)); unsafe // SAFETY allows; a double-mutable-borrow; clippy. 64 tests pass.
docs/io/captions.md + ADR-0019 (Proposed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant