Skip to content

feat(live-update): interactive deploy console teaches upload → channel → rollout - #1015

Closed
riderx wants to merge 1 commit into
mainfrom
cursor/live-update-deploy-console-a250
Closed

feat(live-update): interactive deploy console teaches upload → channel → rollout#1015
riderx wants to merge 1 commit into
mainfrom
cursor/live-update-deploy-console-a250

Conversation

@riderx

@riderx riderx commented Sep 8, 2026

Copy link
Copy Markdown
Member

Summary

Replaces the static /landing-demos/live-update-console-demo.webp image in the Live Update guidance panel with an interactive, console-style teaching demo.

Three teaching beats

  1. Upload — review bundle 1.0.0, size, and CRC32 checksum, then publish.
  2. Channel — pick production or staging and assign the bundle.
  3. Rollout — staged progress (10% → health OK → 100%) with live status text.

Interaction & accessibility

  • Step tabs + primary action button let visitors drive the flow.
  • Optional one-time auto-advance (interruptible on any click).
  • aria-live="polite" status updates, keyboard-reachable step tabs, no replay button.
  • prefers-reduced-motion: skips rollout animation and lands on the complete state.
  • Copy stays inside the card (text-overflow, compact layout).

Demo

Before

Static console screenshot — no interaction.

After — step 1: Upload

Live Update deploy console — upload step

After — step 2: Channel

Live Update deploy console — channel step

After — step 3: Rollout complete

Live Update deploy console — rollout complete

Recording

live-update-console-demo.webm

Files

  • apps/web/src/components/live-update/LiveUpdateDeployConsole.astro — markup + copy wiring
  • apps/web/src/components/live-update/live-update-deploy-console.client.ts — step state, rollout sequence, auto-advance
  • apps/web/src/pages/live-update.astro — swaps static image for component
  • apps/web/src/styles/lu-product.css — console demo styles
  • apps/shared/copy/messages.ts — i18n strings for console UI

Out of scope

  • Observe and other product pages (separate PRs)
  • Page copy redesign
Open in Web Open in Cursor 

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Review in cubic

Replace the static console screenshot with a steppable upload → channel →
rollout demo on the Live Update guidance panel. Includes aria-live status,
keyboard-reachable steps, reduced-motion support, and interruptible auto-advance.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

On-demand reviews are free for the next 12 days. After that, they cost $0.25 per reviewed file.

Or wait 1 minute for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used all 3 included reviews currently available. Your 44 included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Essentials

Run ID: 3b651ff6-7d41-4fe4-8859-3e8fa187664b

📥 Commits

Reviewing files that changed from the base of the PR and between 51de169 and 0a6e971.

📒 Files selected for processing (6)
  • apps/shared/copy/messageContexts.ts
  • apps/shared/copy/messages.ts
  • apps/web/src/components/live-update/LiveUpdateDeployConsole.astro
  • apps/web/src/components/live-update/live-update-deploy-console.client.ts
  • apps/web/src/pages/live-update.astro
  • apps/web/src/styles/lu-product.css

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

sonarqubecloud Bot commented Sep 8, 2026

Copy link
Copy Markdown

@riderx
riderx marked this pull request as ready for review September 8, 2026 17:21
@TorichanCapgo

Copy link
Copy Markdown
Contributor

@coderabbitai full review

Please review current HEAD 0a6e971852b0e5d890a1ffeaa4ce23cf3bfb482e. Jose gate needs CodeRabbit APPROVED on this exact SHA.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

requested_sha="0a6e971852b0e5d890a1ffeaa4ce23cf3bfb482e"
actual_sha="$(git rev-parse HEAD)"
printf 'HEAD=%s\nREQUESTED=%s\n' "$actual_sha" "$requested_sha"
if [ "$actual_sha" != "$requested_sha" ]; then
  echo "ERROR: checked-out HEAD does not match requested commit" >&2
fi

echo '--- changed files ---'
git diff --name-status "${requested_sha}^" "${requested_sha}"

echo '--- diff ---'
git diff --find-renames --find-copies --unified=80 "${requested_sha}^" "${requested_sha}"

echo '--- package scripts ---'
if [ -f package.json ]; then
  jq '{scripts: .scripts, packageManager: .packageManager}' package.json
fi

echo '--- changed-file outlines ---'
for f in \
  apps/web/src/components/live-update/LiveUpdateDeployConsole.astro \
  apps/web/src/components/live-update/live-update-deploy-console.client.ts \
  apps/web/src/pages/live-update.astro \
  apps/web/src/styles/lu-product.css \
  apps/shared/copy/messages.ts
do
  if [ -f "$f" ]; then
    echo "### $f"
    case "$f" in
      *.ts) ast-grep outline "$f" --items all || true ;;
      *) nl -ba "$f" | sed -n '1,80p' ;;
    esac
  fi
done

Length of output: 1808


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- available history ---'
git log --oneline -5 || true
git show --format=fuller --stat --summary HEAD

echo '--- PR files and patches from GitHub ---'
gh api 'repos/Cap-go/website/pulls/1015/files?per_page=100' \
  --jq '.[] | "FILE: \(.filename)\nSTATUS: \(.status)\nPATCH:\n\(.patch // "[no textual patch]")\n---"'

echo '--- component markup ---'
nl -ba apps/web/src/components/live-update/LiveUpdateDeployConsole.astro

echo '--- client module ---'
nl -ba apps/web/src/components/live-update/live-update-deploy-console.client.ts

echo '--- page integration occurrences ---'
rg -n -C 12 'LiveUpdateDeployConsole|live-update-console-demo' apps/web/src/pages/live-update.astro

echo '--- console CSS selectors ---'
rg -n -C 8 'lu-deploy|deploy-console|console-' apps/web/src/styles/lu-product.css

echo '--- console messages ---'
rg -n -C 5 'liveUpdate.*(Deploy|Console)|deploy.*(Console|Upload|Channel|Rollout)|publishBundle|watchRollout' apps/shared/copy/messages.ts || true

echo '--- references to console IDs/classes/data attributes ---'
rg -n -C 2 'lu-deploy-console|lu-deploy-|data-lu' apps/web/src apps/shared/copy

Length of output: 163794


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- exact target ---'
git rev-parse HEAD

echo '--- PR changed paths ---'
gh api 'repos/Cap-go/website/pulls/1015/files?per_page=100' --jq '.[] | [.filename, .status, .additions, .deletions] | `@tsv`'

echo '--- deploy console component ---'
sed -n '1,240p' apps/web/src/components/live-update/LiveUpdateDeployConsole.astro

echo '--- deploy console client ---'
sed -n '1,340p' apps/web/src/components/live-update/live-update-deploy-console.client.ts

echo '--- live update integration ---'
sed -n '1,35p' apps/web/src/pages/live-update.astro
sed -n '385,430p' apps/web/src/pages/live-update.astro

echo '--- changed CSS block ---'
sed -n '700,1100p' apps/web/src/styles/lu-product.css

echo '--- package scripts and workspace configuration ---'
jq '{scripts, packageManager, workspaces}' package.json
find apps -maxdepth 2 -name package.json -print -exec sh -c 'echo "--- $1"; jq "{scripts, packageManager}" "$1"' _ {} \;

echo '--- message module API and locale validation references ---'
rg -n -C 3 'export.*(MessageValues|default)|function.*messages|missing translation|messageContexts|messages\.ts' apps/shared apps/web scripts -g '*.ts' -g '*.js' -g '*.mjs' | head -500 || true

echo '--- static review: untracked timeouts and console behavior references ---'
rg -n -C 3 'setTimeout|clearTimeout|runRolloutSequence|scheduleAutoAdvance|setupLiveUpdateDeployConsole' apps/web/src/components/live-update apps/web/test -g '*.ts' -g '*.astro' -g '*.js' || true

Length of output: 55426


@TorichanCapgo I reviewed commit 0a6e971852b0e5d890a1ffeaa4ce23cf3bfb482e.

I found one blocking issue. I cannot approve this SHA.

  • apps/web/src/components/live-update/live-update-deploy-console.client.ts: Lines 153 and 158 create rollout timers without storing their handles. clearTimers() only cancels autoTimer and the final 2800 ms timer. If a visitor returns to Upload during rollout, the remaining callbacks still set rollout progress and replace the visible aria-live status. When the visitor returns to Rollout, the demo can start at stale 55% progress instead of the required 10% state.

Store and cancel every rollout timer, or use one cancellable sequence token, before resetting or restarting the flow.

I did not run build or browser checks. The sandbox does not permit execution of repository code.

You are interacting with an AI system.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 issues found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/web/src/styles/lu-product.css">

<violation number="1" location="apps/web/src/styles/lu-product.css:907">
P3: The field labels (dt) are 10px text in #64748b on the #f4f7fb console background, which computes to ~4.43:1 contrast — just under the 4.5:1 AA requirement for normal-size text. Darken to #475569 (slate-600), which yields ~7:1, so the small labels stay readable.</violation>
</file>

<file name="apps/web/src/components/live-update/LiveUpdateDeployConsole.astro">

<violation number="1" location="apps/web/src/components/live-update/LiveUpdateDeployConsole.astro:37">
P3: `rolloutLabel` is dead copy: it is computed and shipped but never rendered or consumed. Remove the unused value or render it as the staged-rollout label.</violation>

<violation number="2" location="apps/web/src/components/live-update/LiveUpdateDeployConsole.astro:61">
P2: Keyboard users cannot reach the inactive step tabs because the roving `tabindex` has no arrow-key navigation. Implement the standard tablist arrow-key/focus behavior, or make the inactive tabs keyboard reachable.</violation>

<violation number="3" location="apps/web/src/components/live-update/LiveUpdateDeployConsole.astro:121">
P2: When a visitor selects staging, the status region and rollout still announce production. Make the status copy channel-aware before exposing staging, or remove the staging control until that flow is supported.</violation>
</file>

<file name="apps/web/src/components/live-update/live-update-deploy-console.client.ts">

<violation number="1" location="apps/web/src/components/live-update/live-update-deploy-console.client.ts:156">
P2: When a user navigates back during the rollout animation, the intermediate callbacks keep mutating the reset rollout and can leave the next visit stuck at 10% or 55%. Store both intermediate timeout handles and cancel the complete rollout sequence when navigating or restarting.</violation>

<violation number="2" location="apps/web/src/components/live-update/live-update-deploy-console.client.ts:174">
P2: Returning from a partially watched Rollout leaves the flow stuck because the nonzero progress prevents the sequence from restarting. Restart or resume the rollout whenever an incomplete Rollout step is entered.</violation>

<violation number="3" location="apps/web/src/components/live-update/live-update-deploy-console.client.ts:183">
P2: The upload and channel completion statuses are overwritten immediately when `goToStep()` refreshes the next panel, so the `aria-live` region does not reliably announce completion. Preserve the completion status when advancing, then switch to the next idle status on a later interaction.</violation>

<violation number="4" location="apps/web/src/components/live-update/live-update-deploy-console.client.ts:200">
P1: When auto-advance reaches Rollout, `scheduleAutoAdvance()` clears the completion timer immediately after `runRolloutSequence()` creates it, so the demo stops at 55% and never completes. Separate auto-timer cleanup from rollout cleanup and retain all rollout timer handles.</violation>
</file>

<file name="apps/shared/copy/messages.ts">

<violation number="1" location="apps/shared/copy/messages.ts:2151">
P2: The new console copy is missing from the generated `translationContextByText.ts` artifact, so the edge translation worker cannot resolve context for these strings. Run `bun run generate:message-contexts` and commit the regenerated artifact with the message changes.

(Based on your team's feedback about regenerating translation contexts).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

function scheduleAutoAdvance() {
if (userInterrupted || reducedMotion || rolloutComplete) return
clearTimers()
if (step === 2) return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: When auto-advance reaches Rollout, scheduleAutoAdvance() clears the completion timer immediately after runRolloutSequence() creates it, so the demo stops at 55% and never completes. Separate auto-timer cleanup from rollout cleanup and retain all rollout timer handles.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/components/live-update/live-update-deploy-console.client.ts, line 200:

<comment>When auto-advance reaches Rollout, `scheduleAutoAdvance()` clears the completion timer immediately after `runRolloutSequence()` creates it, so the demo stops at 55% and never completes. Separate auto-timer cleanup from rollout cleanup and retain all rollout timer handles.</comment>

<file context>
@@ -0,0 +1,258 @@
+  function scheduleAutoAdvance() {
+    if (userInterrupted || reducedMotion || rolloutComplete) return
+    clearTimers()
+    if (step === 2) return
+
+    autoTimer = setTimeout(() => {
</file context>

<button type="button" class="lu-console-channel is-selected" data-channel="production" aria-pressed="true">
{consoleCopy.channelProduction}
</button>
<button type="button" class="lu-console-channel" data-channel="staging" aria-pressed="false">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When a visitor selects staging, the status region and rollout still announce production. Make the status copy channel-aware before exposing staging, or remove the staging control until that flow is supported.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/components/live-update/LiveUpdateDeployConsole.astro, line 121:

<comment>When a visitor selects staging, the status region and rollout still announce production. Make the status copy channel-aware before exposing staging, or remove the staging control until that flow is supported.</comment>

<file context>
@@ -0,0 +1,183 @@
+                <button type="button" class="lu-console-channel is-selected" data-channel="production" aria-pressed="true">
+                  {consoleCopy.channelProduction}
+                </button>
+                <button type="button" class="lu-console-channel" data-channel="staging" aria-pressed="false">
+                  {consoleCopy.channelStaging}
+                </button>
</file context>

aria-selected={index === 0 ? 'true' : 'false'}
aria-controls={`lu-console-panel-${index}`}
id={`lu-console-tab-${index}`}
tabindex={index === 0 ? 0 : -1}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Keyboard users cannot reach the inactive step tabs because the roving tabindex has no arrow-key navigation. Implement the standard tablist arrow-key/focus behavior, or make the inactive tabs keyboard reachable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/components/live-update/LiveUpdateDeployConsole.astro, line 61:

<comment>Keyboard users cannot reach the inactive step tabs because the roving `tabindex` has no arrow-key navigation. Implement the standard tablist arrow-key/focus behavior, or make the inactive tabs keyboard reachable.</comment>

<file context>
@@ -0,0 +1,183 @@
+              aria-selected={index === 0 ? 'true' : 'false'}
+              aria-controls={`lu-console-panel-${index}`}
+              id={`lu-console-tab-${index}`}
+              tabindex={index === 0 ? 0 : -1}
+            >
+              <span class="lu-console-step-num" aria-hidden="true">
</file context>

setTimeout(() => {
setRollout(10, true)
setStatus(copy.status.rolloutHealth)
}, 900)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When a user navigates back during the rollout animation, the intermediate callbacks keep mutating the reset rollout and can leave the next visit stuck at 10% or 55%. Store both intermediate timeout handles and cancel the complete rollout sequence when navigating or restarting.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/components/live-update/live-update-deploy-console.client.ts, line 156:

<comment>When a user navigates back during the rollout animation, the intermediate callbacks keep mutating the reset rollout and can leave the next visit stuck at 10% or 55%. Store both intermediate timeout handles and cancel the complete rollout sequence when navigating or restarting.</comment>

<file context>
@@ -0,0 +1,258 @@
+    setTimeout(() => {
+      setRollout(10, true)
+      setStatus(copy.status.rolloutHealth)
+    }, 900)
+
+    setTimeout(() => {
</file context>

live_update_guidance_panel_footer: 'Choose the safe path before rollout: ship OTA, gate by native version, trigger a native build, or roll back.',
live_update_guidance_panel_title: 'Release decision',
live_update_guidance_title: 'Catch breaking updates before they ship',
live_update_console_action_channel: 'Assign to channel',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new console copy is missing from the generated translationContextByText.ts artifact, so the edge translation worker cannot resolve context for these strings. Run bun run generate:message-contexts and commit the regenerated artifact with the message changes.

(Based on your team's feedback about regenerating translation contexts).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/shared/copy/messages.ts, line 2151:

<comment>The new console copy is missing from the generated `translationContextByText.ts` artifact, so the edge translation worker cannot resolve context for these strings. Run `bun run generate:message-contexts` and commit the regenerated artifact with the message changes.

(Based on your team's feedback about regenerating translation contexts). </comment>

<file context>
@@ -2148,6 +2148,36 @@ const messages = {
   live_update_guidance_panel_footer: 'Choose the safe path before rollout: ship OTA, gate by native version, trigger a native build, or roll back.',
   live_update_guidance_panel_title: 'Release decision',
   live_update_guidance_title: 'Catch breaking updates before they ship',
+  live_update_console_action_channel: 'Assign to channel',
+  live_update_console_action_rollout: 'Watch rollout',
+  live_update_console_action_upload: 'Publish bundle',
</file context>

function advance() {
interruptAuto()
if (step === 0) {
setStatus(copy.status.uploadDone)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The upload and channel completion statuses are overwritten immediately when goToStep() refreshes the next panel, so the aria-live region does not reliably announce completion. Preserve the completion status when advancing, then switch to the next idle status on a later interaction.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/components/live-update/live-update-deploy-console.client.ts, line 183:

<comment>The upload and channel completion statuses are overwritten immediately when `goToStep()` refreshes the next panel, so the `aria-live` region does not reliably announce completion. Preserve the completion status when advancing, then switch to the next idle status on a later interaction.</comment>

<file context>
@@ -0,0 +1,258 @@
+  function advance() {
+    interruptAuto()
+    if (step === 0) {
+      setStatus(copy.status.uploadDone)
+      goToStep(1)
+      return
</file context>

function goToStep(next: Step) {
step = next
updateStepUi()
if (step === 2 && !rolloutComplete && rolloutProgress === 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Returning from a partially watched Rollout leaves the flow stuck because the nonzero progress prevents the sequence from restarting. Restart or resume the rollout whenever an incomplete Rollout step is entered.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/components/live-update/live-update-deploy-console.client.ts, line 174:

<comment>Returning from a partially watched Rollout leaves the flow stuck because the nonzero progress prevents the sequence from restarting. Restart or resume the rollout whenever an incomplete Rollout step is entered.</comment>

<file context>
@@ -0,0 +1,258 @@
+  function goToStep(next: Step) {
+    step = next
+    updateStepUi()
+    if (step === 2 && !rolloutComplete && rolloutProgress === 0) {
+      runRolloutSequence()
+    }
</file context>

.lu .lu-console-field dt {
font-size: 10px;
font-weight: 700;
color: #64748b;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The field labels (dt) are 10px text in #64748b on the #f4f7fb console background, which computes to ~4.43:1 contrast — just under the 4.5:1 AA requirement for normal-size text. Darken to #475569 (slate-600), which yields ~7:1, so the small labels stay readable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/styles/lu-product.css, line 907:

<comment>The field labels (dt) are 10px text in #64748b on the #f4f7fb console background, which computes to ~4.43:1 contrast — just under the 4.5:1 AA requirement for normal-size text. Darken to #475569 (slate-600), which yields ~7:1, so the small labels stay readable.</comment>

<file context>
@@ -729,6 +729,338 @@ body:has(.lu) footer > div > div:first-child {
+.lu .lu-console-field dt {
+  font-size: 10px;
+  font-weight: 700;
+  color: #64748b;
+  text-transform: uppercase;
+  letter-spacing: 0.04em;
</file context>
Suggested change
color: #64748b;
color: #475569;

fieldChecksum: copy('live_update_console_field_checksum'),
fieldChannel: copy('live_update_console_field_channel'),
fieldRollout: copy('live_update_console_field_rollout'),
rolloutLabel: copy('live_update_console_rollout_label'),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: rolloutLabel is dead copy: it is computed and shipped but never rendered or consumed. Remove the unused value or render it as the staged-rollout label.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/components/live-update/LiveUpdateDeployConsole.astro, line 37:

<comment>`rolloutLabel` is dead copy: it is computed and shipped but never rendered or consumed. Remove the unused value or render it as the staged-rollout label.</comment>

<file context>
@@ -0,0 +1,183 @@
+  fieldChecksum: copy('live_update_console_field_checksum'),
+  fieldChannel: copy('live_update_console_field_channel'),
+  fieldRollout: copy('live_update_console_field_rollout'),
+  rolloutLabel: copy('live_update_console_rollout_label'),
+  healthOk: copy('live_update_console_health_ok'),
+  orgName: copy('live_update_console_org_name'),
</file context>

@TorichanCapgo

Copy link
Copy Markdown
Contributor

Superseding this PR.

The Live Update teaching console here looks like a sparse toy mockup (fake chrome, thin UI, confusing CLI noise) — Martin rejected it. Rebuilding one end-to-end PR for every product page at real-console craft quality. Do not merge this.

@TorichanCapgo

Copy link
Copy Markdown
Contributor

Closed as superseded — toy console rejected; consolidating into one end-to-end product-page teaching PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants