Skip to content

feat(cli): update preflight UI while replacing the background service - #36448

Merged
kitlangton merged 2 commits into
v2from
update-preflight
Jul 11, 2026
Merged

kitlangton merged 2 commits into
v2from
update-preflight

Conversation

@kitlangton

@kitlangton kitlangton commented Jul 11, 2026 •

Copy link
Copy Markdown
Contributor

What

When a freshly launched opencode finds a version-mismatched background service and must replace it before the TUI can attach, the terminal currently shows one raw stderr line and then up to ~5s of silence. This PR replaces that window with an animated split-footer preflight:

  • OpenCode monogram + wordmark header showing from → to versions
  • Staged status line with a spring-driven left-to-right dissolve between phrases
  • Progress rail with a critically damped spring and a traveling glow pulse
  • One-line scrollback receipt (✓ OpenCode updated to <version>) that survives above the TUI after handoff

The footer renders at 60fps via the renderer's own frame loop, tears down completely (back to main screen) before the TUI creates its own renderer, and falls back to the existing stderr messages when stdin/stdout is not a TTY.

How

  • packages/client: Service.StartOptions.onStart now receives the registration Info of the service being replaced, so the caller knows the old version. Backwards compatible.
  • packages/cli/src/services/update-preflight.tsx: the component + a small handle (begin/finish/fail). Stage labels linger a minimum of 350ms so transitions read as deliberate motion; real work is never delayed.
  • packages/cli/src/commands/handlers/default.ts: mounts the preflight from onStart on version-mismatch, finishes it after Server.resolve succeeds, writes a failure receipt if resolve fails.

Scope

This covers the fresh-launch restart window only. Already-connected TUIs going through the reconnect herd (#36285) need service lifecycle events that don't exist yet; the finer in-restart stages (draining, health polling) are part of that follow-up.

Testing

  • bun typecheck in packages/client and packages/cli
  • bun run test in both packages (25 + 24 pass)
  • End-to-end in a real PTY: started a service registered as 0.0.1-old, launched a client as 0.0.2-new (runtime OPENCODE_VERSION globals from wrapper entrypoints, isolated XDG dirs). Verified the preflight renders with real versions, the service is replaced (new pid, new version, same URL), the receipt lands in scrollback, and the TUI attaches at the new version.

Demo

Real end-to-end run: a service registered as 0.0.1-old is replaced by a client launched as 0.0.2-new. Preflight plays through its stages, the receipt lands in scrollback, and the TUI attaches at the new version.

e2e-demo.mp4

Flow

sequenceDiagram
    participant CLI as opencode (new version)
    participant Foot as Preflight footer
    participant Old as Old service
    participant New as New service
    participant TUI

    CLI->>Old: discover + /api/health
    Note over CLI,Old: version mismatch
    CLI->>Foot: begin(from: old version)
    Note over Foot: "Keeping your session safe"
    CLI->>Old: SIGTERM (drain + suspend sessions)
    CLI->>New: spawn opencode serve --service
    Note over Foot: "Starting the new background service"
    CLI->>New: poll /api/health until ready
    CLI->>Foot: finish()
    Note over Foot: "Connecting to OpenCode"
    Foot-->>CLI: ✓ receipt written to scrollback, renderer destroyed
    CLI->>TUI: run({ server })
    Note over TUI: fresh renderer, receipt survives above
Loading

@kitlangton
kitlangton merged commit 1ccaca8 into v2 Jul 11, 2026
12 checks passed
@kitlangton
kitlangton deleted the update-preflight branch July 11, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant