Skip to content

v3.1.8 Jacobean

Choose a tag to compare

@amrmelsayed amrmelsayed released this 08 Jun 04:07

v3.1.8 Jacobean

Released: 2026-06-08

Tower restarts are no longer disruptive, and the gemini consult lane migrates to a new backend before Google retires the old one on 2026-06-18. Two coordinated stories with operational impact, alongside a steady cadence of polish.

The dominant story of v3.1.8 is closing the loop on terminal recovery. afx tower stop && afx tower start was leaving builder and architect terminals stranded because an unfiltered lsof selector was killing client sockets (including the VSCode extension host's), and persistent sessions were being reborn under fresh ids. Both root causes are now fixed at the Tower layer, a startup-readiness barrier closes the one rare reconcile-race edge case, the web terminal matches VSCode's near-instant give-up on dead sessions, and the in-pane reconnect notice no longer stacks orphaned lines.

Operationally critical for any team using the gemini consult lane: this release swaps the lane's backend from the retiring Gemini CLI to the Antigravity CLI (agy) ahead of Google's deadline. Install agy and run agy auth before 2026-06-18 to keep consults blocking-equivalent; otherwise the lane skips with a non-blocking COMMENT and your consults run 2-way (Claude + Codex) instead of 3-way.

New Codev: Dev bottom-panel tab plus an always-visible status-bar chip make the running dev server visible at a glance, layered on top of the existing PTY without re-plumbing.

Smaller polish across the Builders tree (no more UNCATEGORIZED flash during cleanup, "No active terminal" self-heals with a Recover action, guarded commands always give feedback), the dashboard (Needs Attention no longer drops merged-but-gate-pending builders), and consult (-m claude correctly bills against the subscription when OAuth is set).

Gemini consult lane swaps to Antigravity CLI (agy) before Gemini-CLI retires (#778, PR #988)

Google retires the Gemini CLI subscription serving (Pro / Ultra / free) on 2026-06-18. After that date the old binary that Codev's gemini consult lane shells out to will stop returning valid responses. This release swaps the lane's backend to the Antigravity CLI (agy) so consults keep working past the deadline.

This is a backend swap, not a redesign. The user-facing surface is unchanged: the model identifier stays gemini, the pro alias is retained, consult -m gemini works the same way, porch's consultation lane continues to address gemini by name. Only the binary that the lane shells out to changed.

A few design calls worth noting:

  • OAuth-only backend. agy cannot take an API key (verified), so there is no separate Gemini Developer API backend. Users authenticate via agy auth (OAuth subscription) — same Google account that powers Antigravity.
  • agy's default model, no version pin. Stays on whatever model agy defaults to. Codev doesn't pin a specific Gemini version anymore (the old Gemini CLI accepted explicit --model gemini-2.5-pro etc).
  • Non-blocking skip when agy isn't ready. Missing binary, unauthenticated, IDE-symlink stub, or per-call timeout all produce a VERDICT: COMMENT. Porch's allApprove treats this as non-blocking, so phases still advance with a 2-way consult (Claude + Codex). This is the failure mode the swap defends against — your consults keep working with a degraded panel rather than blocking.
  • Real-binary preference. resolveAgyBin() rejects the Antigravity IDE's agy symlink (by realpath) and prefers the real headless CLI. CODEV_AGY_BIN env var overrides if needed.

codev doctor now checks for agy presence and authentication state with install guidance. The Gemini-CLI builder harness stays on the retired CLI per the approved spec scope; that migration is tracked as a follow-up.

Tower version preflight: warn when running Tower is behind installed CLI (#983, PR #1000)

The v3.1.7 #791 CLI preflight verified that the installed codev CLI was at least as new as the VS Code extension. That check inspects the binary on disk. It does not, and could not, tell whether the running Tower process is executing that same code. After an npm install -g @cluesmith/codev upgrade without a Tower restart, the two diverge silently: the installed binary is the new version, the running Tower is still serving stale handlers from whatever code was loaded when it last started. The user hits 404s on new routes, stale wire shapes, and bug fixes that don't seem to apply, with no signal anywhere that an upgrade hasn't fully taken effect.

This release closes that gap with three coordinated pieces:

  1. Tower exposes its in-memory version. A new read-only GET /api/version endpoint returns the version of the currently running Tower process plus the boot timestamp. The value is whatever code Tower actually loaded, not the disk binary.

  2. The VS Code extension probes it alongside the existing CLI preflight. On every successful connection to Tower, the extension fetches /api/version and compares the running Tower's version against the installed CLI's version. When the running Tower is behind, a divergence toast surfaces with a Restart Tower action. One click runs afx tower stop && afx tower start, polls /health until Tower comes back up, and re-probes to confirm the restart loaded the newer code.

  3. The healthy path stays silent. When running matches installed, no toast appears. The CLI-row tooltip in the Status view surfaces both versions side by side, so the up-to-date state is visible at a glance without being noisy.

Two design calls worth a conscious nod:

  • The divergence rule is running < installedCLI, not running < extension. Comparing against the extension version would produce a futile restart prompt naming a version that isn't installed (a restart can't load code that isn't on disk). The "CLI is behind the extension" condition stays as #791's existing concern, with its existing "update CLI via npm" toast.
  • The 404 path (Tower too old to even report its version) is gated on the installed CLI itself being current. When the installed CLI is itself the source of the running Tower's old code (the extension updated ahead of the CLI), restarting Tower would just reload the same code that still lacks the endpoint. In that scenario the prompt is suppressed and #791's "update CLI" toast handles the recovery instead. The remedy fires only when the remedy actually fixes the condition.

The in-extension restart action depends on the just-shipped #991 Tower fix: before that fix, the unfiltered lsof in afx tower stop would have killed the extension host's own sockets when the extension self-triggered a restart. With #991's -sTCP:LISTEN scoping, the stop targets only Tower's listener, so the extension self-restarting Tower is now safe.

Terminals survive Tower restarts (#991, PR #999)

afx tower stop && afx tower start used to be disruptive in two distinct, compounding ways. Open builder and architect terminals would drop their connections; the VSCode extension host itself would restart; recovery code that was supposed to reconnect the terminals never got a chance to run because the very process running it was being killed. This release fixes both layers at the source. After a restart, open terminals reconnect to the same session within the normal backoff window, replaying any buffered output, with no dead pane, no manual reopen, and no new window.

What was actually going on

Two Tower-level bugs compounded:

  1. afx tower stop was killing more than Tower. getProcessesOnPort used lsof -ti :PORT to find what to terminate. That selector returns both the listener (the Tower server) AND every client holding a socket to the port. The VSCode extension host holds client sockets (one for SSE, one per terminal WebSocket), so afx tower stop SIGTERM'd the entire VSCode extension host on every restart. Every "click here to reconnect" affordance, every backoff retry, every state-refresh loop that earlier issues shipped was being silently destroyed by the very command that should have left them alone. Fixed by restricting the selector to listening sockets: lsof -ti :PORT -sTCP:LISTEN.

  2. Terminal ids were not preserved across restart. When Tower came back up and reconciled persistent shellper-backed sessions, it minted a fresh randomUUID() for each one. The client's /ws/terminal/<old-id> URL went dead, even though the underlying session was alive on the other side of a new id. Fixed by threading the persisted dbSession.id through createSessionRaw and both reconcile paths, so a session keeps its identity across a restart.

With both fixes in place, the existing transport-reconnect layer (@cluesmith/codev-core/reconnect-policy, the terminal adapters from #936 and #971) handles a Tower restart as an ordinary transient drop. No special recovery code path is needed because the URL the client was already using stays valid.

Why the cycle's earlier terminal work landed where it did

#936 (VSCode terminal reconnect bounding and give-up), #971 (web terminal session-unknown fast-path), and even this issue's original client-side framing were all responses to a symptom whose root cause was upstream. Preserving the terminal id removes the dead-id condition those layers were defensively guarding against. The client-side affordances all remain in place as harmless safety nets, but the common case (a routine Tower restart on pnpm -w run local-install or similar) now self-recovers without ever exercising them.

A small edge case remains: a client reconnect that lands after Tower accepts connections but before startup reconcile re-registers the session could 404 once and recover on the next retry or click. Rare in practice; the deterministic follow-up is tracked separately as #997 (reconcile before serving requests).

Two durable lessons recorded with this release

Worth carrying forward to anyone building recovery layers on top of distributed components:

  • Confirm the runtime survives the event before building recovery on top of it. Many iterations of client-side terminal recovery had to be reverted before checking the single most important precondition (does the extension host even survive afx tower stop?). It did not. A one-line check at lsof -ti :PORT would have redirected the entire effort on day one.
  • Fix the source, not the symptom. Multiple cycles of work across this codebase were tracking consequences of the terminal id changing on restart. The cheapest fix is often well upstream of where the symptom shows.

Tower readiness barrier: terminal reads no longer race startup reconcile (#997, PR #1004)

#991 closed the dominant Tower-restart bugs but explicitly deferred one rare edge case: a client reconnect that landed after Tower accepted connections but before reconcileTerminalSessions() had re-registered persistent (shellper-backed) sessions could 404 once and recover on the next retry or click. This release closes that window.

Tower now uses a startup-readiness barrier as an internal ordering signal. The HTTP listener still binds immediately (preserving liveness, so any supervisor's wait-for-port check is unchanged), but the readers of reconcile's output (getRehydratedTerminalsEntry, the /ws/terminal/:id upgrade routes) await the barrier before responding. The first reachable /api/state and /api/overview after restart now sees a complete role → terminalId mapping with no client-side polling.

The /health endpoint adds an optional ready: boolean field exposing the same signal explicitly, so an external supervisor that wants readiness (not just liveness) can check it without inferring. status: 'healthy' stays pure liveness, which matters for restartTower's existing wait-for-/health logic.

Three independent barrier-release paths ensure serving can never wedge: reconcileTerminalSessions()'s finally block (success or throw), its early-return path when shellperManager is absent, and a defensive timeout (CODEV_STARTUP_READY_TIMEOUT_MS, default 10s). On the defensive timeout the individual request proceeds; the barrier itself stays unsettled until reconcile genuinely finishes, so /health.ready doesn't lie.

One durable lesson recorded with this release: process-liveness is not readiness. server.listen() firing (and /health returning healthy) means the process accepts connections, not that async startup work is done. Gate consumers of startup-reconcile output on an explicit completion barrier, and keep the liveness signal separate from the readiness signal so a slow reconcile can't make Tower look dead to a supervisor.

Web terminal stops on dead sessions almost instantly (#971, PR #992)

When Tower restarts or otherwise loses a session, the dashboard's web terminal used to spend the full 6-attempt backoff (~60s) blindly retrying before giving up, because a browser can't read a failed WebSocket upgrade's HTTP status (it only sees close 1006). v3.1.7's #961 narrowed the retry budget from 50 to 6 attempts but left the underlying mismatch in place. This release closes the gap: the web terminal now matches VSCode's near-instant give-up behavior.

Tower discriminates browser clients from Node clients via the Origin header. Browsers (which always send Origin) get an accepted WebSocket upgrade followed immediately by a close with the app-range code 4404. The core reconnect-policy helper recognises 4404 as a permanent failure, and the dashboard's onclose handler fast-paths on it. Node clients (which never send Origin) keep the existing HTTP 404 upgrade-rejection path that #936's VSCode fast-path relies on, so there is no regression.

One transient retry is still expected when a session is killed mid-connection: the first drop sees the generic 1006 close (transient → one retry), the reconnect attempt hits 4404 → give up. Matches the VSCode sequence. Total dashboard dead-time for a killed session: roughly one backoff interval (1s), down from ~60s.

A follow-up (#991) tracks the next layer: a stale tab on a pre-restart terminal id can't self-recover because persistent sessions return under a new id after a Tower restart. The give-up signal is now correct; the auto-remount-onto-successor-id affordance is deferred.

Codev Dev surface: bottom-panel tab + status-bar chip (#921, PR #996)

Two new complementary VSCode surfaces for the single afx dev PTY, so a reviewer can see at a glance whether a dev server is running, for which target, and stop or restart it fast without hunting through the terminal dropdown.

The Codev: Dev tab (the first real view inside #812's bottom-panel container) shows a status header: target name, live-ticking uptime, and best-effort port when derivable from worktree.devUrls or worktree.devCommand. Title-bar actions for Stop, Restart, Switch Target, and Show / Hide the Codev sidebar. When no dev is running, a placeholder row; when one was running and stopped, a brief "Stopped..." epitaph row.

An always-visible status-bar chip ($(server-process) Dev: <target>) appears in the bottom bar only while a dev is running, disappearing on stop. Click it to focus the Codev Dev tab. The chip is the at-a-glance signal that survives regardless of which surface you happen to be in.

Both surfaces derive from the single TerminalManager.onDidChangeDevTerminals event, so they stay in lockstep automatically. The native Codev: <name> (dev) terminal stays as the actual output surface; the new tab and chip coexist with it as status indicators rather than replacing the output. No PTY re-plumbing.

Two implementation details worth a conscious nod, captured as lessons-learned: VSCode's StatusBarItem.backgroundColor only honors errorBackground / warningBackground, not prominentBackground, so a "prominent but not alarming" cue uses the foreground (prominentForeground) instead. And $(zap) now reads as the AI / sparkle glyph in VSCode, so non-AI features want a literal glyph like $(server-process).

Polish

  • "No active terminal" toast self-heals and surfaces a recovery action (#982, PR #1006). The toast used to fire once per session as a dead-end warning, then silently swallow subsequent reattempts even when Tower was already reconnecting. Terminal resolution now runs a short bounded retry before raising any UI, so transient reconnect windows self-heal silently. When retries genuinely fail, the toast carries Retry and Recover Builders actions; the latter opens a terminal at the main checkout root running afx workspace recover --dry-run for scope review before applying.
  • Builders no longer briefly flash into UNCATEGORIZED during cleanup (#907, PR #1003). The Builders tree used to re-render a being-cleaned-up builder under UNCATEGORIZED for a few seconds before it disappeared, because the area was re-derived per refresh from the open-issues list and the just-closed issue was already gone. A new ResolvedEnrichmentCache memoizes the last resolved area while the issue is reachable; the fallback gates on source reachability, not value emptiness, so genuine cross-cutting values still cache correctly and real label changes still propagate. Builders on issues that genuinely have no area/* label continue under UNCATEGORIZED throughout.
  • Terminal reconnect notice overwrites in place and wipes on reconnect (#1001, PR #1002). The retry notice ([Codev: Connection lost. retrying in Xs (attempt Y/6)]) used to print one newline-terminated line per attempt, stacking in scrollback and leaving them orphaned above the resumed output once Tower came back. Each retry now overwrites the same line, and a successful reconnect wipes it. The red give-up state (after 6 failed attempts) is intentionally preserved as the terminal's failure state and is not wiped.
  • Guarded commands always give feedback now (#989, PR #995). Clicking a CLI-dependent command (Spawn Builder, Approve Gate, Send Message, etc) while the Codev CLI is missing or outdated used to produce a modal toast on the first click of the session, then go completely silent on every subsequent click. The first-click modal is unchanged; subsequent clicks now show a brief auto-dismissing status-bar message pointing at Codev: Recheck CLI as the recovery path. Once a recheck confirms ok, the modal-first pattern restarts the next time the state breaks.
  • New Codev tab in the bottom panel (#812, PR #990). A second view container joins the existing activitybar Codev sidebar, docked alongside Problems / Output / Terminal. Opens once on first activation for discoverability, then stays out of the way. Initially shows a single placeholder row signposting upcoming view migrations (Recently Closed, Team, Status) that will populate the panel in follow-up PRs. VSCode provides no positional control for panel view containers, so a new tab lands last; the one-time reveal is the only discoverability lever available.

Other fixes (dashboard, porch, infrastructure)

  • Builders with a merged PR but a still-pending pr gate no longer vanish from the dashboard's Needs Attention (#966, PR #980). After a merge, a builder whose porch pr gate hadn't yet been approved silently dropped off both the Needs Attention rows and the Work surface entirely. Its PR had moved to recently-closed (so didn't surface via the open-PRs path), while its still-pending gate was incorrectly read as "ready". They now correctly surface via the gate-row path, matching the human-attention model where a merged-but-gate-pending builder is exactly what needs acknowledgement.
  • consult -m claude now bills against the Claude subscription, not the metered Opus API (#985, PR #986). When both CLAUDE_CODE_OAUTH_TOKEN (subscription auth) and ANTHROPIC_API_KEY (metered API auth) were set in the environment, consult's Claude subprocess silently picked up the API key, routing all CMAP traffic through the metered API. The consult helper now strips the API-key vars from the subprocess's env copy when an OAuth token is present, so traffic routes via the subscription. CI and key-only environments are unaffected (no OAuth token → API key still used). Reported by an external adopter at roughly $150/day on a heavy dev day before the fix.

Breaking changes

None.

Install

npm install -g @cluesmith/codev@X.Y.Z
afx tower stop && afx tower start

The VS Code extension ships separately via the Marketplace — Codev extension by cluesmith.codev, version X.Y.Z.

Contributors

  • Amr Elsayed (@amrmelsayed) — Terminals & Tower restarts: drove the root-cause Tower fix in PR #999 (#991, killed the unfiltered lsof + preserved terminal ids across restart) and the readiness-barrier follow-up in PR #1004 (#997), plus the user-facing polish layer across PRs #992 (web terminal session-unknown fast-path, #971), #1002 (reconnect notice overwrite + wipe, #1001), #1006 ("No active terminal" self-heals + Recover Builders action, #982), and #1003 (UNCATEGORIZED flash during cleanup, #907). Codev Dev surface: bottom-panel tab plus status-bar chip in PR #996 (#921) on top of the panel-container scaffold in PR #990 (#812). CLI-version-on-disk vs CLI-version-running divergence detection: PR #1000 (#983). Smaller wins: guarded-command modal-first / ephemeral-after feedback in PR #995 (#989) and CI / test hygiene in PR #993 (#967).
  • M Waleed Kadous (@waleedkadous) — Gemini consult lane migration: swapped the gemini backend from the retiring Gemini CLI to the Antigravity CLI (agy) in PR #988 (spec #778) ahead of Google's 2026-06-18 deprecation deadline, with a non-blocking skip when agy isn't installed or authenticated so phases still advance 2-way. Dashboard reliability: Needs Attention no longer drops merged-but-gate-pending builders in PR #980 (#966). Consult billing fix: routing consult -m claude through the Claude subscription when both OAuth and an API key are present in the environment, in PR #986 (#985).
  • Builders working under AIR / BUGFIX / PIR / SPIR protocols across the PRs in this release.