Skip to content

Releases: tu11aa/squadrant

v0.19.5

Choose a tag to compare

@github-actions github-actions released this 05 Sep 10:55
551ea0b

[0.19.5] - 2026-09-05

Fixed

  • claude.ts emitted --messaging-socket-path before --plugin-dir on the crew launch command, truncating cmux's stored argv and breaking role classification/restore (#759). cmux <=0.64.18 truncates launchCommand.arguments at --messaging-socket-path when storing it in claude-hook-sessions.json, so anything appended after it — including --plugin-dir — was dropped from the stored command; the flag actually reached the live Claude process fine, but the truncated stored argv is what cmux's role classification/session-restore reads. The flag is now appended at the end of the command, matching the ordering the launch-cmd path (#697) already used.

Added

  • PermissionRequest registered as a hook source + Notification reclassified by notification_type (#760). PermissionRequest fires ~6s before the matching Notification and carries tool_name/tool_input directly — a richer, earlier task.blocked source than sniffing Notification.message. Added to the managed hook set (#615) and the crew's own per-session hook set, via a new formatPermissionRequestQuestion helper that never dumps full tool_input (only a short, truncated file-path/command hint). Notification is now classified by the structured notification_type field (a 14-value enum on current Claude clients) instead of English substring matching; the old substring test is kept only as a fallback for clients that omit the field.
  • Stop hook's prompt_id threaded through as the real per-turn turnId (#761). Stop/PermissionRequest/Notification payloads all carry prompt_id, a UUID that matches across events of the same turn (verified live, claude 2.1.260). mapClaudeHookToEvent now uses it as turnId instead of the constant "hook-stop", falling back to the constant when absent (older clients).
  • Stop now vetoes turn-completion on active background_tasks/session_crons (#762). These are a structural, agent-reported veto on turn-completion, stronger than the #492 pendingTool veto because they also cover Claude's own background tasks and session crons, which pendingTool cannot see. When either is non-empty, Stop maps to task.progress instead of task.turn.completed; the existing trailing-question detection still takes priority.
  • New StopFailure hook source for API-error turn deaths (#763). Previously a crew turn that died on an API error (529/overload/etc) was invisible until the watchdog reported a plain stall — the wrong story. StopFailure is now registered in the managed hook set (#615) and the crew's own per-session hook set, mapped to a new ControlEvent variant task.turn.failed carrying the resolved turnId and a redacted error string. task.turn.failed is deliberately not task.failed (anti-#2576: no hook may terminalize a task).

Together, #760#763 add two new entries to the squadrant-owned hook set installed into ~/.claude/settings.json (PermissionRequest, StopFailure); Notification and Stop were already registered, only their in-code classification changed. Both new entries are written on the next daemon boot via installClaudeHooks, not on npm install.

v0.19.4

Choose a tag to compare

@github-actions github-actions released this 04 Sep 03:26
8841efc

[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.

v0.19.3

Choose a tag to compare

@github-actions github-actions released this 03 Sep 04:00
ab6b3b0

[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.

v0.19.2

Choose a tag to compare

@github-actions github-actions released this 31 Aug 07:04
b3fecc6

[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).

v0.19.1

Choose a tag to compare

@github-actions github-actions released this 28 Aug 16:20
b35cf05

[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).

v0.19.0

Choose a tag to compare

@github-actions github-actions released this 23 Aug 08:07
26cf48b

[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.

v0.18.1

Choose a tag to compare

@github-actions github-actions released this 13 Aug 09:49
fa8aea3

[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.

v0.18.0

Choose a tag to compare

@github-actions github-actions released this 07 Aug 04:42
a72f5ff

[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.

v0.17.1

Choose a tag to compare

@github-actions github-actions released this 05 Aug 16:29
c47c92e

[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.

v0.17.0

Choose a tag to compare

@github-actions github-actions released this 04 Aug 03:28
14a6463

[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.