@urun-sh/core 0.2.47 → 0.2.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ - **Wall-clock media liveness is gone.** Streams are not guaranteed to be
6
+ continuous, so "seconds since the last inbound byte / decoded frame" was never
7
+ a valid death signal. Two rules keyed on it, and both are removed:
8
+ - the inbound-stall rule (`media-stall` + its `media-idle` companion). It also
9
+ emitted a `console.warn` **on every sample interval** — "[urun] no inbound
10
+ media for 6s, but the ICE path is connected — the sender is idle, not dead;
11
+ holding the transport" — at a perfectly healthy path (live receipt:
12
+ inboundTotal 1985272, packetsReceived 1744, packetsLost 0). The bursty gop=1
13
+ image-as-video lane, where one frame per completed render means silence is
14
+ NORMAL, logged that line forever. Both the diagnostic and the recovery are
15
+ deleted; a quiet stream is now completely silent and completely untouched.
16
+ - the mid-session decode-gap rule. `decode-wall` no longer means "framesDecoded
17
+ stopped advancing"; it now means **the decoder never produced its FIRST frame**
18
+ within `stallTimeoutMs` of first byte arrival — the actual failure it was
19
+ written for (a mid-stream join with no keyframe: healthy RTP, `framesDecoded`
20
+ stuck at 0, black video). That check is a BOUNDED one-shot join probe anchored
21
+ to first byte arrival; once a frame decodes, the probe disarms permanently and
22
+ nothing polls the stream again.
23
+ - **Recovery keys only on the transport's own signals**, per the library-native
24
+ mechanism rule: `connectionstatechange` → `restartIce()` in place → and, once
25
+ the in-place restart budget is spent (or the restart watchdog expires with the
26
+ path still down), the same resolve-then-dial `_renegotiate()` that re-resolves a
27
+ rebound SFU backend. That escalation is NEW and replaces the flat-counter path,
28
+ so the SFU-GameServer-recycle protection is preserved without a clock: ICE
29
+ consent freshness (RFC 7675) re-probes the selected pair every ~5s regardless of
30
+ media, which is why the ICE agent's verdict — and not a receive counter — is
31
+ authoritative. A higher control-doc incarnation still renegotiates.
32
+ - **The join probe is PER-CONSUMER.** A session-wide aggregate `framesDecoded`
33
+ would let one already-decoding (or audio) lane settle the probe on behalf of a
34
+ NEW video lane that joined with no keyframe — masking exactly the black video
35
+ the probe exists to catch. Each consumer now carries its own first-byte anchor,
36
+ its own loss baseline and its own settled latch; the timer runs while any live
37
+ lane is unsettled, stops when all are, and a later `consume` re-arms only for
38
+ the lane it announces. `decode-wall` detail now names the failing lane
39
+ (`consumerId`) and reports THAT lane's counters instead of session-wide sums.
40
+ - `mediaLiveness` keeps its shape (`pollMs`, `stallTimeoutMs`); `stallTimeoutMs`
41
+ now bounds the first-frame join window. No public API change.
42
+ - The ast-grep conformance guard is retargeted and widened:
43
+ `no-teardown-on-idle-inbound-counter` → `no-wallclock-media-liveness`, which now
44
+ fails the build on ANY wall-clock media-gap branch that drives
45
+ `_renegotiate()`/`_attemptReconnect()` (inbound counter or decode counter),
46
+ while allowing the bounded first-byte-anchored join probe.
47
+
3
48
  ## 0.2.44
4
49
 
5
50
  - **A flat inbound RTP counter no longer tears down a healthy media transport.** The