From f1ecc041215bc47157e1e4301a31f1ac44ef3b75 Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Thu, 15 Oct 2026 00:08:36 +0000 Subject: [PATCH 1/4] Correct the media stage bound to connected --- browsers/live-view.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx index d520bd98..e4946b02 100644 --- a/browsers/live-view.mdx +++ b/browsers/live-view.mdx @@ -109,7 +109,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin | --- | --- | | `transport` | the socket never opened inside its bound, or closed before signaling | | `signaling` | the socket opened but no offer arrived inside its bound | -| `media` | a peer exists but ICE never reached `checking` inside its bound | +| `media` | a peer exists but ICE never reached `connected` inside its bound | | `peer` | peer construction or the remote offer threw | | `unsupported` | the browser has no `RTCPeerConnection` | | `server` | the server closed the connect | From 58849c976ad0295df1e68111679cf338440c6d02 Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:32:47 +0000 Subject: [PATCH 2/4] Move the example watchdog above the client's connect budget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snippet remounted 15s after mount. The client's connect bounds run in sequence and sum to 23s, so an embedder following the example tore the frame down before the client could report which stage stalled — including media, the stage most likely to have stalled. Also correct the note about KERNEL_CONNECTION_TIMEOUT never firing once negotiation begins. The media bound stays armed past `checking` until ICE reaches `connected`, so a connect that stalls in between does report one; what never emits it is a viewer whose frames stop after it connected. --- browsers/live-view.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx index e4946b02..4ee29dc6 100644 --- a/browsers/live-view.mdx +++ b/browsers/live-view.mdx @@ -130,7 +130,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin ### Detecting a viewer that never starts -Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived: +Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 23s. Reload the frame sooner than that and you will tear it down before it can report which stage stalled: ```typescript Typescript/Javascript const iframe = document.querySelector('#kernel-live-view'); @@ -147,7 +147,7 @@ function arm() { iframe.src = 'about:blank'; iframe.src = src; arm(); - }, 15000); + }, 25000); } window.addEventListener('message', (event) => { @@ -161,7 +161,7 @@ window.addEventListener('message', (event) => { arm(); ``` -Do not gate on `KERNEL_CONNECTION_TIMEOUT` alone. The watchdog behind it is cleared once negotiation begins, so a connection that stalls after that point never emits it. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. +Do not gate on `KERNEL_CONNECTION_TIMEOUT` alone. It only covers a connect that is still negotiating: the media bound clears once ICE reaches `connected`, and a viewer whose frames stop after that never emits one. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. `KERNEL_CONNECTION_FAILED` and `KERNEL_CONNECTION_TIMEOUT` are both terminal, and a failed connect posts one of them, never both. Whichever arrives, nothing further happens until the iframe reloads. Surface a persistent message rather than remounting blindly: a `peer` or `unsupported` reason is a property of the browser environment, so a remount repeats it, while a `transport` reason is worth one retry. From 77bfce2d7f8b286d1d08e5f7d4131b3f488b3466 Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Mon, 19 Oct 2026 21:42:24 +0000 Subject: [PATCH 3/4] Set the example watchdog above the connect budget and deslop the guidance The example remounts at 35s, above the client's connect bounds, which run in sequence (transport, signaling, media) and now sum to 28s after kernel-images#472 settled the media bound at 10s. Below the budget the example reproduces the bug this page was fixing: the embedder tears the frame down before the client can name the stage that stalled. The prose around it says what the timer has to clear and why, since the timer also covers loading the frame and waking a sleeping browser. The timeout warning names `completed` alongside `connected`, which is the other way the media bound clears. The `media` row now reads "connected or completed ... covers a peer that stalls after checking", folded in from kernel/docs#698, which this supersedes. --- browsers/live-view.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx index 4ee29dc6..eea7935c 100644 --- a/browsers/live-view.mdx +++ b/browsers/live-view.mdx @@ -109,7 +109,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin | --- | --- | | `transport` | the socket never opened inside its bound, or closed before signaling | | `signaling` | the socket opened but no offer arrived inside its bound | -| `media` | a peer exists but ICE never reached `connected` inside its bound | +| `media` | a peer exists but ICE never reached `connected` or `completed` inside its bound — this covers a peer that stalls after `checking` | | `peer` | peer construction or the remote offer threw | | `unsupported` | the browser has no `RTCPeerConnection` | | `server` | the server closed the connect | @@ -130,7 +130,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin ### Detecting a viewer that never starts -Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 23s. Reload the frame sooner than that and you will tear it down before it can report which stage stalled: +Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 28s by default. It also covers loading the frame, including waking a browser that has gone to sleep, so leave room above that budget when you set it. If you reload sooner, you'll tear the frame down before it can report which stage stalled: ```typescript Typescript/Javascript const iframe = document.querySelector('#kernel-live-view'); @@ -147,7 +147,7 @@ function arm() { iframe.src = 'about:blank'; iframe.src = src; arm(); - }, 25000); + }, 35000); } window.addEventListener('message', (event) => { @@ -161,7 +161,7 @@ window.addEventListener('message', (event) => { arm(); ``` -Do not gate on `KERNEL_CONNECTION_TIMEOUT` alone. It only covers a connect that is still negotiating: the media bound clears once ICE reaches `connected`, and a viewer whose frames stop after that never emits one. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. +Do not gate on `KERNEL_CONNECTION_TIMEOUT` alone. It only covers a connect that is still negotiating: the media bound clears once ICE reaches `connected` or `completed`, and a viewer whose frames stop after it connected never emits one. Log it alongside `KERNEL_PLAYING` to capture the connection state at the moment things stalled. `KERNEL_CONNECTION_FAILED` and `KERNEL_CONNECTION_TIMEOUT` are both terminal, and a failed connect posts one of them, never both. Whichever arrives, nothing further happens until the iframe reloads. Surface a persistent message rather than remounting blindly: a `peer` or `unsupported` reason is a property of the browser environment, so a remount repeats it, while a `transport` reason is worth one retry. From 044c381cd4bf8b37316507a7c9903b0d790683cc Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Tue, 20 Oct 2026 04:17:17 +0000 Subject: [PATCH 4/4] Keep the media bound at 30s and move the example watchdog above the budget The media bound stays at its interim ceiling. The stretch through ICE `connected` is unmeasured in every region, so shortening the default is not something the probe data supports yet; a region that has the timings lowers it through the live view proxy's override instead. With the defaults unchanged, the three bounds sum to 48s rather than 28s, so the example timer moves to 50s to stay above the budget it has to outlast. --- browsers/live-view.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx index eea7935c..e5286a9c 100644 --- a/browsers/live-view.mdx +++ b/browsers/live-view.mdx @@ -130,7 +130,7 @@ Both terminal events carry the same `reason`, so branch on it instead of matchin ### Detecting a viewer that never starts -Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 28s by default. It also covers loading the frame, including waking a browser that has gone to sleep, so leave room above that budget when you set it. If you reload sooner, you'll tear the frame down before it can report which stage stalled: +Gate on `KERNEL_PLAYING`. It fires only once frames actually arrive, so it is the signal that distinguishes a working viewer from one that is still connecting or has silently failed. The terminal events below tell you when the client has stopped trying. Start a timer when you mount the iframe and remount if `KERNEL_PLAYING` has not arrived. The timer has to outlast the client's connect bounds, which run in sequence — transport, then signaling, then media — and sum to 48s by default. If you wait less than that, you'll tear the frame down before it can report a stage that stalled late. The timer also covers loading the frame, including waking a browser that has gone to sleep, so leave room above the budget when you set it: ```typescript Typescript/Javascript const iframe = document.querySelector('#kernel-live-view'); @@ -147,7 +147,7 @@ function arm() { iframe.src = 'about:blank'; iframe.src = src; arm(); - }, 35000); + }, 50000); } window.addEventListener('message', (event) => {