Skip to content

Latest commit

 

History

History
1198 lines (849 loc) · 130 KB

File metadata and controls

1198 lines (849 loc) · 130 KB

Changelog

All notable changes to Squadrant (formerly claude-cockpit) are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[0.19.4] - 2026-09-04

Fixed

  • False "First turn not delivered" warning + duplicate first-turn paste on cold Claude Code boot (#745). pollFirstTurnConfirmedAt's fixed 100s window was anchored at spawn start, but the hook can only stamp confirmation after the scrape submits — and the scrape itself gets up to ~106s of readiness/submit retries, so the poll could time out ~9s before a delivery that had, in fact, landed. The poll now runs through the scrape's unsettled window plus a 15s post-settle grace (180s absolute cap) and cancels once a verdict is reached. Separately, the "paste never rendered → re-paste once" rule fired on sawDraft=false even when a cold-boot paste had already been submitted before the draft was observed, submitting the whole turn twice; the re-paste (and its confirmedSendToPane fallback) is now gated on the pane still being frozen at the pre-send image.
  • heal daemon false-FAIL on the plist-drift race, and a same-version-bump misclassified as a hijack (#751, #752). forceKickstartAndVerify captured its "before" pid after reconcilePlistAndService's bootout+bootstrap had already started the new instance, so a kickstart -k racing that fresh process could never observe a pid change and reported FAILED on an already-healthy daemon; reregisterDaemon now snapshots the pid before reconcile runs. Separately, the #670 foreign-install guard compared full paths, so a routine same-manager version bump (e.g. pnpm's .pnpm/squadrant@0.19.2/.pnpm/squadrant@0.19.3/) was misread as a hijack and printed the refusal banner on every CLI call; same-manager/different-version is now classified as an upgrade and reconciled with a one-line notice, while a genuine different-manager hijack still refuses. Read-only crew subcommands (list/read/tasks) now skip ensureDaemon entirely.
  • Gap-session handoff facts lost everything but the last exchange (#753). extractTranscriptTail kept only the final user/assistant message per gap-session transcript, so an incidental last message (e.g. a daemon notice) could erase all record of what the session actually did. It now walks the full tail and builds a per-transcript digest — ordered user prompts, assistant final text per turn, tool-call counts, files touched, and PR/issue/commit references — capped at ~8KB, oldest entries truncated first.
  • Boot-gap "daemon was down" alert read as a live outage when delivered late (#744). The alert text carried only a bare minute count, so a copy stuck in an unreachable captain's mailbox for hours read as a current outage once finally delivered. formatDownAlertText now bakes the actual outage window (local time + tz offset) into the message, and the delivery loop prefixes a [stale — generated N ago] tag when a daemon-sourced message is delivered more than 1h after it was generated.

[0.19.3] - 2026-09-03

Fixed

  • Socket directory created world-readable, blocking captain/crew launch on Claude Code ≥2.1.259 (#749). ensureSocksDir() now creates /tmp/cc-socks with mode 0700; Claude Code 2.1.259 refuses to operate against a 755 socket directory.

[0.19.2] - 2026-08-31

Added

  • Event architecture Phase 0-1: fact pipeline + opencode cutover (#739, #740, #743). Introduces @squadrant/core's own event-tracking module — an AgentFact vocabulary, per-agent FactAdapter seam, a LifecycleSource facade wiring adapters to the pipeline, a flight-recorder ring buffer for post-hoc debugging, and a pure fact-to-ControlEvent mapping layer with an adapter conformance test suite. Ships invariant checks I1–I3 (depth-based tool-call pairing) and I4–I6 (trust, unknown-rate, and liveness-disagreement). opencode is moved onto the fact pipeline via a FactAdapter reading SSE frames directly, retiring the old OpencodeControlSource; claude stays on its existing path (shadow only) per the approved design. A generated ControlEvent producer/consumer table plus a --check gate now catches producer/consumer drift at build time.

Fixed

  • False "First turn not delivered" warning + duplicate first-turn re-send (#745). crew spawn's own screen-scrape confirmation could time out even when the first turn genuinely landed, because the crew's UserPromptSubmit hook confirms delivery to the daemon independently of — and can outrace — that scrape. Hook confirmation is now raced against the scrape for the scrape's own full timeout window and treated as primary, not a post-failure fallback: a scrape failure alone no longer concludes non-delivery when the hook already confirmed. As defense in depth, crew send now refuses to re-send the exact original task text once delivery is confirmed, closing the double-run scenario where following the (previously false) warning's guidance re-ran the task a second time.
  • Heal daemon false-FAIL on the plist-drift race (#741, #742). squadrant heal daemon could report a failed restart even when the daemon had actually come back up cleanly, racing its own plist-drift detection against the just-completed restart. Fixed at the source of the race rather than papering over it with a retry.
  • Opencode fact-pipeline perf regression and event-index cache-miss fallback (#743). A per-frame performance regression in the new opencode adapter (introduced during Phase 0-1 development) is fixed before shipping; chatty opencode frames are filtered, resolve() is TTL-cached, and dead snapshot state is removed. The eventsTaskIndex now falls back to a live store lookup on a cache miss instead of losing the event.

Notes

  • Phase 2 (per-crew FactLog cleanup, claude shadow-mode cutover, lifecycle refinement) is deliberately deferred — not part of this release.
  • First opencode crew after upgrade should be watched for event-pipeline health (I5 invariant floods, subagent-orchestrating crews' known ~2 CREW IDLE per task).

[0.19.1] - 2026-08-28

Added

  • Per-role thinking level → --effort. Roles could already pin a model (defaults.roles.<role>.model); they can now also pin a thinking level via a new thinking field, emitted as claude's --effort <level> flag (plus squadrant launch --thinking / crew spawn --thinking CLI overrides). Deliberately distinct from the existing defaults.effort tokenomics dial (squadrant effort) — same word, different concept, kept apart on purpose. Claude-only; codex/opencode/gemini reject the flag.
  • Crew answer (#592). squadrant crew answer <project> <name> <option> deliberately answers a crew's open AskUserQuestion/permission modal — reads the rendered option list, requires an explicit index/text match (never an implicit default), and drives the selection via Down/Up + Enter. crew reply now delivers through the same path as crew send before transitioning task state, instead of silently dropping the message body on a no-op "success".

Fixed

  • False CREW STALLED from a stale pendingTool (#542). Root cause: the daemon's cmux events-bridge had no case for agent.hook.PostToolUse, so pendingTool never closed for a crew whose tool call had genuinely finished. The #492 stall veto then vetoed every real turn-completion forever, and the watchdog eventually fired a false CREW STALLED against an idle crew. Fixed at the transition — events-bridge now closes the window on PostToolUse, the state machine recognizes the cmux bridge's own PostToolUse spelling, and the watchdog gained a defense-in-depth check that recovers to awaiting-input instead of stalling when idle evidence already exists.
  • Pane-error false terminalization (#704). The interactive-probe's error detector matched its error vocabulary against any pane line — including content a crew merely displayed or quoted — and unconditionally terminalized the crew. It now skips visibly quoted/prefixed lines (box borders, blockquotes, cat -n echoes) and cross-checks the crew's surface-liveness probe before terminalizing; an "alive"/"unknown" verdict downgrades to a new non-terminal task.warn ControlEvent, surfaced to the captain pane as "CREW WARN" instead of being silently dropped.
  • Heal daemon restart verification (#729). squadrant heal daemon previously deferred to a cautious plain kickstart (no -k), a documented no-op on an already-healthy daemon, so a plist-matching heal run could report "restart complete" without touching the running process. It now forces kickstart -k, polls the pid until it actually changes, and retries the kickstart -k call itself (default 5×300ms) to avoid racing a preceding bootout's still-unloading exit handler.
  • Unclean daemon death detection and delivery jam isolation (#589, #590). Persists a JSON exit marker on every shutdown path (including crash handlers) plus a running heartbeat marker touched every ~60s; a running marker that survives to the next boot with no exit marker to explain it is now logged and alerted as an UNCLEAN death (SIGKILL/OOM/power-loss), instead of reading identically to a first boot. The delivery loop also wraps each project's per-tick pass in its own try/catch so one project's exception can't starve delivery for every project after it, and backs off exponentially (capped at 60s) once a project crosses its max-defer threshold instead of polling forever.
  • Task records no longer terminalize while the crew surface is alive (#595). reapOrphanedCrews, the wall-clock task-timeout sweep, and store.put() could all previously terminalize or silently overwrite a task record without checking the crew's own pane was actually gone. All three are now gated on the surface-liveness probe (alive/unknown fail safe), awaiting-input is added to the existing #629 blocked/review ceiling exemption, and crew send's reopen is now reported back to the caller instead of being invisible.
  • Oversized first-turn text corruption (#730). A multi-KB first-turn paste into a claude crew's pane could look "settled" before fully landing, letting Enter submit a truncated draft. First-turn text above 1200 bytes is now spilled to a temp file with a short "cat it and follow it exactly" pointer instead of being pasted verbatim; the daemon dispatch still receives the full task text.
  • Captain memory write gate (#556). Crews previously had unrestricted write access to their captain's long-term memory directory, which is loaded into every future session. The existing unmatched PreToolUse hook now denies a matching Write/Edit/MultiEdit/NotebookEdit, or a Bash command whose text references a memory path, when issued inside a crew session — with an exact deny reason logged to stderr. Crews report, captains decide what is durable.
  • Named peer identity for daemon-sent messages (#711). squadrantd's receipt socket now registers a ~/.claude/sessions/<pid>.json entry keyed to the socket it bound, so CREW DONE/BLOCKED/IDLE/TAKEOVER messages render as a named peer instead of the anonymous "Another Claude session" framing. Registry kind corrected to "daemon" (verified against the Claude Code 2.1.241 binary's interactive|bg|daemon|daemon-worker allowlist), and stale entries are cleaned up on both pid reuse and process exit.
  • Delivery probe failures no longer read as "no-box" (#713, #714). A failed cmux read-screen invocation was previously conflated with a genuinely unconfirmed input box. It is now classified as a distinct probe-failed defer reason with its own DELIVERY STUCK wording, and the delivery loop re-resolves the captain surface once per attempt on a probe-failed defer (no-box/modal/draft never re-resolve, since the surface is known alive).
  • Heal status delivery-queue visibility (#715). heal status now surfaces delivery-queue health via a structural stuck boolean on ComponentHealth/HealComponent (no string parsing) — only a genuinely stuck queue fails heal status; a merely deferring queue is advisory.
  • Liveness-tick captain log spam (#732). runLivenessTick logged a captain's derived state on every ~6s tick regardless of change, burying real events for stopped-but-still-listed projects. It now logs only on an actual state transition (or first observation).

Changed

  • Docs. Added the AFK auto-continue safe-option-only clause to captain/crew templates (#616); made the #711 kind-allowlist evidence reproducible in-comment; dropped the stale "unreadable screen" phrasing from the no-box doc (#714); corrected opencode channel state, the captain socket path, and shadow-rollout wording across the agent-control-channel and architecture diagrams (#667).

[0.19.0] - 2026-08-23

Added

  • Agent Control Channel & Captain Channel (#667). Replaces screen-scraped liveness and pane-typing delivery inference with native agent control channels and socket messaging.
    • Implemented the ControlChannel port with a five-branch DeliveryOutcome (delivered, held, denied, unreachable, accepted), plus native LifecycleSource implementations (ClaudePeerRegistrySource, OpencodeControlSource).
    • Added rollout configuration flags: defaults.controlChannel for crew delivery and defaults.captainChannel for captain delivery (off / shadow / on).
    • Claude Peer Channel (Slice 3): Routes crew messaging over Claude's native --messaging-socket-path with NDJSON framing, T1-confirms-T0 delivery receipts, a background receipt listener for held/denied/delivered observability, and records accepted-unconfirmed status.
    • Captain Channel (Slice 4): Routes captain-bound delivery through the peer control socket (captainChannel), bypassing pane-defer machinery (ghosts, modals, no-box). Added Telegram delivery receipts for held and unreachable captain deliveries, and squadrant ping now reports honest delivery outcomes.
  • CLI Agent & Model Runtime Overrides (#627). Added --agent <name> and --model <name> flags to squadrant launch <project> so operators can override configured captain agent and model defaults at runtime.
  • Claude Instruction File Projection Target (#705). Brought CLAUDE.md into the cross-agent projection system (packages/shared/src/agents/projection.ts), unifying instruction emission alongside Codex, Gemini, and opencode to eliminate instruction-file drift across formats.
  • Captain Session Self-Description & Identity Wiring (#708, #709).
    • Passed Claude's -n, --name flag during launch and crew spawn so captains (squadrant-captain-<project>) and crews (squadrant-crew-<project>-<name>) are self-describing in session registries.
    • Resolved captain sessionId from launch-time socket paths and wired it into captain-bound messages, restoring pid-reuse protection.
  • Local Dev Daemon Opt-In (#694). Added the SQUADRANT_DEV_DAEMON environment variable allowing developers to run a local daemon from a repo checkout while maintaining strict refusal for linked worktrees.

Fixed

  • Captain channel init retry loop (#712). Fixed an issue where transient socket bind errors (EACCES) at daemon boot permanently latched the daemon into pane-only fallback mode for its entire lifetime. It now retries initialization with capped exponential backoff, unrefs the timer, and catches retry errors cleanly.
  • Captain socket collisions during batch launches (#706). Fixed launch.ts using an undefined command-level positional argument instead of the target project name, which caused batch (--all) and parallel launches to collide on /tmp/cc-socks/squadrant-captain-undefined.sock.
  • Receipt listener daemon crash on boot and CLI hang (#695, #696).
    • Handled UDS error events during listener startup, scoped receipt sockets by PID (squadrantd-<pid>.sock), and cleaned up stale socket files before binding to prevent crash-loops.
    • Unref'd the receipt server and cleaned up socket files on exit so short-lived CLI commands (like squadrant ping) do not hang.
  • Captain and crew resolution on symlinked paths (#689, #696). Switched registry lookups from working directory paths (which broke on macOS symlinks like /tmp vs /private/tmp) to launch-time messagingSocketPath, preventing false gone or accepted-unconfirmed delivery verdicts.
  • Captain re-adoption with truncated cmux argv (#699). Fall back to reading live arguments from the OS process table (ps) when cmux truncates stored argv at --messaging-socket-path, and added squadrant heal captain [project] --all to reconcile degraded registry entries.
  • Fast-turn confirmation race (#691). Used statusUpdatedAt timestamps to reliably confirm turns on fast-responding agents.
  • Control channel exception safety in crew send (#686). Guarded control-channel exceptions in runCrewSend (both shadow and on modes) to ensure errors log cleanly and fall back safely to pane delivery without breaking the turn loop.
  • Claude captain visibility in liveness registry (#697). Gated launch --messaging-socket-path injection strictly on captainChannel !== 'off' and positioned it last in the command arguments to prevent cmux store truncation from breaking role classification.

Security

  • Refuse silent Anthropic fallback (#627). When an alternative agent (opencode/codex/gemini) is launched with no explicit model and defaults to an Anthropic model via global configuration, squadrant launch now refuses to boot rather than silently falling back to Anthropic.
  • Hardened daemon worktree guard (#682). Strengthened the daemon's refusal to bind production sockets from inside linked worktrees against caller context bypasses.
  • Daemon socket process isolation (#687). Derived the daemon socket path from SQUADRANT_CONFIG, enabling complete process isolation across test and dev environments.

Removed

  • Complete removal of GitNexus integration (#703). Removed all remaining references, skills, and configuration for GitNexus across the codebase, architecture documentation, and agent instruction files following its deprecation.

[0.18.1] - 2026-08-13

Security

  • Restricted permissions on configuration files and directories (#668). Previously, config.json (which holds sensitive data like telegram.botToken) was world-readable (0644). Config files are now tightened to 0600, the config and project directories to 0700, and the daemon socket to 0600 after binding. A one-shot migration automatically secures existing paths on update. Additionally, DEFAULT_CONFIG_PATH and CONFIG_DIR now honor the SQUADRANT_CONFIG environment variable.

Fixed

  • Daemon crash-loop due to duplicate global installs (#670). Fixed an issue where having Squadrant installed globally multiple times (e.g., via npm and pnpm simultaneously) hijacked the launchd plist and caused a daemon crash-loop.
    • The update banner now prints the upgrade command for the install's specific package manager.
    • ensureDaemon now refuses to seize a plist owned by a different, still-installed Squadrant binary.
    • squadrant doctor warns operators of multiple installations.
    • squadrantd refuses to bind the production socket from a monorepo or worktree checkout.
  • False 'healthy' status when daemon is offline (#671). squadrant heal status previously reported all components as healthy if the daemon was completely offline, treating an empty component list as vacuously healthy. It now proactively probes daemon liveness via a real socket connection before trusting component data.

[0.18.0] - 2026-08-07

Breaking

  • squadrant crew close refuses dirty worktrees. Previously, closing a crew would silently destroy its worktree even if it contained uncommitted files. It now REFUSES to close and requires an explicit --force flag. Anyone automating crew close will see a new failure if dirty files exist. (#649)

Added

  • Operator Takeover Protocol (#649). When the operator works directly inside a crew's tab, they can now record that fact so the captain knows to keep hands off. The crew keeps running normally; what changes is that the captain will not send to it, close it, or act on its lifecycle signals until handback.
    • Added CLI commands squadrant crew takeover and squadrant crew handback.
    • Added in-terminal slash commands /takeover and /handback.
    • Task records now track operatorHold status.
    • Operator-held status surfaces in list, tasks, and handoff facts.
    • The watchdog nudges operators on long-running takeovers.
    • The daemon suppresses captain pushes and refuses send/close operations on held crews.

Changed

  • Crew worktrees base on the captain's branch (#661). Crew worktrees are now based on the captain's currently checked-out branch instead of the GitHub default branch, ensuring they branch off the correct contextual state.

Fixed

  • Sweep ceiling measures current working stretch (#664). The sweep ceiling now measures the current working stretch instead of the overall task age. Task records now track the start time of the current stretch on task.started and task.reopened.

[0.17.1] - 2026-08-05

Fixed

  • Read fetchAgeMs post-fetch to resolve false staleness (#654). 'squadrant handoff facts --fetch' reported fetchAgeMs measured BEFORE the fetch, so a just-fetched repo could still trip the 24h stale warning. Age is now read after the fetch; test pins an exact post-fetch age.
  • Human review gate embedded in templates and made signal-agnostic (#656). It now covers CREW REVIEW, CREW DONE and CREW IDLE. Previously only REVIEW was gated, so a crew signalling done or going idle could get its work merged without operator sign-off. Contradicting 'merge if appropriate' wording removed.

Changed

  • Captain startup rules moved to templates (#653). Captain startup rules (fetch origin, check branchState flags, identify current task, read handoff) moved into templates/captain.claude.md and captain.generic.md. They previously lived only in plugin/skills/captain-ops/SKILL.md, which loads only on voluntary skill invocation and never for non-Claude captains. Dead status.md reference removed.
  • Template and skill audit. Audit of all 9 templates and 16 skills: replaced 5 hardcoded /Applications/cmux.app cmux invocations with squadrant runtime equivalents, removed dead read-status.sh and two further dead status.md references.

[0.17.0] - 2026-08-04

Added

  • squadrant handoff facts replaces the old handoff authoring layer, which fabricated state instead of reporting it (#650, #651). The previous command copied fields straight from in-process state (currentState = s.completed), which produced empty blockedItems whenever the live picture didn't map cleanly onto that shape, and let a captain misreport "188 commits ahead" sourced from a stale local ref nobody had fetched. The new command makes no synthesis decisions — it gathers facts and groups them by provenance (liveRepo, claudeMem, checkpoint, gapSessions) so a reader can see which claims come from the live repo versus a stale archive versus a gap in session history, and judgment about what those facts mean is left to the captain reading them, not baked into the tool. handoff.json is now archived on read instead of deleted, so a broken reconstruction can be diagnosed after the fact instead of vanishing with the read. Branch-state verification (branchState) adds fetch-gated truth: upstreamStatus, dirtyWorkingTree, onUnexpectedBranch, and mergedIntoBase flags, with network access opt-in via --fetch rather than silently hitting the remote on every read. Alongside this, the captain session registry (#651) writes one line per session start to {spokeVault}/captain-sessions.jsonl from the SessionStart hook, so transcript attribution has an authoritative record instead of guessing which transcript file belongs to the current captain by file mtime.
  • squadrant tokens — attribute token spend from Claude transcripts (#626). Streams ~/.claude/projects/<slug>/*.jsonl line-by-line (never slurps a whole file — transcripts here run up to 5.7MB) to report, per project and in total: captain vs crew, boot prefix vs accumulated conversation (boot = turn-1 total, confirmed via turn-2 cache_read), and the four token classes separately (input/output/cache_read/cache_write) — no collapsed total, no baked-in dollar rates. Claude-only today; no speculative multi-reader interface added ahead of a second driver writing an equivalent transcript. A same-day follow-up made the command disclose its rolling ~30-day data window: Claude Code's own transcript retention (cleanupPeriodDays, default 30 days) prunes older sessions, so a shrinking file count read like a drop in spend rather than what it actually was — retention. The reader now tracks and prints the actual [earliest, latest] timestamp range each session covers, everywhere a count is reported.
  • Persisted work-item primitive — squadrant work (#630 step B). Per-project JSON store under ~/.config/squadrant/work (atomic write+rename, path-traversal guard, 30-day TTL GC on done/cancelled items, no daemon dependency) with a minimal CLI: start/list/done/cancel, --parent for hierarchical waves, and work list --tree. A same-day follow-up fixed the default view: hiding all terminal (done/cancelled) items orphaned any still-open child of a done parent, rendering it as a parentless root with no sign it belonged to a wave — the exact "wave finished out of order, what's left?" scenario --parent exists to surface. The default view now keeps a done/cancelled parent visible (dimmed) whenever any descendant is still open, and work done <id> now warns (non-refusing) when it still has open children. Step A of the same effort removed the dead write-status.sh instructions and stopped reading status.md for task counts — nothing writes that file anymore, so status/standup/retro were silently rendering stale or zero data; they now say plainly they have no task-count data source.

Fixed

  • A crew's shared production daemon could be bounced by any one of the 26 registered projects' crew processes, exposing every in-flight task across all of them to the restart window (#636). ensureDaemon()'s implicit self-heal ran on every CLI invocation, including crew-spawned ones — which inherit a different PATH/build (worktree-resolved node/pnpm) than the captain, making their plist-drift check unreliable. Fixed in three passes, converging on fail-closed: the daemon's mutating path (plist write, bootout, bootstrap, kickstart) now requires a positively-identified captain invocation (SQUADRANT_ROLE=captain, set at exactly one choke point — launchOneWorkspace) rather than merely the absence of a crew marker, which failed open for codex crews that never receive that marker at all (same failure shape as #499 — marker-absence treated as a positive signal). A small explicit allowlist (isOperatorInitiatedCommand, {launch, init}) covers the two remaining legitimate self-heal cases the captain-only gate would otherwise miss: a fresh install (before any captain marker exists) and a captain launched before this code shipped, whose already-running process can't retroactively gain the env marker until relaunched. squadrant heal daemon is unaffected — it already calls the explicit reconcile path directly, for any role.
  • gitnexus_impact was mandated in CLAUDE.md as an absolute pre-edit safety gate despite returning confirmed false negatives — a crew followed the mandate exactly and broke 6 daemon-core files (#642 item 1). The tool reports 0 callers / LOW risk for symbols that do have real callers. CLAUDE.md's Always Do / Never Do wording is softened so a LOW/0 result now reads as "no information" (cross-check with grep + typecheck) rather than authoritative assurance; HIGH/CRITICAL results are unaffected and remain meaningful. Advisory pending a full replacement decision.
  • Crew boot prefix (61.2k tokens, measured via the new squadrant tokens) was heavier than the captain's own boot (54.5k) and paid on every spawn with little amortization (#625). Crew CLAUDE.md templates only ever reference squadrant:karpathy-principles — the other 15 squadrant skills (captain-ops, telegram, wiki-ops, etc.) are captain/command-only, but crews were served the full plugin directory's skill listing anyway. A new "subset" plugin-dir mode mirrors ~/.config/squadrant/plugin-crew as a strict allowlist (currently just karpathy-principles) and points the claude driver's --plugin-dir at it for role crew only; captain, command, and side roles are unaffected. Measured 33,436 → 32,079 first-turn tokens (a 1,357-token cut) in a controlled headless comparison.

[0.16.6] - 2026-07-29

Fixed

  • Three lifecycle bookkeeping regressions, root-caused from live production evidence before any fix was written (#594, #629). (a) #629 — a crew sitting in review or blocked pending a human decision could be killed by sweep()'s wall-clock task-timeout ceiling, which applied to any non-terminal state with no exemption for states that pause on a decision with no natural time bound; live evidence showed a task enter review and get cancelled by the ceiling ~9h later, permanently closing the crew approve path. Fixed by exempting isStickyAttention states (blocked/review) from the ceiling, reusing the predicate from the #492→#605→#608 family. (b) #594a — a crew awaiting a registered background Monitor watch was misclassified idle: calling Monitor arms a background watch and returns almost immediately, but the crew's turn then genuinely ends (Stop hook) while the watch is still outstanding, and squadrant read that as "awaiting the captain" when the crew was actually about to self-resume; live evidence showed CREW IDLE firing 3× for the same turn in a Stop→auto-resume loop. Fixed with new pendingMonitor bookkeeping (armed on PreToolUse for Monitor, not cleared by its own PostToolUse) that vetoes the turn.completed → awaiting-input transition, bounded by a new stall budget so a crew that armed Monitor once and then went genuinely silent still gets flagged idle eventually. (c) #594b — a CREW IDLE push could arrive after crew close had already terminalized the task, because the notify decision reads a TaskRecord snapshot synchronously while the mailbox write is awaited I/O, leaving a gap a concurrent close could land in; fixed by re-checking the daemon's current record right before writing and dropping a notification the store has since superseded. 11 new tests across state-machine/watchdog/daemon/delivery-loop, all failing before the fix and passing after; full suite (2239 tests) green. Explicitly does not cover #542/#515 — a different root cause (a PostToolUse that's dropped entirely, never clearing pendingTool), unaffected by this change.
  • crew approve's PR title was the crew's raw task prompt — the captain's instruction, not a change summary (#611). Now prefers, in order: the branch's last commit subject, then the review note, then the task-prompt first line as a last resort.
  • Defer/stuck delivery logs gave no way to diagnose which project or why, and the stuck alert's wording actively misled when the real blocker was a modal (#617, #484). Threaded the existing defer classification (modal / no-box / draft, plus a byte-identical-hold "stable" signal) through to both the per-defer log line (project + reason, throttled to onset and every 30th tick) and the stuck-alert text — a modal now gets its own message instead of the generic draft/ghost wording.

Changed

  • installClaudeHooks now unconditionally verifies and repairs the full squadrant hook set on every run (#615), logging a warning when a hook — most notably the AskUserQuestion mapping crew-blocked signalling depends on (#560) — is found missing from an existing settings.json. Also adds an opt-in defaults.claudeEnv config field that deep-merges into settings.json's env block (non-clobbering, a conflicting existing key is logged not overwritten); absent by default, so AFK auto-continue stays off unless a machine opts in explicitly.

[0.16.5] - 2026-07-23

Added

  • squadrant diff — cmux-native crew code review (#596, #599, #604). Opens a crew's diff entirely inside cmux, no VSCode. Phase 1 (#596) adds squadrant diff <project> <crew> via a new RuntimeDriver.showDiff() capability, resolving the crew's worktree/branch through the daemon ledger. Phase A (#599) extends it with --staged/--unstaged/--working to peek at a crew's uncommitted working tree mid-task, not just the branch-vs-base surface. Phase B (#599) adds a non-terminal review checkpoint between crew work and PR creation: crew signal review fires a "CREW REVIEW" alert (parallel to the existing blocked/done machinery, no new parallel system), crew send clears it back to working as feedback, and the new squadrant crew approve <project> <crew> — only reachable from review state — pushes the branch, opens the PR via gh pr create, and terminalizes the task. #604 rounds it out with --pr <N> (wraps gh pr diff), --base/--head/--against (merge-base diff), and a no-args list-and-pick mode over live crews.
  • explainer-reel skill (#598). Wraps iart-ai's explainer-video-skills with a squadrant dark-neon style pack — design tokens, an SVG component library, and a Playwright→FFmpeg GIF pipeline — plus an interactive "swimlane band" HTML mode for click-through system-flow diagrams.

Fixed

  • squadrant diff failed on every real invocation with "Error: --focus requires a value," hidden entirely by CI. cmux's diff subcommand defines --focus as a value flag (<true|false>), not the bare toggle showDiff pushed — invisible to tests because the args array is asserted against a mocked execFile, never handed to real cmux. Fixed to push --focus true.
  • crew approve was unreachable — the new review gate didn't actually gate. Two stacked bugs. (a) Crew's own completion-protocol task.done unconditionally overrode review → done, bypassing the gate because state already read 'done' by the time approve ran (#605): reduce() now vetoes any task.done while state === 'review' unless it carries source: 'approve', the distinct terminal channel only runCrewApprove stamps. (b) That guard only covered task.done — a crew's normal turn-end (task.turn.completed) still fell through to awaiting-input and clobbered review the same way, since a turn always ends right after signal review fires (#608). Factored isStickyAttention(state) (blocked | review) and applied it at every liveness-absorb guard so the two states move in lock-step; task.turn.started is untouched, so the reject/feedback path still resumes review → working.
  • Telegram /launch silently opened nothing (#586). The curated command path execs /launch's argv from the daemon, which has no CMUX_WORKSPACE_ID and no terminal — without --headless, ensureCmuxReady() hit the isInsideCmux() gate, opened cmux.app, and exited before the workspace ever launched. #520 had already fixed this on the boot-if-down path; the curated command path was missed. Audited the rest of commands.ts — no other command carries a terminal/cmux gate.

[0.16.4] - 2026-07-15

Fixed

  • The captain-delivery probe could inject a real backspace keystroke into a draft the captain was actively typing, and force-deliver a queued message on top of it — this fired 4 times in production and stalled a P1 security task for 27 minutes (#579, #484). The maxDefers backstop escalated to a live backspace-invariance probe purely on how many times delivery had deferred, even while the pane's content was still changing every poll — a genuinely typing captain, or a Claude Code suggested-prompt ghost that didn't match the narrow "Press...to..." detection regex (#579), would eventually cross that count and get probed anyway. Probe escalation now requires content stability (byte-identical polls) and never fires on defer count alone, so a live, changing draft is never touched. Because deferring forever behind a genuinely stuck delivery was previously silent, a new "DELIVERY STUCK" alert now fires once per stall (and re-arms after recovery) through the notifier plugin and Telegram — routed out-of-band, not through the same captain mailbox the stall is blocking, so the operator can be reached while delivery is still stuck rather than only after it clears.
  • squadrant status hid a live captain entirely if the project had no status.md, even though status.md is documented as optional (#549). The default table used status.md as the source of truth for whether a project's row rendered at all, so a project that had never written one — or had a corrupted one — silently vanished from the table with "no status.md," even while the daemon's liveness registry showed the captain demonstrably alive. Captain liveness is now always rendered from daemon state, with status.md only supplying the optional crew/task/progress columns on top of it. A corrupt status.md is now shown distinctly ("status.md unreadable", in red) instead of being swallowed and rendered the same as a missing one, and a deliberately-stopped captain now gets its own magenta indicator instead of being lumped in with one that crashed.

[0.16.3] - 2026-07-12

Fixed

  • A crew's blocked AskUserQuestion prompt could go completely unnoticed — no CREW BLOCKED ever fired (#560). A crew's own per-crew Claude hook set had no PreToolUse coverage at all, so opening the question modal emitted nothing to the daemon; detection had accidentally been leaning on an unrelated hook subsystem that didn't reliably cover crews. Crews now get a dedicated PreToolUse → AskUserQuestion hook that extracts the real question and options from the tool call and reports it as blocked-on-input, even falling back to a generic message if the tool payload is malformed — so a stalled crew is always surfaced instead of silently waiting forever.
  • CREW DONE could be lost because the send and close paths disagreed on which task record was "the" active one, and a signal on an already-finished task reported success while doing nothing (#574, #557). When a crew had duplicate task records under the same name, sending and closing picked different ones, so the two ends of a crew's lifecycle could silently drift onto different task ids. Task-record selection is now shared logic used everywhere, and crew signal done|blocked|failed now fails loudly instead of quietly no-opping when the target task is already in a terminal state.
  • effort set --project silently rewrote the global effort dial instead of the named project's override, and captains could be told about an effort change that didn't actually apply to them (#575, #576). The --project flag was accepted but ignored, so a per-project effort request landed globally; notifications were also broadcast as one blanket message, so a project running its own override could be told a value that wasn't actually its effective effort. effort set --project now writes a scoped per-project override (and rejects an unknown project name outright instead of writing a stray file), and change notices are computed per recipient so only captains whose effective effort actually changed hear about it.

[0.16.2] - 2026-07-12

Fixed

  • The daemon never re-adopted a captain after a cmux restart, and silently killed its live crews (#565). When cmux crashes and reattaches a session, it degrades the session's launchCommand — it comes back as a bare ["claude"], stripped of --append-system-prompt-file. squadrant classifies a captain by its launchCommand, so the record read role: "unknown", was filtered out of the liveness snapshot entirely, and runLivenessTick then marked the captain ended purely because it was absent from the snapshot. The captain read stopped for 8 hours while it was demonstrably alive and working; the orphan-reaper repeatedly cancelled its crews' task records while those crews were alive and actively committing code, so CREW DONE could never fire and cross-project dispatch never settled. It was not even hand-fixable: purging the liveness registry and patching the correct pid was overwritten on the next daemon boot, because the snapshot still omitted the project. Fixed in two layers: (1) a session's identity (sessionId) now outranks a degraded launchCommand classification; (2) markEnded now requires positive evidence that the pid is dead — absence from a snapshot is no longer a death sentence. The second layer is the important one: it blocks the whole class. Absence of evidence is not evidence of absence, and reaping is a destructive action that must never be taken on an inferred signal.
  • runtime send / crew send typed a message into the input box, never committed it, and reported success anyway (#566). Two stacked bugs. (a) runCrewSend only wrote a stderr warning when confirmedSendToPane reported the paste-settle-Enter loop had exhausted, then returned normally — so the CLI printed ✔ Sent and exited 0 for a message still sitting unsent in the box. Two live agents ended up blocked on a question that was typed but never submitted, with nothing in the system reporting a problem. (b) A v0.16 regression: routing runtime send through the mailbox (#529) converted an unconditional direct-to-pane send into one gated by the delivery loop — which is gated on captain liveness. With liveness wrong (#565), every message was silently swallowed. Fixed: appendCaptainMessage returns its seq, and waitForCaptainDelivery confirms the delivery cursor acked it before the CLI reports success; otherwise it throws (non-zero exit, no ). crew send now throws on unconfirmed submit. A send now either lands or fails loudly — there is no third outcome. The #529 draft-clobber protection is retained: the send is never pre-gated on liveness, only its confirmation is.
  • Crews running the test suite could starve the machine and take down the cmux control plane (#570). package.json's "test" script was a bare vitest — with no run subcommand, which in a TTY (exactly what a crew has inside a cmux pane) enters watch mode: the process never exits, holds a filesystem watcher on the whole monorepo, and re-runs the entire 2044-test suite on every file change. Several crews, each continuously editing files, therefore each spun an infinite full-suite re-run loop. (This also explains crews appearing to hang: their "run the full suite, then signal done" step could never return.) Fixed to "vitest run" (one-shot, always exits); watch mode is now opt-in via "test:watch" for humans. Measurement also showed that even one-shot concurrent runs saturate the machine (4x peaked at 11.77 load on 12 cores), so scripts/heavy-lock.mjs adds a machine-wide mkdir-based semaphore (macOS has no flock(1)) bounding concurrent heavy commands via SQUADRANT_HEAVY_MAX (default 2). Queued runs wait rather than fail — a failing test command would just teach crews to skip testing. A slot held by a dead pid is reclaimed, so a killed crew can never deadlock the repo. The guardrail now lives in code, not in a playbook: the guidance already existed in captain-ops and was ignored.

[0.16.1] - 2026-07-11

Fixed

  • Crew worktrees silently type-checked and tested against the MAIN checkout instead of their own code (#387): git worktree add never populates node_modules, and crew worktrees live nested under <repoRoot>/.worktrees/. A worktree with no local node_modules does not fail — Node's module resolution silently walks UP past it into the main checkout's node_modules. So a crew's tsc/vitest could resolve workspace packages to the main repo's stale code rather than the crew's own uncommitted changes, and pass. This made crew self-verification unreliable: a TS2339 slipped past every local check and was caught only by CI. addWorktree() now installs the worktree's own dependencies immediately on creation, detecting the package manager from the lockfile present (pnpm / yarn / npm / bun), no-opping for non-JS projects, and warning loudly (rather than silently skipping) when a package.json has no lockfile. pnpm was NOT at fault — each worktree correctly carries its own workspace root.
  • N concurrent crews could starve the machine and take down the cmux control plane (#387): crews run build/test commands at their own discretion, so there is no central point to queue them. The crew's top-level process is now launched under nice -n 10, which every child it forks (tsc, vitest workers) inherits — so a burst of concurrent crew builds degrades gracefully instead of starving cmux and the daemon.
  • Spotlight indexing exclusion for crew worktrees is now automatic (#387): the .metadata_never_index marker is written to the worktree root on creation (macOS only, best-effort). Previously this was a manual, uncommitted, single-machine mitigation.

[0.16.0] - 2026-07-11

Added

  • Update notifier: warn installed users when a newer squadrant is on npm (#536): CLI startup checks registry.npmjs.org directly (never the npm view CDN — see the v0.13.1 silent-publish-failure incident), caching the result for ~24h. Behind → a single actionable stderr line; up-to-date, offline, or timed-out → silent no-op, fired fire-and-forget via node:https with the request socket unref()'d so it never adds latency or blocks process exit. A failed check backs off for 1h instead of re-hitting the registry on every invocation. Opt-out via defaults.updateCheck=false or NO_UPDATE_NOTIFIER.
  • squadrant launch --keep flag (#534): mirrors --fresh — resumes the latest session even after a day rollover or template-hash change, so a captain session can survive across day/template boundaries instead of being forced fresh. Still starts fresh on a genuine first launch. --fresh and --keep are mutually exclusive.
  • Daemon boot/exit markers (#535): the daemon now stamps an explicit boot marker on start and an exit marker on a clean stop(), closing an un-awaited-shutdown gap where teardown could be skipped from the log's perspective on restart.

Fixed

  • squadrant status default view showed a live captain as offline (#538): the default table derived its captain indicator from status.md's captain_session frontmatter — a Reactor-engine relic (retired #155) nothing writes anymore — while --detailed already read the daemon's LivenessRegistry correctly. The default view now shares the same queryHealth() call as --detailed; an unreachable daemon renders ? rather than asserting offline (a false "unknown" is safe, a false "offline" is not).
  • Captain crew send could silently confirm an open modal's default option, dropping the intended message (#516): confirmedSendToPane had no equivalent to the delivery path's hasModalOptionList guard, so a paste+Enter into an open AskUserQuestion/permission modal would confirm the modal's highlighted default instead of delivering the message — this produced two wrong commits in production. crew send now precheck-fails loudly (throws, no "✔ Sent") instead of silently succeeding, without prematurely clearing a task's BLOCKED state ahead of the precheck.
  • Two racing captain records for one project could flip a live captain to gone (#527): when two cmux sessions share a cwd (a live captain plus a stale corpse from session restore), the runtime snapshot returned two records for the same project key and LivenessRegistry's last-write-wins apply could let the dead-pid record win. Records are now grouped and deduplicated per project before applying, preferring the record with a live pid.
  • Daemon-restart and effort broadcasts could clobber an in-progress captain draft (#529): both broadcasts wrote directly into the captain pane via CmuxDriver.send, unconditionally overwriting whatever the user was typing. They now route through the same mailbox (appendCaptainMessage) the delivery loop already drain-protects.
  • CLI-originated interrupts (ping, runtime send) bypassed the same draft-clobber protection (#529, #531): routed through the mailbox; squadrant runtime send-key is removed (see Breaking).
  • Crew close/respawn race could leave a zombie task record or fire a false CREW STALLED for an already-closed crew (#513).

Changed

  • CREW IDLE wording softened (#522): awaiting-input is reached only via a genuine turn-boundary event (task.turn.completed) — never a watchdog-derived path — so it always means the crew deliberately ended its turn, including a long-lived crew pausing between sequential subtasks. The old "review and reply or close" phrasing read like a possible fault; it now reads "turn ended, awaiting your reply."

BREAKING

  • Deleted the squadrant runtime send-key command. It had zero callers across all plugins and templates. Deferring an unconditional Enter keypress through the mailbox guard changes its semantics entirely (what it submits depends on what draft the user typed while the key was queued). If you need to simulate a raw keypress without draft protection, shell out to the underlying binary directly: /Applications/cmux.app/Contents/Resources/bin/cmux send-key --workspace "workspace:N" Enter.

[0.15.0] - 2026-07-08

Added

  • Captain liveness redesign — hybrid ground-truth (retires the streak sweep): captain open/close/crash detection is now driven by a persisted LivenessRegistry (<stateRoot>/liveness.json, survives daemon restart) reconciled from the cmux session store via a new DaemonSurfaceDriver.liveness() seam and arbitrated by a per-tick pid floor. It distinguishes a clean close (stopped) from a crash (gone — store record lingers with a dead pid) from alive, with provenance precedence runtime ≥ agent > scan. Replaces the K=3 title-sweep streak model that produced both false-positives and false-negatives and lost all state on daemon restart. Dashboards, the health IPC, and the Telegram boot-if-down probe now read one ground-truth source.
  • Per-project effort override: resolveEffort now honors a per-project effort in the project override config; squadrant effort --project <name> shows the resolved value. The global dial is unchanged, and a project with no override falls back to it.
  • Web dashboard LIVE "mission control" tab (now the default tab): a compact one-line-per-project view with a state-count header, attention-first ordering (blocked/errored float up, offline sinks), last-seen-age and task-count columns, a search box, sortable column headers, a status filter, and expandable per-crew detail rows. (Per-crew pid/uptime/agent/model are placeholders pending #524.)
  • Web dashboard: status filter + per-captain badge on the projects view.
  • opencode init setup guidance + default global config provisioning (#511).
  • Daemon broadcasts a restart notice to captains on build change (#510).

Fixed

  • Captain misclassified as alive when stopped, blocking Telegram auto-launch (#517): the alive check now reads fresh pid-verified health, so a stopped or crashed captain no longer reads alive and correctly triggers boot-if-down.
  • squadrant launch boot-if-down was a silent no-op outside cmux (#520): added an explicit --headless launch path.
  • CLI and web dashboards showed non-running captains as idle: a captain with no registry entry (unknown) now correctly renders offline.
  • A locked or partial cmux store read no longer false-closes captains: liveness() now throws on an unreadable snapshot (leaving the registry intact) instead of returning an empty set that would mark every captain stopped.
  • CREW IDLE flood during long tool-executing turns (#492): verified fixed (the pendingTool veto holds) and closed.

Changed

  • Retired the title-sweep / K=3 streak captain-liveness model in favor of the registry + pid floor.

[0.14.3] - 2026-07-02

Added

  • squadrant ping/squadrant dispatch reach any registered project (#506): cross-project ping (fire-and-forget) and dispatch (tracked task, report-back) are no longer gated on shared group membership — any registered project can reach any other. Group membership now only grants extras: acceptDelegations gating and boot-if-down (cross-group dispatch fails fast instead of burning the full warmup timeout on a target that was never going to boot). squadrant group dispatch is now a deprecated alias for squadrant dispatch.

Fixed

  • Daemon self-heals undelivered first turns instead of only alerting (#466): the CREW UNDELIVERED watchdog measured "undelivered" from lastHeartbeat, which unrelated heartbeats kept resetting — a dropped first turn could go unrecovered indefinitely while the task still reported working. The sweep now measures the undelivered window from createdAt (monotonic, immune to heartbeat masking) and auto-resends the first turn once past budget, re-checking TUI readiness and confirmation state to stay idempotent. Scoped to claude crews for now.
  • opencode SSE bridge boot-grace raised 30s -> 120s (#504): the bridge's connection-retry window (30s) was shorter than the first-turn delivery budget (90s) it needs to outlast, so under concurrent crew-spawn load a slow-to-bind opencode server could cause the bridge to give up before delivery even completed — permanently blinding turn-end and permission-gate detection for that crew with only a log line, no captain-visible signal.

[0.14.2] - 2026-07-02

Fixed

  • fix(crew): opencode first-turn drop from splash-marker drift (#499): the hardcoded splash marker Ask anything… (U+2026 ellipsis) never matched opencode's real render, Ask anything... (ASCII dots), so first-turn delivery confirmation could false-positive with no retry safety net. Fixed with drift-tolerant matching, a sawSplash fail-closed latch, and a positive readiness gate.
  • fix(web): idle/never-launched projects health-neutral in the dashboard rollup (#498): projects registered but idle/never-launched (captain not alive) no longer trip the master annunciator to DEGRADED, and template-hash drift is demoted out of the rollup — only a genuinely alive captain's delivery backlog or real faults read DEGRADED now.

[0.14.1] - 2026-07-01

Fixed

  • CREW IDLE no longer floods the captain during long tool-executing crew turns (#492): task.turn.completed is treated as liveness-only while a tool call is still in flight, vetoing spurious awaiting-input flaps from racing lifecycle sources.

[0.14.0] - 2026-07-01

Added

  • Dashboard health monitoring (#491, closes #322): four new health signals surfaced on the web dashboard.
    • B1CaptainDelivery.stats() exposes an in-flight defer count and a stuck flag once it crosses maxDefers; Overview gets a "Delivery defers" trend and each project card rolls up to caution/fault (guarded on !captainStopped).
    • B2 — the CREW UNDELIVERED watchdog condition is promoted to a first-class ComponentHealth signal (detail = "undelivered (<state>)") instead of only firing as a notification side-channel; Overview gets a headline banner + count.
    • B3TelegramBridge.health() tracks polling, lastSuccessfulPollAt, and the last poll error, closing the "false green while the poll loop is silently dead" gap; surfaced as a Daemon-tab instrument row.
    • B4LifecycleSource gets an optional health(), implemented on CmuxStoreSource, NativeHookSource, and CodexAppServerSource, aggregated and rendered as per-source instrument rows on the Daemon tab.

Fixed

  • mailboxStats now includes rotated archives (#322): sizeBytes/oldestEntryAgeMs previously only counted the current un-rotated log file.

Docs

  • README rewritten as a user-journey guide (#489): split into a top-level narrative (You → Captains → Crews) plus a separate QUICKSTART and reference doc.
  • Architecture diagrams refreshed (#490): added a self-contained lifecycle+delivery flow diagram and updated the monorepo architecture diagram.

[0.13.5] - 2026-07-01

Fixed

  • Daemon no longer auto-answers the captain's modal (#484): delivery no longer auto-submits into the captain's AskUserQuestion/permission modal. A positive N. Label option-list detector (hasModalOptionList) defers delivery before the ghost/probe branches can mistake a modal's highlighted option for a live draft.

[0.13.4] - 2026-06-30

Fixed

  • Crew first-turn no longer dropped on slow boot (#466): crew first-turn delivery now gates on the Claude pane being CC-initialized (the persistent bottom status block), not just the bare input box which renders during claude-mem cold-init where keystrokes are silently dropped. Readiness budget extended 30s->90s because crews cold-init under load (the captain path boots unloaded). First fix in this class to target delivery readiness rather than post-hoc confirmation.
  • Captain draft no longer clobbered (#258): delivery defers while the captain's input box has an in-progress draft instead of gluing the crew message onto it and submitting. Grapheme-aware liveness probe (handles drafts ending in a space or emoji), 50ms settle re-read, restores any probed character; inconclusive liveness defers. The maxDefers backstop still guarantees eventual delivery.
  • Ghost/hint no longer blocks delivery (#258 follow-up): a ghost/history hint in the captain input box (dim suggestion text) no longer causes delivery to defer forever — a backspace no-op (ghost, non-editable) now delivers, while a real draft (backspace consumes a character) still defers. Closes the regression from the initial #258 defer-on-ambiguity.
  • Terminal events survive daemon restart (#474): CREW DONE/BLOCKED/failed/cancelled events bypass the stale-skip and deliver regardless of age, so a daemon restart >5min after enqueue no longer silently drops them. Added per-decision delivery logging to squadrantd.log.

Removed

  • Inert null-draft escalation (#477): removed a no-op code path (probe escalation was ignored by the cmux driver for null-drafts) and corrected its misleading test. No behavior change.

[0.13.3] - 2026-06-29

Fixed

  • First-turn delivery could still silently drop on a single (non-concurrent) spawn of a large multi-line --agent claude crew — the v0.13.2 fix only covered concurrent-spawn load. Three interlocking causes: the CREW UNDELIVERED watchdog was unreachable for a crew whose first turn never landed (it never leaves the submitted state), the boot-readiness gate could latch onto the claude-mem startup banner (which has HR lines but no input-box prompt glyph) and paste before the real input box rendered, and a screen change during boot could be mistaken for a confirmed submit. The watchdog now also covers quiet submitted crews, the boot gate requires the actual Claude Code input box ( prompt), and a screen-change only counts as delivery when the paste was observed. (#466)

Changed

  • First-turn delivery confirmation is now hook-driven instead of inferred from the terminal screen. A UserPromptSubmit Claude Code hook (registered per crew) fires when the prompt is actually submitted and authoritatively stamps the first-turn-confirmed signal, ending the class of bugs where a screen-scrape heuristic mis-read an opaque TUI. The confirmation is stamped once and is the sole confirmation source for claude crews (the screen-scrape remains only as a fallback when the hook cannot be installed). (#470, #472)

[0.13.2] - 2026-06-29

Fixed

  • First-turn delivery could silently drop under concurrent spawn load: spawning several large multi-line --agent claude crews at once could leave one sitting at an empty prompt (0% context) because the boot-readiness gate latched onto the session-start banner before the input box had rendered, so the paste landed in a not-ready box and the retry loop exhausted without confirming submission — while crew spawn still reported success and the watchdog mislabeled the inert crew as "deep thinking". The boot gate now waits for a parseable input box before pasting, first-turn delivery reports a delivery status and auto-falls-back to the confirmed send path when the paste can't be confirmed, non-delivery surfaces a warning instead of a false success, and the watchdog emits a distinct "CREW UNDELIVERED" alert (via a new first-turn-confirmed signal) instead of "deep thinking" for a crew that never received its task. (#466)
  • Daemon task-ledger cruft and ghost lifecycle notifications: terminal task records accumulated indefinitely, and an abandoned task whose crew surface was gone could still fire CREW IDLE / CREW TIMEOUT notifications, confusingly surfacing mid-session. The daemon now prunes terminal records per project on sweep, suppresses lifecycle notifications for interactive tasks whose surface is provably gone, and crew tasks --all-terminal bulk-purges terminal records. (#457)
  • crew spawn --task-file was not readable from an isolated-worktree crew's working directory; the file is now copied into the worktree root and the crew is given a short pointer first turn. (#458)
  • The release workflow reported success even when npm publish failed (the publish step was continue-on-error), so a bad token could leave npm a version behind while CI stayed green. The publish step now fails the job on error and a verification step confirms the published version is live on the registry. (#463)

[0.13.1] - 2026-06-29

Fixed

  • Large first-turn paste-strand: spawning a crew with a large task description could strand the crew's first turn unsubmitted — the big payload pushed Claude Code into bracketed-paste mode and the submit Enter was swallowed before the paste finished rendering, so the task sat in the input box and never started. An un-rendered large paste is no longer treated as submitted; delivery settles the paste window first, then sends a separate confirmed Enter. (#455)
  • Transient API retry mis-classified as CREW FAILED: when a crew's underlying CLI hit a transient API error and entered its own retry loop, the pane classifier read the in-flight retry as a fatal failure and fired a bogus CREW FAILED signal even though the crew recovered on its own. In-flight retries are no longer classified as terminal failure. (#459)
  • Crew spawn collision on stale branch: if a previous crew left its crew/<name> branch behind (closed without cleanup), spawning a new crew with the same name hard-failed on the branch-already-exists collision. Stale crew branches are now reused or uniquified on spawn instead of erroring. (#460)

[0.13.0] - 2026-06-28

Added

  • Native lifecycle ingestion (#333 Phase 1): squadrant now installs and owns its own Claude Code lifecycle hooks via a NativeHookSource (primary), registered namespaced and non-clobbering in ~/.claude/settings.json, alongside a CodexAppServerSource adapter for the codex app-server and a CmuxStoreSource backup that reads ~/.cmuxterm. A new internal squadrant hooks claude <event> CLI bridges Claude lifecycle events to the daemon. These run additively next to the existing cmux events bridge. (#333)
  • Interactive no-arg squad launch: running squad launch with no project opens a multi-select and boots the chosen captains in parallel.

Fixed

  • First-turn drop regression (#333): AskUserQuestion was registered as an invalid top-level Claude hook event, which made Claude Code show a blocking "Settings Warning" modal at session start that swallowed a freshly spawned crew's first turn. It is now registered correctly as a PreToolUse hook with matcher: "AskUserQuestion". (#333)
  • opencode multi-option picker is now detected as CREW BLOCKED so the captain is prompted to choose, instead of the crew stalling silently while the daemon shows it as working.

[0.12.1] - 2026-06-26

Fixed

  • Crew first-turn could silently strand unsubmitted: a large first-turn payload pushed Claude Code into bracketed-paste mode, so the submit Enter landed as a literal newline inside the paste instead of submitting — the crew sat at 0% with the task stuck in its input box. First-turn delivery now pastes, waits for the paste window to settle, sends a separate confirmed Enter, and re-issues only Enter (never re-pastes) if still unsubmitted. (#339, #447)
  • Large follow-up squadrant crew send messages could strand the same way (the atomic send path hit the identical paste race). The confirmed-submit sequence is now extracted into a shared helper and applied to the follow-up send path too. (#448, #449)

Changed

  • The built-in default crew model is now sonnet (was opus); opus remains opt-in via the extreme routing tier or an explicit --model opus. This matches the intended tokenomics, makes fresh installs default to sonnet, and resolves the recurring config-drift advisory on upgrade. (#446)

[0.12.0] - 2026-06-26

Added

  • Telegram: the captain now sends a typing indicator when it receives an inbound message, so the phone shows activity while the captain works. (#431)

Fixed

  • opencode crew first-turn boot-race: spawning an opencode crew could silently drop the first turn at the splash while the daemon recorded the task as working (false-healthy). Replaced the fixed 2.25s retry window with a time-bounded 15s confirm-on-delivery loop. claude/codex paths unchanged. (#235, #442)

Changed

  • Internal: completed the thin-wrapper refactor arc (#367) — CLI commands now parse-call-format over orchestration repatriated into @squadrant/core behind a DI seam (group, launch, side, telegram, crew). No user-facing behavior change. (#432#439)
  • Internal: repatriated core test suite out of cli (S1, #427); relocated generated codex protocol mirror to packages/agents/vendor/ (S5, #429); disambiguated daemon/delivery twin namings (S4, #428, #441).

[0.11.3] - 2026-06-24

Added

  • Guided onboarding. squadrant init is now a re-run-safe, TTY-safe 5-step wizard (hub vault, agent + projection setup, plugin guidance, first-project registration, optional Telegram) that prints the exact next command at each step; non-interactive runs print the checklist and exit without blocking. (#424)
  • squadrant doctor now prints an inline remediation hint under each FAILING check (e.g. missing config → run squadrant init; daemon unreachable → run squadrant heal daemon; missing plugin → the install command). (#424)

Fixed

  • Self-heal stale pre-rebrand cockpit references in per-project Claude settings. The claude-cockpit → Squadrant rebrand left cockpit crew _hook commands in projects' .claude/settings files, firing cockpit: command not found on every captain/crew turn-end across 11 projects. writePerCrewSettingsLocal now rewrites these on every crew spawn, and migrate-to-squadrant.sh step 4.6 now sweeps every registered project, so the error stops and cannot recur. (#422)

Changed

  • Bumped the opencode compatibility manifest last-verified to 1.17.9 (lifecycle-verified). (#424)
  • Documentation de-cockpit: renamed the monorepo architecture diagram to 2026-06-18-squadrant-monorepo-architecture.html (and the .vi version), and fixed stale cockpit naming / config paths across architecture.html, CLAUDE.md, AGENTS.md, and several specs/plans. (#423, #425)

[0.11.2] - 2026-06-24

Changed

  • Bumped the cmux compatibility manifest last-verified version to 0.64.17. The 0.64.160.64.17 release is iOS features + macOS-app-internal Swift/SPM refactors + CI; it changes none of the cmux CLI subcommands or socket contract squadrant depends on, so doctor no longer warns when running against 0.64.17.

[0.11.1] - 2026-06-24

Added

  • Channel commands now run in any Telegram project topic (#419). Slash commands like /status, /crews, and /notify previously only worked in the supergroup's General command channel; they now run from any project topic too, via a shared runChannelCommand helper that the General channel and project-topic handlers both delegate to.

[0.11.0] - 2026-06-23

Added

  • Tap-first Telegram commands (inline buttons). /notify, /effort, and /crews//launch//mute//unmute now reply with tappable button panels instead of needing typed arguments — pick from buttons, no syntax to remember. Button taps are gated on your user-id (remoteControl) like commands, applied via the existing state writers / curated command runner, and the panel re-renders to mark the new state. Typed forms (/notify cap on, /crews <project>, /effort <mode>) still work for power users.
  • Guided /spawn over Telegram. /spawn now replies with a project picker; tap a project and the bot asks (ForceReply) for the task — your reply spawns the crew. No typed arguments needed. (Completes the tap-first command UX.)

Fixed

  • An incomplete /notify in a Telegram project topic now replies with a tap-first button panel instead of being sent to the captain. A bare /notify (or /notify@<botname>, or a dimension with no value like /notify cap) previously fell through and was appended as a captain message. It is now recognized as a /notify attempt: fail-closed behind remote control, then either applied (when typed in full, e.g. /notify cap on) or answered with the notification panel (Captain on/off · crew tier · mute/unmute). Ordinary messages and /mute//unmute are unchanged.
  • Telegram commands tapped from the / menu in groups now work correctly. Telegram appends @<botname> to menu-tapped commands (e.g. /status@squadrant_bot). The three command parsers (parseCommand, parseNotifyPref, notifyToggle) now strip this suffix from the first token before matching, so menu-tapped commands are recognized identically to manually typed bare commands.

Added

  • squadrant telegram setup auto-captures your Telegram user-id. The running daemon now passively records the sender id from every allowlisted inbound message into telegram-state.json (lastUserId). On a re-run of setup in reuse mode, if the daemon has seen a message from you, setup auto-offers "Enable remote control for your user-id <id>?" without requiring --user-id or a conflicting getUpdates detection poll. Precedence: --user-id flag > getUpdates-detected (first-run only) > lastUserId from state.
  • squadrant telegram setup is now re-run-safe. Re-running setup with an existing supergroup configured skips getUpdates entirely — avoids the 60 s timeout caused by the daemon's poll consuming the single-consumer long-poll channel. New --redetect flag forces fresh group detection; new --user-id <id> flag lets you enable remote control on a re-run without touching getUpdates. Allowlist precedence: --user-id > detected userId (first-run only) > existing cfg.users (preserved).
  • Daemon auto-restarts when you change daemon-cached config. squadrant telegram setup, squadrant config set <telegram.*|defaults.taskTimeoutMs|defaults.cmuxEventsBridge|projects.*>, and project registration now restart the daemon so the change takes effect immediately (was: silently stale until a manual squadrant heal daemon). Use --no-restart to opt out. Interactive crews + tasks + Telegram state recover automatically via the disk store + boot reconcile.
  • Telegram /command menu registration. squadrant telegram setup now registers the bot's command menu automatically, and squadrant telegram register-commands (re)registers it on demand — so /status, /notify, /mute, etc. appear in Telegram's / autocomplete. Setup also reuses an existing bot token on re-run (use --reset-token to rotate it), reports existing project topics so you can see what's already linked, and never recreates topics that already exist in state.
  • squadrant:telegram skill documenting setup, remote control, command registration, and notification tuning.
  • Telegram mute confirmations. Turning a project quieter via squadrant telegram notify <p> off|cap off|crew <lower> now posts a one-time confirmation into that project's topic (bypassing the mute), so you can tell on Telegram that it went silent rather than guessing.
  • Per-project layered config. A new override layer at ~/.config/squadrant/projects/<name>.json resolves as built-in → global config.json → per-project, merged per key (resolveNotify / loadProjectOverride / saveProjectOverride in @squadrant/shared). Fully additive: an absent project file behaves exactly as the global defaults — no migration. The resolver is generic; Telegram notification is its first tenant (per-project effort/models keys are reserved, not yet wired).
  • Telegram notification tiers (per-project). Outbound lifecycle pushes are filtered by a per-project crew tiernonedone_only (task.done/task.failed) ⊂ alert_only (+ blocked/approval/input/timeout, the default) ⊂ all. New CLI squadrant telegram notify <project> crew <tier> / cap <on|off> and Telegram /notify crew <tier> / /notify cap <on|off> (fail-closed behind remote control) write the per-project config file. The live active mute axis (/mute / /unmute / notify <project> on|off) is unchanged and stays in telegram-state.json; the live value overrides the config-default active.
  • Distinct Telegram formatting for task.failed (CREW FAILED + error), task.approval.requested (APPROVAL NEEDED + question), task.input.requested (INPUT NEEDED + question), and task.timeout (CREW TIMEOUT) — previously these fell to the generic line.
  • cap gate on squadrant telegram send — with a project's resolved cap=off, explicit captain messages are suppressed (not sent), independent of idle-mute.

Known issues

  • A config-write restart can orphan in-flight headless crews (interactive crews recover fine) — see #410.

Changed

  • Telegram notifications are now per-project and muted by default. Lifecycle events (crew done/blocked/idle) are delivered to a project's topic only after you engage that project — by sending any message into its topic, by /unmute (Telegram, requires remoteControl), or by squadrant telegram notify <project> on. This changes prior behavior where every project pushed all lifecycle events. Mute again with /mute <project> or squadrant telegram notify <project> off. Command replies and the General command channel are unaffected.

[0.10.0] - 2026-06-23

The Telegram stability slice — closes two usability gaps so the integration is solid enough to release, both gated behind a fail-closed user-id allowlist + an opt-in master switch. Default behavior is unchanged on upgrade (remoteControl defaults to false).

Added

  • Project-topic auto-launch (#403). When a project-topic message arrives and no captain is alive, the daemon boots one (async execFile, bounded warmup poll, per-project debounce) then delivers the message — instead of silently queuing. Acts only with remote control enabled.
  • General command channel (#402). Slash commands in the supergroup's General topic run a curated registry of squadrant operations from the phone: /help, /status, /projects, /crews, /launch, /effort, /config get|set, /spawn. Each maps to a validated CLI argv run via async execFile (no shell passthrough); unknown/freeform input gets a /help hint.
  • User-id allowlist + remoteControl opt-in (#321). TelegramConfig gains users?: number[] and remoteControl?: boolean. Control surfaces act only when remoteControl === true and message.from.id ∈ users[] — fail-closed; chat membership alone is never enough.
  • squadrant config get / config set — read/write a config value by dotted key. config set over Telegram is restricted to a default-deny writable-key allowlist (currently defaults.effort); secrets can never be written remotely.
  • telegram setup enhancement — the wizard now captures your Telegram user-id and offers to enable remote control, writing users + remoteControl idempotently.

Security

  • /config set over Telegram rejects telegram.botToken, telegram.users, telegram.chats, and telegram.supergroupId (default-deny allowlist). Inbound handlers never let an error escape the poll loop, preserving at-least-once offset semantics.

[0.9.2] - 2026-06-22

A patch release adding the agent self-reporting feedback loop and fixing a stale version in the feedback command.

Added

  • Agent self-reporting prompt blockAGENTS.md gains a "Reporting squadrant bugs" section that instructs crew agents to route detected squadrant defects up to the captain, who can then file a GitHub issue. Enables a semi-automatic defect feedback loop without requiring CLI tooling.
  • CONTRIBUTING.md — new root-level contributor guide covering the development setup, the agent self-reporting convention, and how to file issues.

Fixed

  • squadrant feedback now reports the real version. packages/cli/src/commands/feedback.ts was hardcoding "0.1.0" as the squadrant version in submitted feedback; it now reads the actual version from the package at runtime.

[0.9.1] - 2026-06-22

A patch release fixing four issues that surfaced during the v0.9.0 claude-cockpit → squadrant live cutover.

Fixed

  • Migration build no longer aborts on stale workspace links. scripts/migrate-to-squadrant.sh step 6 now runs pnpm install before pnpm build. After the repo folder is renamed, pnpm's workspace symlinks still point at the old @cockpit/* package dirs, so building first failed with hundreds of unresolved-import errors mid-cutover; reinstalling regenerates the @squadrant/* links first.
  • Memory remap no longer false-alarms "DATA LOSS". scripts/remap-claude-mem.sh now flags data loss only when the observation count decreases (-lt), not on any change (-ne). A live claude-mem observer can legitimately insert new rows mid-remap (the script never deletes), which previously tripped a spurious FATAL ... DATA LOSS and aborted.
  • Captain/crew Stop hooks no longer invoke the removed cockpit binary. scripts/migrate-to-squadrant.sh gained a step that rewrites stale cockpit crew _hook commands to squadrant crew _hook in existing Claude Code settings files (repo-level and global). The hook-generation source already emitted the new command, but settings files written before the rebrand kept failing with cockpit: command not found on every Stop/PostToolUse hook.
  • Stray smoke-test file no longer ships in the npm tarball. Deleted scripts/notify-relay-placement-smoke.mjs, which leaked into the v0.9.0 package because package.json files includes the whole scripts/ directory.

[0.9.0] - 2026-06-22

Changed

  • Rebrand: claude-cockpit → Squadrant. The project grew from a Claude-Code-only tool into a multi-agent orchestration layer (Claude, Codex, opencode, Gemini), so the claude- brand was retired. Every brand surface is renamed:
    • npm package claude-cockpitsquadrant; CLI command cockpitsquadrant (+ alias squad).
    • Internal packages @cockpit/*@squadrant/*; daemon bundle dist/cockpitd.jsdist/squadrantd.js.
    • Runtime config dir ~/.config/cockpit~/.config/squadrant; launchd label com.cockpit.daemoncom.squadrant.daemon; hub vault ~/cockpit-hub~/squadrant-hub; skill namespace cockpit:*squadrant:*; crew env vars COCKPIT_*SQUADRANT_*.
  • A one-time migration script (scripts/migrate-to-squadrant.sh, idempotent, with --dry-run + automatic backup) performs the live cutover: moves the config dir and hub vault and rewrites config.json to the new paths/labels.

[0.8.2] - 2026-06-21

Fixed

  • cockpit effort no longer self-notifies the captain that ran the command — the active-notify loop now skips the project whose path matches the current working directory (realpath-canonical, with a stale-path fallback). (#383)
  • Zombie task resurrection — the daemon sweep's timeout branch no longer falls through to clobber a terminal (cancelled) task back to working; terminal states are now sticky, ending the repeated CREW TIMEOUT + name-collision mis-tag. (#380, #378)

Added

  • Explicit stopped project status — closing a captain workspace now reaps its orphaned interactive crews exactly once (on a confirmed captain-gone K-streak) and the dashboard renders a calm stopped state (magenta/⏻) instead of a red CRITICAL fault. A genuine fault (corrupt store, unexpected surface-gone) still rolls up to gone. (#388, #324, #323)
  • Debug-gated send instrumentation (COCKPIT_DEBUG_SEND) — captures a pre/post input-box read-back frame around each captain delivery to catch the intermittent #339 Enter-inserts-newline glitch in the wild. Read-only (never re-sends), strict no-op when the flag is unset. (#386, #339)
  • Global effort dial (cockpit effort <max|balance|low>) — one tokenomics lever the captain honors when spawning crews: max biases toward the strongest model, low toward cheaper agents/models, balance keeps default routing. Captain-discretion, not a mechanical routing rewrite. (#381, #317)

Changed

  • Control-plane store hygiene — automatic garbage-collection of stale terminal task records on sweep, a crewTag helper to disambiguate crew notifications, and a manual purge command with force override. (#382, #378)

CI

  • Runtime smoke step — CI now executes the bundled bins (node dist/index.js --help, crew --help, cockpitd --help) after build, catching NodeNext ESM .js-extension crashes that tsc + vitest miss. (#384, #344)

[0.8.1] - 2026-06-19

A post-reorg cleanup patch. The public CLI surface is unchanged.

Removed

  • notify-relay fully deleted — daemon-direct cmux delivery is now unconditional (the relay proxy hop is gone). Captain-gone detection moved to projectHealth (stoppedProjects/captainMissingStreak). (#332, #373)

Added

  • Semantic crew heartbeat — the watchdog now distinguishes three states instead of one overloaded idle pulse: CREW IDLE (real turn-end, Stop hook only), CREW QUIET (alive but deep-thinking; stays working, no false 'awaiting-input'), and CREW STALLED (a tool call in flight past TOOL_STALL_BUDGET_MS=10min — a recoverable 'possibly hung' warn that auto-clears on the tool's PostToolUse). Degrades to QUIET-only for opencode/codex. (#354, #375)

Changed

  • Thin-wrapper refactorlaunch.ts (446→210) and crew.ts (804→510) now push orchestration logic into @cockpit/core / @cockpit/agents / @cockpit/workspaces (session-freshness, buildAgentCmd, crew-protocol incl. the #278 completion-protocol with an exact-string snapshot guard, crew-lifecycle reap, cmux-readiness, pane helpers), each unit-testable without spawning processes. (#367, #374, #376)

Docs

  • Refreshed docs/testing/crew-lifecycle-checklist.md for the relay deletion + the new CREW QUIET/STALLED model. (#377)

[0.8.0] - 2026-06-18

An architecture release. Cockpit's flat src/ is now an internal six-package workspace monoreposhared · core · agents · workspaces · web · cli — behind a one-way dependency DAG enforced by TypeScript project references, bundled by tsup into the same single dist/index.js (CLI) + dist/cockpitd.js (daemon). The public CLI surface is unchanged. This release also lands daemon-direct cmux delivery (the notify-relay is off the hot path) and a cluster of daemon/lifecycle bug fixes that surfaced during the cutover.

Changed

  • Monorepo reorganization (internal, no user-facing change). The flat src/ tree and three top-level dirs became six private workspace packages, each with a single responsibility, wired by a cli composition root: @cockpit/shared (config schema, types, leaf lib), @cockpit/core (daemon, state-machine, protocol, and the AgentDriver/driver-seam interfaces), @cockpit/agents (the AI-driver seam — claude/codex/opencode/gemini), @cockpit/workspaces (the environment seam — cmux runtime, obsidian workspace, cmux notifier), @cockpit/web (the observability dashboard), and @cockpit/cli (commands, bin entry, daemon host). TS project references enforce the one-way DAG (shared ◄ core ◄ {agents, workspaces, web} ◄ cli) so core can never import a concrete driver or the CLI; adding a new surface or agent is a new folder plus one wiring line. tsup inlines all five library packages into the same two bundled outputs, so the launchd daemon entrypoint and cockpit bin are unchanged. (#352, #355, #356, #357, #358, #361, #366, #368)

  • Daemon-direct cmux delivery (daemonDirectCmux). The daemon now delivers crew lifecycle events straight through the cmux runtime/notifier instead of via a separate notify-relay tab, removing a process-lineage wall and a class of relay-tab-death blind spots. (#332, #342, #345, #346, #347, #348, #351)

Fixed

  • Daemon socket hijack (#360). cockpitd did an unlink-then-bind on the shared socket with no liveness guard, so a second invocation (including a stray CLI) could orphan a live daemon — state reads survived but new connections failed. It now connect-probes any existing socket and refuses to start if a live daemon answers, and short-circuits --help/--version without booting. (#362)

  • Crew-tasks control-plane timeouts = event-loop starvation (#2). A synchronous execFileSync in the cmux driver blocked the daemon's hot path, causing crew tasks/signal/spawn to time out while status kept working — and drove constant daemon PID churn. Converted to async execFile; the churn is gone. (#365)

  • Config read ENOENT in the bundled CLI (#363). package.json path resolution overshot one directory in the tsup bundle (cockpit config/--version failed); corrected to resolve relative to the bundled dist/. (#365)

  • Codex app-server orphans on daemon stop (#3). The codex interactive driver now stops its app-server cleanly when the daemon stops, instead of leaving reaped-to-daemon orphans. (#365)

  • Hardcoded crew-worktree base branch (#359). Crew/side worktrees now derive their base from git symbolic-ref refs/remotes/origin/HEAD instead of a hardcoded develop, so main-based repos work. (#362)

  • Daemon-direct cutover hardening. Re-entrancy guard on the delivery+probe loops (#347), three delivery-storm bugs — cursor corrupt-guard, writeCursor race, stale-skip (#346) — production construction of DaemonCmux when the flag is on (#345), missing ESM .js import extensions causing ERR_MODULE_NOT_FOUND at runtime (#343), and a launch double-run / startup-send confirmation fix under cmux 0.64.16 (#340).

Added

  • /where-i-am (/wim) orientation skill. A quick project-status report for re-orienting at the start of a session. (#364)

Docs

  • Post-reorg documentation refresh: README, CLAUDE.md, and AGENTS.md now describe the six-package layout; a new current architecture diagram (docs/diagrams/2026-06-18-cockpit-monorepo-architecture.html) replaces the pre-reorg overview; and a docs/README.md master index was added. Shipped/superseded specs, plans, diagrams, and research were archived (bundled to the hub vault) so the tree carries only active docs — nothing deleted. (#349, #369, #370)

[0.7.0] - 2026-06-16

A compatibility release aligning cockpit with cmux 0.64.16, headlined by a fix for cockpit launch --fresh (broken by cmux's new pinned-workspace protection) and the elimination of cmux's deprecation noise. Introduces an external-tool compatibility manifest so dependency drift is caught early, plus first steps toward driver-agnostic crew-lifecycle detection via cmux's native event stream.

Added

  • External-tool compatibility manifest + doctor drift check. New src/lib/compat-manifest.ts pins the supported version of every external component cockpit depends on — cmux (min 0.64.0, last-verified 0.64.16), claude (min 2.1.32), node (min 18, last-verified 24.6.0), and presence-checked codex 0.139.0 / gemini 0.38.2 / opencode 1.17.4. cockpit doctor now warns (non-blocking) when an installed tool is below its floor or newer than the last-verified version, surfacing a future breaking update early instead of letting it fail silently. (#325)

  • cmux native event stream for crew-idle detection (B1). The daemon consumes cmux's agent.hook.Stop events as an additional crew-idle signal, keeping the screen-scrape as fallback. (#328)

  • Agent-hook working-state to suppress false stalls (B4/A3). agent.hook.PreToolUse/UserPromptSubmit derive a "working" state so a crew mid-tool-call is no longer misreported as stalled (the #292 class). Additive and gated; the delicate draft scraper is untouched. (#331)

Fixed

  • cockpit launch --fresh works again on pinned workspaces. cmux 0.64.16 refuses to close a pinned workspace; the driver's stop() now unpins before closing, so --fresh replaces the captain workspace instead of leaving a stale duplicate. (#325)

  • cmux deprecation noise eliminated. Migrated the driver to cmux's canonical noun-verb commands (workspace list/create/rename/close) and set CMUX_QUIET=1 in the cmux subprocess env, removing the per-call "legacy alias" notices. Read commands also lock --id-format refs to stay robust against a future default change. (#325, #327)

  • Focus-neutral crew spawn (A1/B3). cmux's new freeform-canvas layout broke the index-based focus-restore dance; the driver now passes --focus false (cmux's new default) and drops the dance entirely, preventing keystroke leakage into a crew's launch line. (#327)

  • --json parsing for workspace list / tree. Replaces brittle regex parsing of cmux text output with structured JSON. (#327)

  • Relay-health log noise pruned. The daemon no longer floods not_found: Workspace not found every sweep on stale closed-crew refs — stale records are pruned and logged once. (#329)

Changed

  • Agent Hibernation evaluated, gated off. cmux's agent-hibernation is global-only and would hibernate the captain/relay, so it ships behind defaults.cmuxAgentHibernation (default false) with documented rationale rather than enabled. (#329)

Docs

  • cmux 0.62→0.64 compatibility audit, the agent-lifecycle + daemon-architecture research dossier, and a workspace-groups (audit C1) deferral note — backing follow-up issues #326 (compat backlog), #332 (deprecate relay → daemon-direct cmux), #333 (driver-agnostic LifecycleSource), and #114 (native codex TUI via hooks). (#327, #330, #334)

[0.6.2] - 2026-06-16

A patch release bundling the web observability dashboard and a startup-delivery fix — the work that accumulated on develop after 0.6.1, ahead of the cmux-compat changes that land in 0.7.0.

Added

  • Web observability dashboard. New cockpit dashboard --web [--port] [--interval] serves a zero-dependency localhost HTTP+SSE dashboard. It assembles a degrade-never-blank FullSnapshot (Tier 0–4: daemon state, crews, mailbox stats, and external probes for cmux / agent CLIs / vaults / config behind injectable runners) and renders pure HTML/SSE with a severity rollup, a stale banner, and remediation text. Ships a light, WCAG-AA theme with an explanatory title on every widget, tabs, and zero-dep SVG donut/sparkline charts. Read-only beta. Closes #314, #319. (#314, #319)

Fixed

  • Dashboard snapshot/probe accuracy. Corrected the hub-spoke vault health check, project-scoped probes, lag reporting, and severity classification; adds a stale ProbeState for template-drift caution. Data audited 68/68 fields accurate. Closes #320. (#320)

  • Startup prompt delivered exactly once. The captain launch path now recognizes non-streaming working states (a shell-waiting spinner carries no token down-counter), so a working captain is no longer misread as idle and re-sent the startup prompt — eliminating the 3× duplicate startup runs. (#312, #292 follow-up)

[0.6.1] - 2026-06-15

A reliability patch addressing relay ghost-materialization, headless launcher I/O pressure, cross-project boot-if-down, and crew spawn focus leakage — plus a build fix and a CI gate.

Fixed

  • Cross-project boot-if-down now works reliably. The target captain is brought to operational before warmup is judged; warmup timeout extended from 30 s → 120 s and exposed as --warmup-timeout flag. Closes #288. (#291)

  • Headless launcher I/O pressure reduced. Task-progress writes are coalesced via a 250 ms / 50-chunk debounce with a final flush, stopping O(chunks) file writes. stdout/stderr capture is capped at a 4 MB tail to bound memory. Closes #88. (#293)

  • Relay no longer materializes Claude Code ghost-suggestions into drafts. A buffer-liveness probe replaces the destructive re-paste mechanism: the probe distinguishes a ghost auto-suggestion from a real draft without clearing the input, so a ghost can never be committed as a crew message. The ~5-min defer stall from the previous heuristic is also eliminated via an early stability-probe path. Closes #294 and #302. (#297, #303)

  • crew spawn (tab) no longer steals cmux focus. Captain focus is restored after the new-pane call, preventing keystroke leakage into the crew launch command. Closes #295. (#299)

  • TypeScript build error in headless-launcher tests fixed. The writeResult mock was typed incorrectly, breaking npm run build. Closes #300. (#301)

Changed

  • crew spawn defaults to an isolated git worktree+branch. Parallel crews no longer collide on a shared working tree; opt out with --shared for small single-file tasks. Closes #296. (#298)

  • CI build-and-test is a required merge gate on develop. Broken builds and test failures are now caught at PR time rather than after merge.

[0.6.0] - 2026-06-14

A coordination-layer release headlined by leveled crew routing and the side-sessions framework, plus two crew-lifecycle reliability fixes — crews now signal DONE on their own, and --worktree crews are genuinely isolated. Also ships experimental cross-project intra-group delegation.

Added

  • Leveled crew routing. Captains now pick a crew's agent + model by task tier via a configurable defaults.crewRouting.rules[] ruleset (JSON, keyword → tier → {agent, model}, first-match-wins by array order). resolveCrewRoute is consulted at spawn; an explicit --agent/--model always overrides. New cockpit:add-pick-crew-rule skill edits the ruleset. Default tiers: extreme→claude/opus, hard→claude/sonnet, mobile→codex, daily→opencode. Closes #275. (#276)

  • Side-sessions framework. New cockpit side spawn|send|list|close --role research|debug opens a dedicated fresh-context tab on the captain model (opus), deliberately off the crew/daemon lifecycle — no task record, no CREW IDLE/DONE noise to the primary captain. research discusses ideas and produces artifacts (issue/spec/plan) with no edits; debug does systematic-debugging in an isolated scratch worktree (instrument + failing test, never ships) and hands a diagnosis + optional draft patch back. Report-back is offer-and-confirm: a structured handoff via cockpit runtime send to the captain pane plus a durable {spokeVault}/side-handoffs/<topic>.md record. New cockpit:side-session skill. Closes #283. (#284, #285)

  • Cross-project intra-group delegation (experimental). cockpit group dispatch <to-project> "<task>" records a tracked task on a same-group sibling and wakes its captain via the existing mailbox/relay; the dispatcher yields and is notified when the task settles (done/blocked/failed). Validates same-group + acceptDelegations, attempts boot-if-down with a bounded warmup poll, and rejects loudly (task not recorded) on warmup failure. Experimental: boot-if-down of a down sibling does not yet reliably produce an operational target captain (#288) — works best when the target captain is already up. Closes #246. (#274)

  • PR-time CI. ci.yml now runs the build + full test suite on every pull request to develop and main, closing the gap where broken tests could reach develop silently (tests previously ran only on push to main). (#273)

Fixed

  • Crew DONE is now signalled reliably and unprompted. claude and opencode crews used to finish their work, report via text, and end the turn without ever running cockpit crew signal done → the watchdog parked the task at awaiting-input (CREW IDLE), so the captain never saw CREW DONE. The first turn sent to claude/opencode crews now carries a concrete completion-protocol suffix with --task-id/--project baked in (codex parity — robust to the keystroke-env race AND to model discretion). captain-ops also gained a "Handling CREW IDLE" reconciliation step (classify done-vs-waiting-vs-working on a single spot-check). Closes #278. (#281)

  • --worktree crews are now isolated. A --worktree crew used to run git in the captain's MAIN checkout (the pane was created in the captain workspace cwd and the session never cd'd into the worktree), dragging the captain's HEAD onto the crew branch. The claude/opencode launch now cds into the spawn cwd first (no-op for non-worktree spawns). Closes #279. (#282)

  • crewRouting config-migration backfill. Existing ~/.config/cockpit/config.json files written before routing existed never received defaults.crewRouting, so leveled routing was a silent no-op. loadConfig now backfills the default ruleset when absent, persists it once, and prints a one-time upgrade notice. Closes #286. (#289)

  • Relay draft-preservation third state. parseDraftFromScreen now defers delivery on an overlay/unknown screen instead of misclassifying it, so a crew reply can't clobber an in-progress captain draft in that state. (#268) (#272)

  • Daemon teardown flake + crew anti-polling guidance. The daemon test teardown now awaits async server close (eliminates an ENOTEMPTY flake, #146); captain-ops gained an anti-polling guard so captains don't spin unbounded until loops reading crew screens (#241). (#277)

[0.5.4] - 2026-06-11

A stability release headlined by the RAM-flood fix — orphaned headless claude -p sessions no longer accumulate until the machine runs out of memory. Also adds per-spawn --model override, captain-draft preservation in the inbox, a captain-managed relay with live cockpit relay logs, PROTOCOL_VERSION framing, cockpit heal, and project-management skills.

Added

  • --model <alias> flag for per-spawn crew model override. cockpit crew spawn --model <alias> overrides defaults.roles.crew.model for a single spawn, taking precedence over runtime config — fixing model-drift when the stored config is stale. Closes #250. (#265)

  • cockpit-register-project and cockpit-new-project skills. Two agent-usable skills so captains can register an existing repo (resolve path, derive name, pick group, cockpit projects add, verify) or stand up a brand-new GitHub repo (gh repo create → clone → register) without hand-editing config.json. Both document the --group-role auto-primary gotcha. Closes #262. (#263)

  • Captain-managed relay supervisor. The captain now owns its notify-relay as a single run_in_background process running an in-process restart loop (3s backoff), replacing the separate ✉ notify-relay cmux tab spawned by cockpit launch that could die unnoticed. Closes #240. (#242)

  • cockpit relay logs <project> [--follow]. On-demand live visibility into the captain-owned relay over a per-project unix socket — no persistent logfile (lines are broadcast to connected readers only and dropped when nobody is watching), and the relay core is untouched (wired via the existing opts.log injection point). Closes #244. (#247)

  • Relay logs a deliver line on successful delivery. The relay's happy path now logs each crew signal flowing through, so a healthy relay no longer looks idle in cockpit relay logs — previously it only logged on failure. (#244) (#248)

  • Relay-as-cmux-proxy for crew-surface liveness. Surface-liveness probes now run inside the captain's cmux lineage (where the relay lives) and post results back to the daemon, instead of the launchd daemon calling cmux directly — which always returned empty "gone" verdicts and ghost-reaped live crews. The pre-result default stays "unknown", which never reaps. (#239 Phase B) (#257)

  • Socket-boundary schema validation. daemon.handle() validates event.type against the full ControlEvent union before touching state and fast-errors on malformed frames; the event reducer gained an exhaustive default so an unknown/future event type can never return undefined. Closes #87. (#256)

  • Wire PROTOCOL_VERSION and keepalive framing. src/control/protocol.ts now exports PROTOCOL_VERSION = 1. The client (sendRequest) stamps _v on every outgoing request; the server stamps _v on every reply. On a version mismatch the client rejects with a clear error (cockpitd protocol vN, this client expects vM — upgrade cockpitd or this CLI) instead of silently misparsing. An absent _v (pre-v1 daemon) is treated as compatible — no breakage on rolling restart. The bump policy is documented inline: bump for any wire-shape change. startServer also emits a {"type":"_keepalive"} frame every 10 s on held-open attach connections (crew-attach stream and future subscribe channels), using an injectable clock so tests drive the timer without real delays. createDecoder and decodeFrames silently discard keepalive frames at the shared decode layer, so no consumer ever sees them. Closes #92 and #94.

  • cockpit heal <component> — targeted remediation surface. Three subcommands close the detect → notify → remediate loop for remote/unattended operation. heal status [--project P] [--json] (dry-run: prints unhealthy components and the exact fix command; --json is machine-readable for skill/Telegram bridges; exit 0=healthy, 1=error, 2=unhealthy). heal relay <project> (re-establishes the notify-relay via the existing spawnInjector primitive; idempotent — no-op on alive/stale relay so it never competes with the captain's #240-owned supervisor). heal daemon (restarts cockpitd via the idempotent launchd kickstart path). cockpit heal crew <id> is explicitly deferred (overlaps #100). Closes #234. (#234)

  • Hard crew task-timeout. The daemon sweep now detects non-terminal tasks that exceed a per-task wall-clock ceiling (default 8h, configurable via defaults.taskTimeoutMs). When the ceiling is crossed the daemon fires a detect-only CREW TIMEOUT escalation to the captain via the existing mailbox → notify-relay pipe — the same path CREW STALLED / CREW DONE ride. No state change or kill (detection-first, per #77). Distinct from the heartbeat/stall budget, which only measures heartbeat freshness; a continuously-heartbeating crew stuck on one task for hours is now caught. Closes #225. (#225)

Fixed

  • RAM-flood root-causes — the freeze that filled swap and forced restarts. Orphaned claude -p headless sessions were accumulating until the machine ran out of memory (seen at 13.5 GB swap on 24 GB, load avg 82). Three root causes fixed: (#259) the launchd-throttled (KeepAlive+ThrottleInterval=10) cockpitd crash-loop that re-dispatched headless tasks on every boot — a stray src/control/cockpitd.js launch path is guarded, socket-write failures no longer escape as fatals, and an inFlightHeadlessIds guard stops reconcile() from double-dispatching; (#260) the test suite shelling out to the real claude CLI when startCockpitd ran without a mocked spawn — a launchHeadless injection seam isolates tests; (#261) headless children orphaning to PPID 1 and surviving daemon death — activeHeadlessKills reaps them on stop(). Verified end-to-end: a live daemon on the fix ran with a single boot, 0 crash signatures, and 0 leaked sessions. (#264)

  • Captain's in-progress draft is preserved on relay delivery. Typing in the captain inbox while a crew reply arrived used to concatenate your draft into the delivered message and submit both. The relay now defers delivery while you have a real in-progress draft and delivers only when the input is empty (deliver-when-empty); a configurable walk-away fallback (relay.maxDeferDeliveries, default 300 ≈ 5 min at the ~1s poll) force-delivers a long-held draft via best-effort backspace clear-and-restore. parseDraftFromScreen is scoped to the live input box so transcript text never triggers a spurious defer or gets re-pasted. (#258 — #266, #267, #269)

  • cockpit shutdown now terminalizes crew task records before closing workspaces. Previously, closing a captain workspace left all its crew task records non-terminal in the daemon store (ghost records). On daemon restart the #225 timeout sweep fired against every ghost simultaneously, flooding the captain with CREW TIMEOUT notifications. cockpit shutdown [project] now sends task.cancelled (reason: captain shutdown) for every non-terminal crew task before closing the workspace — the same terminalization cockpit crew close already performed. Daemon errors during terminalization are swallowed so a down daemon never blocks the workspace close. Closes ghost-source root cause of the #225 timeout flood. (#225)

  • Crew task-timeout now terminalizes the record (persistent dedup, flood-proof across restarts). The prior #225 implementation used an in-memory firedTimeout Set that reset on daemon restart, re-firing every CREW TIMEOUT notification for all still-non-terminal tasks on every restart. The Set is removed; when a task exceeds the wall-clock ceiling the sweep now transitions it to cancelled (lastEvent: "sweep.task-timeout") via store.put before firing the notification. The terminal state is the persistent dedup: TERMINAL_STATES.has(r.state) at the top of the sweep loop gates every future pass, including passes from a freshly-restarted daemon instance. The timeout message continues to report the task's original state (e.g. state: awaiting-input), not cancelled. Reverses the detect-only decision from #77; detect-only + volatile dedup was the flood bug. (#225)

  • Running captains no longer show 'gone'. Captain liveness now derives from the relay heartbeat the daemon can see over the socket, instead of a cmux read the launchd daemon is always denied. Relay beating → captain alive; heartbeat gone → captain gone; no relay registered → unknown (no false alarm). (#239 Phase A)

[0.5.3] - 2026-06-06

A reliability and config-hygiene release. Adds a service-health layer and config-drift detection, and hardens the daemon against false crew-cancellation and hung cmux subprocesses.

Added

  • Service-health layer. Relay register / health-check / heal plus component liveness; cockpit doctor and cockpit status --detailed now surface component health, and the daemon best-effort heals a downed relay while always surfacing it as actionable. (#226, closes #207, #77, #208)
  • Config drift detection. config.json carries a _cockpitVersion stamp and is checked against the current default schema after an update. cockpit config check --fix applies the safe tier (missing/deprecated keys); the config-doctor skill reconciles judgment calls (changed defaults, invalid values). Closes the config.json half of cockpit's auto-update story. (#230)

Fixed

  • No more false-cancellation of live crews. SessionEnd terminalization is gated behind a surface-liveness probe, so a nested/subprocess SessionEnd (GSD, subagents, claude-mem) no longer cancels a live crew. Fixes a regression introduced in 0.5.2. (#229, closes #227)
  • A hung cmux can no longer wedge captain/relay. cmux subprocess calls now time out (15s) so a hung cmux fails fast instead of wedging the captain or relay. (#228, closes #209)
  • reapCrewChildren works on busy machines. Raised the ps auxE maxBuffer so child-process reaping does not silently fail under load. (#222 — shipped in 0.5.2; its changelog entry was omitted at the time.)

[0.5.2] - 2026-06-05

A daemon-reliability and crew-safety patch release. The headline fix terminalizes dead interactive crews so they stop re-emitting false CREW STALLED alerts, plus per-crew worktree isolation and shell-injection-safe crew dispatch.

Added

  • Per-crew git worktree isolation. Crews can now run in their own git worktree via --worktree, so concurrent crews no longer collide on a shared working tree / HEAD. (#216, #218)
  • opencode CP3 permission gate. Interactive opencode crews can surface permission.asked to the captain (opt-in), closing the last notify-and-answer gap for opencode. (#215)
  • Injection-safe crew dispatch. cockpit crew spawn/send accept --task-file / --message-file to pass briefs and messages by file, bypassing shell metacharacter substitution and inline-brief truncation. (#177, #205)

Fixed

  • Dead interactive crews are now terminalized. A three-part fix stops orphaned interactive crews (no live pane, no heartbeat) from oscillating working ↔ stalled and firing false CREW STALLED alerts forever: SessionEnd now terminalizes a claude crew instead of resuming it to working; crew close terminalizes the daemon task even when the pane is already gone; and a surface-liveness backstop in the daemon's sweep/reconcile reaps crews whose surface is provably gone. Liveness-based, so the 24h interactive heartbeat budget (#131/#133) is preserved. (#139, #219)
  • notify-relay no longer silently drops events. The daemon↔relay formatter is unified so the daemon is the single source of truth; CREW IDLE and task.approval.requested events reach the captain instead of being discarded on formatter drift. (#210, #214, #217)
  • codex first-turn race. The initial codex turn is no longer dropped ("no thread for task") — first-turn say() is gated on the in-flight dispatch. (#212, #213)
  • Bounded crew read/tasks output. cockpit crew read/tasks output is bounded to prevent truncated results and /compact churn. (#206)

Docs

  • Corrected the crew-lifecycle checklist methodology (two signal mechanisms, CP4 gap #210, 2026-06-03 findings). (#211)

[0.5.0] - 2026-06-01

This release closes the cross-agent crew-lifecycle parity goal: all three agents (claude, codex, opencode) now have a controlled lifecycle plus notify-and-answer on questions/permissions, each driven by a reliable signal source rather than screen-scraping.

Added

  • opencode SSE turn-end bridge. Interactive opencode crews now launch as opencode --port <N>; the daemon opens a long-lived subscription to the crew's /event stream and maps the documented session.idle event to a turn-end, so it learns a crew is idle without the crew shelling out to cockpit. The daemon no longer sits at working forever. (#188)
  • codex crew sandbox parity. Codex crews now run with sandbox: "danger-full-access", matching the already-unsandboxed claude/opencode crews, so cockpit crew signal done|blocked|failed can reach the daemon socket. The full codex signal lifecycle (question / done / reopen) now works end-to-end. approvalPolicy remains an independent axis, so the permission gate still fires under --approval. (#190)
  • Semi-automatic claude crews. acceptEdits permission mode plus a permission allowlist let crews run on cheaper models while still gating risky operations. (#178)
  • Event-driven permission detection. The claude Notification hook surfaces a real permission prompt as CREW BLOCKED within ~0–3s; the in-cmux notify-relay also detects crews parked at a prompt. (#180, #181)
  • Trailing-question detection. A crew that ends a turn on a question is surfaced to the captain as CREW BLOCKED. (#174, #176)
  • Crew lifecycle test checklist. A reusable 6-checkpoint regression harness to run on any crew/daemon/relay/template change. (docs/testing/crew-lifecycle-checklist.md)

Fixed

  • codex approval round-trip. answer() maps approve/deny to the codex app-server schema (old approved/denied, v2 accept/decline), so captain approvals are accepted instead of silently rejected.
  • Turn-end no longer clobbers a blocked crew. task.turn.completed from blocked is now a no-op, so an app-server/SSE trailing turn-end can't drop the question a signal blocked just raised.
  • Silent mid-turn re-block. crew send to a blocked/awaiting crew re-arms the daemon so a subsequent permission prompt re-fires CREW BLOCKED. (#183)
  • crew close terminalizes the task via a silent cancelled state, ending phantom CREW BLOCKED/IDLE pushes after a captain-initiated close. (#184)
  • Exactly-once first-turn delivery plus an opencode boot-race readiness gate. (#175)

Changed

  • CREW IDLE notifications reverted. The idle-ping feature (#182/#185/#185b) was removed: it depended on the claude Stop hook, which fires unreliably in the claude-mem/cmux environment (a probe sat at working for 216s+ with the hook never firing). Reliable idle/turn-end detection now comes per-agent from real protocol events — opencode SSE session.idle and codex app-server TurnCompleted — instead of a flaky hook.

[0.4.0] - 2026-05-29

Added

  • Control-plane daemon (cockpitd). A new background daemon provides an AF_UNIX socket server with newline-JSON framing, a task state machine, atomic per-task JSON state store, heartbeat watchdog with stall detection and automatic recovery, startup crash-reconciliation, and self-healing daemon management on every cockpit invocation. A launchd plist target is included for macOS service integration. (PR #85 and the full control-plane series)
  • Codex interactive crews. An AppServerClient speaking the codex app-server v2 protocol (mandatory handshake, thread start/resume/read, id-correlated requests, notification fanout), a CodexInteractiveDriver owning the app-server child process, an approval/gate primitive, a cockpit crew attach cmux-tab renderer, and cockpit crew chat --provider codex / --approval / reply --gate verbs. (#86 interactive slice, #96–#104)
  • Claude interactive crews routed through the daemon. Claude crew sessions now flow through cockpitd rather than bypassing it, unifying the session lifecycle under the daemon's state machine. (#108, #64 slice)
  • opencode interactive crews wired through the daemon. opencode crews gain a dedicated crew template, per-crew permission configuration, and autoApprove/model passthrough, all served through the daemon. (#127, #128, #129)
  • Daemon push-notifications to the captain. Terminal task events are delivered to the captain via an in-cmux relay, keeping the captain informed without polling. (#109, #110, #111, #112)
  • Mailbox + injector foundational refactor. A new mailbox abstraction and injector layer underpin the daemon's communication channels. (#113, #116)
  • Dashboard status grid now reads live daemon task state. The dashboard no longer depends on status.md — it queries the daemon directly for current task state. (#154)
  • Self-contained architecture HTML report, with a Vietnamese translation. (#147, #149)
  • Process-cleanup rule added to crew templates and the captain-ops skill to ensure child processes are cleaned up on session exit. (#164)
  • Release automation. A GitHub Actions workflow tags vX.Y.Z from package.json, publishes a GitHub Release with notes from the CHANGELOG section, and (when an NPM_TOKEN secret is set) publishes to npm — on every push to main. (#170)

Changed

  • Crew sessions use an identity-first generic template with a no-nested-subagents rule, replacing agent-specific templates. (#105, #106)
  • Source-managed directories self-heal on every cockpit invocation. Missing directories under source control are re-created automatically. (#74)
  • Plugin manifest registers the cockpit skill namespace. Dead plugin/package.json removed. (#72, #73)

Fixed

  • Multi-line crew prompts no longer fragment. Newlines are collapsed before the cmux send, preventing truncated prompts. (#136, #166)
  • First-turn crew dispatch no longer drops on slow CLI boot. Fixed delays have been replaced with pane-readiness polling for reliable first-turn delivery. (#165, #167)
  • False CREW STALLED alerts eliminated. The Stop map now correctly resolves to awaiting-input, and the heartbeat refreshes mid-turn via a PostToolUse hook. (#124, #131, #133)
  • cmux shell-injection closed in sendToPane/sendToSurface and the notify path. (#119, #122)
  • notify-relay now runs as a hidden background tab rather than a split pane, preventing accidental interference. (#117, #123, #161, #162)
  • Daemon-bounce loop fixed by separating PATH drift detection from program-arg changes. (#126)
  • cmux stderr no longer leaks into the captain terminal. (#121, #125)
  • Fresh-install gaps closed: cmux binary path resolution cascade (issues #1, #144), launchd plist PATH baking (issues #5, #143), and a reconciled Node >=18 floor across README, cockpit doctor, and package.json (#142).
  • Cockpit hooks delivered via .claude/settings.local.json instead of --settings, aligning with Claude Code's recommended hook mechanism. (#134, #137)
  • codex approvalPolicy defaults to 'never' for unattended crews. (#132)
  • task.reopened semantic fixed. Re-tasking a done crew now fires CREW DONE again as expected. (#148, #150)
  • vitest scoped to src/**/*.test.ts to avoid picking up non-source test files. (#157, #158)
  • Captain tab renamed and pinned so crew reports route to the correct surface. (#83, #84)
  • Projection reads the canonical project source outside the cwd sandbox. (#63)
  • Control-plane red-team hardening: path-traversal sanitization, fail-loud interactive dispatch, and PATH baked into the launchd plist.
  • Captain is notified when a crew goes idle. An idle interactive crew now transitions to awaiting-input and fires a single accurate CREW IDLE notice instead of a misleading CREW STALLED; the explicit signal done path still fires CREW DONE. (#172)
  • codex crews can report terminal state. cockpit crew signal accepts --task-id/--project flags, and codex threads receive their concrete task id + project via developerInstructions, so codex crews can signal done/blocked/failed like claude/opencode. (#173)

Removed

  • Reactor engine. The always-on GitHub poller / auto-delegation engine has been retired — reaction rules (reactions.json), the polling and matching scripts, the auto-status poller and status classifier, the reactor role and its skill, and the cockpit reactor command are all gone. Event-driven auto-delegation is no longer part of cockpit; agents are launched explicitly.
  • Aider runtime driver and support. The aider driver, its tests, and all spawn/launch/doctor/template wiring have been removed. Aider was never wired into src/config.ts and saw no active use; cockpit's supported agents are now Claude Code, Codex, Gemini CLI, and opencode. The --agent aider option no longer exists.

[0.3.3] - 2026-05-15

Added

  • opencode CLI agent support. New driver (createOpencodeDriver) probes opencode --version and declares auto_approve / json_output / streaming / model_routing capabilities. cockpit crew spawn ... --agent opencode builds opencode run "<prompt>" (plus --format json and -m <model> when applicable). The matching projection emitter writes to ~/.config/opencode/AGENTS.md at user scope and <root>/AGENTS.md at project scope, sharing the same marker-merge flow as codex. opencode crews run as interactive sub-sessions like claude crews — cockpit crew send delivers follow-up turns to the live TUI. Print-mode is still used for one-shot roles (reactor, exploration).

[0.3.2] - 2026-05-06

Fixed

  • Crew now honors configured model routing. cockpit crew spawn was not passing --model to the agent CLI, so Claude crews silently fell back to the user's global default (typically opus) instead of the configured defaults.roles.crew.model (sonnet by default). Read the model from config and pass it through buildCommand. Token spend for crew sessions drops accordingly.
  • Model passthrough is agent-aware: only applied when the spawn agent matches the role's configured agent (defaults.roles.crew.agent). Cross- agent crews (e.g. --agent codex while config routes crew to claude) skip the model arg, since model names are agent-specific (sonnet is a Claude alias and would be invalid for codex / aider / gemini).

[0.3.1] - 2026-05-06

Crew sessions become interactive sub-sessions instead of one-shot print runs — the captain's equivalent of a Claude Agent Team subagent. Each crew is named, addressable, stays idle between turns, and is driven by new cockpit crew send/read/close/list verbs. Closes #56.

Added

  • Interactive Claude crewscockpit crew spawn boots Claude without -p, then sends the task as the first turn after the CLI is ready. The session stays alive between turns waiting for the captain's next message.
  • Named crews--name <n> (or auto-generated crew-1, crew-2, … picking the next free slot from existing tabs in the captain workspace). Tab title becomes 🔧 <project>:<name> so the surface itself is the registry — no state file.
  • cockpit crew send <project> <name> "<message>" — send a follow-up turn to an existing crew. Replaces the "spawn a new tab for every turn" pattern.
  • cockpit crew read <project> <name> — read the crew's current screen from the CLI (no need to flip into the cmux UI).
  • cockpit crew close <project> <name> — shutdown a crew (closes its tab).
  • cockpit crew list <project> — list live crews for a project.
  • SpawnOptions.interactive flag — Claude driver omits -p when set so callers can deliver the prompt over runtime.send.
  • RuntimeDriver.listSurfaces(workspaceId) — enumerate surfaces (tabs / panes) inside a workspace with their titles. Cmux driver parses cmux tree --workspace.

Changed

  • Captain templates + captain-ops SKILL rewritten — teach the new spawn-once / send-follow-ups / close-when-done pattern. Stops the "tons of tabs" growth seen pre-0.3.1.
  • README + CLI help updated with the new verbs.

Known limitations

  • Non-Claude agents (codex / gemini / aider) still launch in print-mode; full interactive support per agent is a follow-up.
  • Crew tabs do not persist across cockpit shutdown <project> — they're surfaces inside the captain workspace and die with it. Matches Agent Team semantics.

[0.3.0] - 2026-05-05

The thin-redirect release. Cockpit becomes a thin multi-agent orchestration layer where the captain is disposable, crew are fresh CLI sessions in split panes (any agent), Command is on-demand, and an auto-poller derives liveness from cmux pane content so agents don't have to write status.

Umbrella tracking: #40 (closed). Design spec: docs/specs/2026-05-05-cockpit-thin-redirect-design.md.

Added

  • Crew spawn via split-pane CLIcockpit crew spawn <project> <task> [--direction <d>] [--agent claude|codex|gemini|aider] opens a fresh agent CLI in a split pane next to the captain. Replaces Claude-only TeamCreate / Agent tool. Works for any agent (#41, #46).
  • RuntimeDriver pane operationsnewPane, closePane, sendToPane, readPaneScreen so callers reach panes via the existing abstraction (#41).
  • Auto-status poller — reactor reaction polls captain panes via cockpit runtime read-screen, classifies state (idle/busy/blocked/errored/ offline) from the last ~50 lines, writes {spokeVault}/status.md with state + timestamp + last-activity excerpt. Pure machine, no agent action required (#43, #48).
  • Dashboardcockpit dashboard --pane opens a refreshing sidebar grid in cmux; hub Obsidian Dataview page aggregates all spoke status.md files. Both consume the same auto-derived data (#44, #49).
  • cockpit command [--task briefing|learnings-review|wiki-aggregate] — on-demand one-shot Command session in a split pane, instead of an always-on persistent Command workspace (#42, #47).
  • Multi-agent template paritycaptain.generic.md / crew.generic.md projected to ~/.codex/AGENTS.md, ~/.gemini/GEMINI.md, .cursor/rules/cockpit.mdc so non-Claude agents have working captain/crew contracts (#45, #50).

Changed

  • Captain templates and captain-ops skill rewritten — no more TeamCreate / Agent / SendMessage references; crew spawning routes through cockpit crew spawn; mandatory write-status-after-every-event rule removed (the auto-poller covers liveness).
  • captain.claude.md — added one-line compact-recovery doc note. Verified live: role survives /compact via --append-system-prompt-file, so role-amnesia is not a real problem; only work-context loss remains and is covered by handoffs.
  • launch --all — no longer auto-launches a Command session. Bare cockpit launch no longer defaults to Command. Command is opt-in via the new cockpit command subcommand.
  • Vault discipline — handoff / wiki / learnings are now opt-in (captain writes when meaningful), not nagged on every event. Vault becomes a consumer of auto-derived status, not the primary write target.
  • scripts/spawn-crew-pane.sh is now a thin shim that forwards to cockpit crew spawn (preserved for backward compat).

Removed

  • "Captain MUST write status after every significant event" rule
  • "Daily log" requirement (still possible, just opt-in)
  • Auto-launched Command session in --all flow
  • Claude-only TeamCreate / Agent tool dependence in captain workflow

0.2.0 - 2026-05-05

First tagged release. Establishes cockpit as a multi-agent orchestration layer (Command → Captain → Crew + Reactor) with a pluggable slot architecture and GitHub-driven automation.

Added

Multi-agent foundation

  • Driver model for multi-agent support — Codex, Cursor, Gemini CLI, and Aider alongside Claude Code (#16).
  • Multi-agent direction statement and Karpathy coding-discipline skill applied across captain/crew/direct edits (#32, #33).
  • Projection slot V1 — cross-agent config sync so non-Claude agents see the same project context (#31, #36).

Plugin slot system (#9)

  • Phase 1: Runtime slot — abstracts cmux behind a runtime driver (#20).
  • Phase 2: Workspace slot — pluggable workspace provisioning (#26).
  • Phase 3: Tracker slot — pluggable status/progress tracking (#28).
  • Phase 4: Notifier slot — pluggable notification surfaces (#29).

Reactor & automation

  • Reaction engine — declarative GitHub event polling with rule-based actions in a dedicated workspace (#1).
  • CI Feedback Reactor — auto-fix CI failures via crew dispatch (#3).
  • cockpit retro command — weekly/sprint retrospective summaries from daily logs and git history (#6).

Commands & workflows

  • cockpit launch and cockpit shutdown — bootstrap and tear down the Command/Captain/Crew workspace set in cmux.
  • cockpit standup — daily standup summary from captain logs.
  • cockpit feedback — capture user feedback into the project record.
  • Daily briefing on new day; captain writes daily logs.
  • Project groups — sibling repos share context via claude-mem; primary repo auto-detected; --group-role enforced.
  • Auto-discovery of repos under a parent directory with primary/sibling identification.
  • Auto-generated unique captain names with collision validation on projects add.
  • Session continuity — resume last session by default; --fresh flag forces a new session; built on claude -c.
  • Configurable permission modes for command and captain sessions (#21, #22) — defaults to auto.
  • Workspace icons — command, captain, crew — for cmux visual distinction.

Knowledge & integrations

  • LLM Wiki knowledge compilation system — Karpathy-inspired ingest/query/log scripts per spoke vault (#13).
  • GSD integration for crew wave-based execution on multi-step tasks (#14).
  • Model routing config — Opus for command/captain/review, Sonnet for crew/reactor (#12).
  • Task Master integration via session handoff files.
  • Docs scaffolding for research, specs, and ADRs.
  • Project roadmap covering 13 features across P0–P3.

Changed

  • Cockpit roles default to auto permission mode at launch (#21, #22).
  • --append-system-prompt-file used for roles to preserve project CLAUDE.md; templates deployed via cockpit init.
  • Captain writes status on session start and after every task event.
  • Command session restricted to delegation-only tools (Bash/Read/Write); no Grep/Glob/Edit on project source.
  • Switched from manual git worktree to Claude Code's built-in worktrees.

Fixed

  • Command-ops freshness gate — validates captain workspace age before reuse, preventing stale-session bugs (#37, #38).
  • Exact captain-name matching enforced — never reuse similar workspaces.
  • Use absolute cmux path everywhere; auto-launch the cmux app if not running.
  • Detect external-terminal launches and bring up the cmux app.
  • Use workspace:N refs (not names) for cmux select-workspace.
  • Install CLAUDE.md into workspace cwd; navigate to command on launch.
  • Brove project path corrected; warn on projects add when no .git found.
  • Strengthened command CLAUDE.md hard rules against doing work directly.
  • Correct plugin keys, captain naming, and status display.

Documentation

  • README with install, commands, and architecture.
  • Multi-agent direction spec (docs/specs/2026-04-24-multi-agent-direction.md).
  • P0 roadmap items marked complete; out-of-repo work moved out.