Problem
The sidebar's LastCode local-nightly Download action is not a normal network download. The first click can take several minutes because it creates or refreshes a dedicated worktree, installs dependencies, runs the 11-stage full local CI gate, builds the macOS artifacts, serves the updater ZIP over loopback, and lets electron-updater verify and stage it.
Today the in-app experience collapses that work into a generic downloading/building state. During the local build, DesktopUpdateState.downloadPercent stays at its initial value because progress events only begin later, when electron-updater stages the finished ZIP. The user cannot tell which phase is running, whether the build is making progress, or roughly how much remains. A failure changes the tooltip to a retry instruction, but the button is not visually red and the durable diagnostic details are only available in a toast and ~/.lastcode/local-updates/build.log.
This became concrete when a local nightly build ran for several minutes and then failed with an error of the form:
[lastcode:local-update] Local LastCode build failed. See /Users/example/.lastcode/local-updates/build.log. <failing command> failed with exit code 1.
After returning later, the user saw the Download button again without an obvious record of what phase failed or a convenient way to hand the failure to an agent.
Desired experience
In-progress hover feedback
While a lastcode-local update is building or staging, hovering the sidebar update button shows:
- a concise 1–3 word phase label; and
- an estimated percent complete.
Use the same truthful, stage-weighted approach as the installed lastcode-build command. This is an estimate based on observed stage transitions and bounded elapsed-time interpolation, not fake precision or a prediction of wall-clock completion. The percent must remain monotonic, stay below 100% until staging actually completes, and clearly identify itself as estimated (for example, Workspace tests · 31% est.).
The UI should expose meaningful user-facing phases derived from real pipeline markers. Exact labels can be refined for clarity, but the implementation must cover the actual sequence:
Preparing — select the immutable checkpoint/revision and prepare the dedicated worktree.
Installing deps — install the pinned workspace dependencies.
- Full CI phases —
Electron setup, Format & lint, Typechecking, Workspace tests, Rust format, Desktop build, Preload checks, Rust tests, Mobile tools, Mobile lint, and Release smoke.
- Packaging phases —
Building artifacts, Branding, Staging app, Installing deps, Building DMG, and Finalizing.
Staging update — serve the completed output locally and let electron-updater download, verify, and stage the ZIP.
Do not maintain a second divergent percentage table in the renderer. Extract or otherwise share the existing phase markers and weights from scripts/lastcode-build.mjs so the CLI and desktop report the same model. The desktop may coalesce broadcasts to avoid excessive IPC/render churn, but it must publish phase changes promptly and enough percent changes to make progress legible.
Failure state and diagnostic copy
If the local build or local staging step fails:
- the sidebar update button turns red/destructive and remains visibly failed until the user retries or a later check establishes a different state;
- hovering the failed button opens an interactive error panel; and
- the panel contains a clear Copy details action.
Copy details writes a prompt-ready plain-text diagnostic summary suitable for pasting directly into a coding-agent conversation. At minimum include:
- a stable header such as
[lastcode:local-update] Local LastCode build failed;
- installed version, target version, and selected checkpoint/revision when available;
- last known phase and estimated percent;
- whether the failure occurred during local build/packaging or updater staging;
- the full current error message; and
- the absolute log path (
~/.lastcode/local-updates/build.log expanded for the running user).
Keep the copied payload bounded and safe. Do not dump the entire log, process environment, credentials, or tokens. If a log suffix is included, sanitize control sequences and cap its bytes/lines; otherwise the absolute log path plus exact failure is sufficient because a local agent can inspect it. Reuse the existing clipboard helper/error feedback patterns rather than silently failing when clipboard access is unavailable.
The retry action must remain available. The interactive hover panel must not close while the pointer moves from the button to Copy details.
Relevant implementation seams
scripts/lastcode-build.mjs
BUILD_PHASES, resolveBuildPhaseIndex, and estimateBuildProgress are the existing stage-weighted model.
BuildProgressDisplay tails only newly appended bytes from ~/.lastcode/local-updates/build.log, tracks the latest real marker, and displays % est..
scripts/lastcode-local-update.mjs
buildUnlocked owns the real sequence: worktree preparation, vp install --frozen-lockfile, full checkpoint CI (or a reusable CI stamp), macOS artifact build, validation, and final structured result.
- failures currently wrap the failing command with the log path.
scripts/lastcode-local-ci.ts
- defines and prints the 11 concrete full-CI stage labels used as log markers.
apps/desktop/src/updates/LastCodeLocalUpdates.ts
- launches the helper and currently buffers stdout/stderr until process exit; its
build contract returns only the final built result.
- this is the natural boundary for observing structured progress or tailing the build log while the helper runs. Prefer structured, typed progress over scraping arbitrary latest log lines into UI state.
apps/desktop/src/updates/DesktopUpdates.ts
downloadAvailableUpdate transitions to downloading, awaits the entire local build, starts the loopback feed, and then delegates ZIP staging to electron-updater.
handleDownloadProgress currently represents hosted/updater byte progress only and broadcasts at 10% boundaries.
apps/desktop/src/updates/updateMachine.ts
- owns download start/progress/failure state transitions. Local build phase/error diagnostic state should be modeled here rather than assembled ad hoc in the component.
packages/contracts/src/ipc.ts
DesktopUpdateState and DesktopUpdateStateSchema cross the Electron IPC boundary. Add the smallest typed fields needed to distinguish local build phase/progress and carry bounded copyable diagnostics. Do not overload hosted byte-download semantics with ambiguous values.
apps/web/src/components/sidebar/SidebarUpdatePill.tsx
- owns the button, hover card, action/retry handling, and release-note tooltip.
- its hover card already uses
pointer-events-auto when release notes need to stay interactive; the failure action needs equivalent interactive behavior.
apps/web/src/components/sidebar/DesktopUpdateStatusIcon.tsx
- renders the current circular progress treatment from
downloadPercent.
apps/web/src/components/desktopUpdate.logic.ts
- centralizes action resolution and tooltip copy. It already has
shouldHighlightDesktopUpdateError, but the current sidebar button does not apply a destructive presentation.
apps/web/src/hooks/useCopyToClipboard.ts and apps/web/src/components/ui/toast.tsx
- provide established clipboard success/failure and copy-error UI patterns.
docs/lastcode/local-nightly-updates.md and docs/lastcode/nightly-workflow.md
- document the user flow, failure log, and existing CLI progress behavior; update them if the visible behavior or diagnostic contract changes.
Acceptance criteria
- With local nightlies enabled in a packaged Apple Silicon LastCode build, clicking Download immediately changes the sidebar control to an in-progress state.
- Hovering throughout a cold build shows a 1–3 word phase plus monotonic
% est. that advances across worktree preparation, dependency install, every full-CI stage, packaging, and updater staging.
- A reusable full-CI stamp skips directly to the appropriate later percentage without moving backward.
- The phase/weight source of truth is shared with
lastcode-build; tests fail if the CLI and desktop phase models drift.
- The displayed value never reaches 100% until the updater ZIP is verified/staged and the state becomes installable.
- Hosted release downloads retain their existing byte-download progress behavior and wording.
- A deterministic local helper failure produces a red/destructive sidebar update button and an interactive failure hover panel.
- Copy details copies a bounded plain-text diagnostic containing the target identity, last phase/progress, failure context, exact error, and expanded build-log path; clipboard failure is reported to the user.
- Moving the pointer from the button into the failure panel does not dismiss it before the copy action can be used.
- Retry still starts a fresh eligible attempt, clears the visible failure state, and resumes phase reporting from the new run.
- Focused tests cover phase-marker mapping, monotonic/skip behavior, IPC schema/state transitions, local-vs-hosted tooltip/presentation behavior, destructive error styling, and the copied diagnostic payload.
- One integrated packaged-desktop verification demonstrates a successful cold local build and an injected deterministic failure; capture before/after screenshots for the UI change.
Scope boundaries
- LastCode-only. Do not propose this behavior upstream to
pingdotgg/t3code and do not change the hosted release service.
- Packaged Apple Silicon macOS local nightlies remain the only supported local-update platform.
- Do not add a general build/task progress framework, background-job database, new daemon, or polling API.
- Do not parse and expose arbitrary raw build-log lines as user-facing phase labels.
- Do not estimate remaining time or promise a completion timestamp; report only a truthful stage-weighted estimated percent.
- Do not make the entire build log a clipboard payload or add automatic prompt submission. The user explicitly chooses Copy details and pastes it into an agent prompt.
- Do not redesign the install/restart confirmation flow or the nightly release-notes card beyond what is required to host progress and failure feedback.
- Do not weaken build locking, immutable artifact validation, CI stamps, or updater signature/checksum verification.
Problem
The sidebar's LastCode local-nightly Download action is not a normal network download. The first click can take several minutes because it creates or refreshes a dedicated worktree, installs dependencies, runs the 11-stage full local CI gate, builds the macOS artifacts, serves the updater ZIP over loopback, and lets
electron-updaterverify and stage it.Today the in-app experience collapses that work into a generic downloading/building state. During the local build,
DesktopUpdateState.downloadPercentstays at its initial value because progress events only begin later, whenelectron-updaterstages the finished ZIP. The user cannot tell which phase is running, whether the build is making progress, or roughly how much remains. A failure changes the tooltip to a retry instruction, but the button is not visually red and the durable diagnostic details are only available in a toast and~/.lastcode/local-updates/build.log.This became concrete when a local nightly build ran for several minutes and then failed with an error of the form:
After returning later, the user saw the Download button again without an obvious record of what phase failed or a convenient way to hand the failure to an agent.
Desired experience
In-progress hover feedback
While a
lastcode-localupdate is building or staging, hovering the sidebar update button shows:Use the same truthful, stage-weighted approach as the installed
lastcode-buildcommand. This is an estimate based on observed stage transitions and bounded elapsed-time interpolation, not fake precision or a prediction of wall-clock completion. The percent must remain monotonic, stay below 100% until staging actually completes, and clearly identify itself as estimated (for example,Workspace tests · 31% est.).The UI should expose meaningful user-facing phases derived from real pipeline markers. Exact labels can be refined for clarity, but the implementation must cover the actual sequence:
Preparing— select the immutable checkpoint/revision and prepare the dedicated worktree.Installing deps— install the pinned workspace dependencies.Electron setup,Format & lint,Typechecking,Workspace tests,Rust format,Desktop build,Preload checks,Rust tests,Mobile tools,Mobile lint, andRelease smoke.Building artifacts,Branding,Staging app,Installing deps,Building DMG, andFinalizing.Staging update— serve the completed output locally and letelectron-updaterdownload, verify, and stage the ZIP.Do not maintain a second divergent percentage table in the renderer. Extract or otherwise share the existing phase markers and weights from
scripts/lastcode-build.mjsso the CLI and desktop report the same model. The desktop may coalesce broadcasts to avoid excessive IPC/render churn, but it must publish phase changes promptly and enough percent changes to make progress legible.Failure state and diagnostic copy
If the local build or local staging step fails:
Copy detailswrites a prompt-ready plain-text diagnostic summary suitable for pasting directly into a coding-agent conversation. At minimum include:[lastcode:local-update] Local LastCode build failed;~/.lastcode/local-updates/build.logexpanded for the running user).Keep the copied payload bounded and safe. Do not dump the entire log, process environment, credentials, or tokens. If a log suffix is included, sanitize control sequences and cap its bytes/lines; otherwise the absolute log path plus exact failure is sufficient because a local agent can inspect it. Reuse the existing clipboard helper/error feedback patterns rather than silently failing when clipboard access is unavailable.
The retry action must remain available. The interactive hover panel must not close while the pointer moves from the button to Copy details.
Relevant implementation seams
scripts/lastcode-build.mjsBUILD_PHASES,resolveBuildPhaseIndex, andestimateBuildProgressare the existing stage-weighted model.BuildProgressDisplaytails only newly appended bytes from~/.lastcode/local-updates/build.log, tracks the latest real marker, and displays% est..scripts/lastcode-local-update.mjsbuildUnlockedowns the real sequence: worktree preparation,vp install --frozen-lockfile, full checkpoint CI (or a reusable CI stamp), macOS artifact build, validation, and final structured result.scripts/lastcode-local-ci.tsapps/desktop/src/updates/LastCodeLocalUpdates.tsbuildcontract returns only the final built result.apps/desktop/src/updates/DesktopUpdates.tsdownloadAvailableUpdatetransitions todownloading, awaits the entire local build, starts the loopback feed, and then delegates ZIP staging toelectron-updater.handleDownloadProgresscurrently represents hosted/updater byte progress only and broadcasts at 10% boundaries.apps/desktop/src/updates/updateMachine.tspackages/contracts/src/ipc.tsDesktopUpdateStateandDesktopUpdateStateSchemacross the Electron IPC boundary. Add the smallest typed fields needed to distinguish local build phase/progress and carry bounded copyable diagnostics. Do not overload hosted byte-download semantics with ambiguous values.apps/web/src/components/sidebar/SidebarUpdatePill.tsxpointer-events-autowhen release notes need to stay interactive; the failure action needs equivalent interactive behavior.apps/web/src/components/sidebar/DesktopUpdateStatusIcon.tsxdownloadPercent.apps/web/src/components/desktopUpdate.logic.tsshouldHighlightDesktopUpdateError, but the current sidebar button does not apply a destructive presentation.apps/web/src/hooks/useCopyToClipboard.tsandapps/web/src/components/ui/toast.tsxdocs/lastcode/local-nightly-updates.mdanddocs/lastcode/nightly-workflow.mdAcceptance criteria
% est.that advances across worktree preparation, dependency install, every full-CI stage, packaging, and updater staging.lastcode-build; tests fail if the CLI and desktop phase models drift.Scope boundaries
pingdotgg/t3codeand do not change the hosted release service.