@mu-crew/mu 3.9.0 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-4PACTXAB.js +17 -0
- package/dist/chunk-4PACTXAB.js.map +1 -0
- package/dist/chunk-77GBZ2KR.js +85 -0
- package/dist/chunk-77GBZ2KR.js.map +1 -0
- package/dist/chunk-7OQNJWBY.js +391 -0
- package/dist/chunk-7OQNJWBY.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-WT7IN2OD.js +9887 -0
- package/dist/chunk-WT7IN2OD.js.map +1 -0
- package/dist/chunk-XXK7FR37.js +640 -0
- package/dist/chunk-XXK7FR37.js.map +1 -0
- package/dist/chunk-YIF3X32A.js +2792 -0
- package/dist/chunk-YIF3X32A.js.map +1 -0
- package/dist/cli-UZEMPIPW.js +6266 -0
- package/dist/cli-UZEMPIPW.js.map +1 -0
- package/dist/cli.js +37 -26127
- package/dist/cli.js.map +1 -1
- package/dist/extension/mu-pi.js +129 -103
- package/dist/extension/mu-pi.js.map +1 -1
- package/dist/index.d.ts +108 -53
- package/dist/index.js +324 -11728
- package/dist/index.js.map +1 -1
- package/dist/tui-X2XICJTN.js +6452 -0
- package/dist/tui-X2XICJTN.js.map +1 -0
- package/dist/tui-sync-worker.js +16 -0
- package/dist/tui-sync-worker.js.map +1 -0
- package/dist/yank-T3AYQTVR.js +12 -0
- package/dist/yank-T3AYQTVR.js.map +1 -0
- package/docs/ARCHITECTURE.md +9 -3
- package/docs/architecture/control-socket.md +5 -1
- package/docs/architecture/sync.md +18 -4
- package/docs/guide/dispatch.md +4 -0
- package/docs/guide/sql.md +1 -1
- package/docs/reference/env.md +2 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agent-state.ts","../src/agents/transport.ts","../src/ctl/client.ts","../src/ctl/protocol.ts","../src/ctl/path.ts","../src/db.ts","../src/capture.ts","../src/tasks/status.ts","../src/logs.ts","../src/hlc.ts","../src/legacy-ops.ts","../src/mux.ts","../src/mux/herdr.ts","../src/mux/tmux.ts","../src/mux/input-timing.ts","../src/mux/types.ts","../src/mux/detect.ts","../src/agents/spawn.ts","../src/agents.ts","../src/glyphs.ts","../src/op-context.ts","../src/reconcile.ts","../src/staleness.ts","../src/workspace/core.ts","../src/workspace/crud.ts","../src/vcs/git.ts","../src/vcs/helpers.ts","../src/vcs/types.ts","../src/vcs/jj.ts","../src/vcs/none.ts","../src/vcs/sl.ts","../src/vcs/index.ts","../src/workspace/decorate.ts","../src/workspace/orphans.ts","../src/tasks/core.ts","../src/tasks/queries.ts","../src/tasks/id.ts","../src/tasks/errors.ts","../src/tasks/edges.ts","../src/workstream.ts","../src/tasks/edit.ts","../src/tasks/lifecycle.ts","../src/tasks/claim.ts","../src/tasks/wait.ts","../src/agents/abort.ts","../src/agents/adopt.ts","../src/agents/kick.ts","../src/output.ts","../src/tmux.ts","../src/agents/spawn-lock.ts","../src/file-lock.ts","../src/agents/errors.ts","../src/apply.ts","../src/dag.ts","../src/doctor-summary.ts","../src/drift.ts","../src/rebuild.ts","../src/fleet-hazards.ts","../src/link.ts","../src/log-render.ts","../src/segments.ts","../src/tracks.ts","../src/state.ts","../src/sync.ts","../src/undo.ts"],"sourcesContent":["import { accessSync, constants, existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { delimiter, join } from \"node:path\";\nimport { execa } from \"execa\";\nimport { expectsCtl } from \"./agents/transport.js\";\nimport { ctlProbe } from \"./ctl/client.js\";\nimport { ctlSocketPath } from \"./ctl/path.js\";\nimport type { CtlState } from \"./ctl/protocol.js\";\nimport { activeMux } from \"./mux/detect.js\";\nimport { tmux } from \"./tmux.js\";\n\nexport type RuntimeState = \"busy\" | \"needs_input\" | \"needs_permission\" | \"unknown\";\nexport type StateSource = \"murmur\" | \"herdr\" | \"ctl\" | \"none\";\n/** How a pi agent's control socket answered; \"n/a\" for non-pi agents. */\nexport type CtlLink = \"ok\" | \"missing\" | \"refused\" | \"n/a\";\n\nexport interface StateReading {\n state: RuntimeState;\n source: StateSource;\n since: number | null;\n alive: boolean;\n reason?: string;\n /** Set for agents that expect a control socket. */\n ctl?: Exclude<CtlLink, \"n/a\">;\n /** ctl only: pi holds queued messages, so an idle reading is about to\n * turn busy. */\n pending?: boolean;\n /** ctl only: pi's settled-run count (`mu agent wait --after-runs`). */\n runs?: number;\n}\n\nexport interface StateAgentRef {\n name: string;\n workstreamName: string;\n paneId: string;\n /** When set and it names pi, the control socket is the state source. */\n cli?: string;\n}\n\n/** Per-agent budget for the control-socket status read. */\nconst CTL_STATE_PROBE_MS = 1000;\n\n/** ctl's idle maps to needs_input, the same as murmur's idle. */\nexport function ctlRuntimeState(state: CtlState): RuntimeState {\n return state === \"busy\" ? \"busy\" : \"needs_input\";\n}\n\nexport const UNKNOWN_REASON = {\n murmurMissing: \"murmur not installed\",\n extensionMissing: \"murmur pi extension not linked\",\n noRow: \"murmur has no row\",\n stale: \"remote snapshot stale\",\n herdrNone: \"herdr reports no state\",\n paneGone: \"pane gone\",\n ctlMissing: \"ctl missing\",\n ctlRefused: \"ctl refused\",\n} as const;\n\nexport type MurmurRunner = () => Promise<string | null>;\n\nconst MURMUR_CACHE_MS = 10_000;\nlet murmurRunnerForTests: MurmurRunner | null = null;\nlet murmurCache: { at: number; stdout: string | null } | undefined;\n\nexport function agentKey(a: { name: string; workstreamName: string }): string {\n return `${a.workstreamName}/${a.name}`;\n}\n\nexport function ambiguousReason(n: number): string {\n return `ambiguous: ${n} hosts`;\n}\n\nfunction murmurOnPath(): boolean {\n for (const dir of (process.env.PATH ?? \"\").split(delimiter)) {\n if (dir.length === 0) continue;\n try {\n accessSync(join(dir, \"murmur\"), constants.X_OK);\n return true;\n } catch {\n // Keep scanning PATH.\n }\n }\n return false;\n}\n\nfunction murmurExtensionLinked(): boolean {\n const piDir = process.env.PI_CODING_AGENT_DIR ?? join(homedir(), \".pi\", \"agent\");\n return existsSync(join(piDir, \"extensions\", \"murmur.ts\"));\n}\n\nexport function murmurAvailable(): boolean {\n return murmurOnPath() && murmurExtensionLinked();\n}\n\nconst defaultMurmurRunner: MurmurRunner = async () => {\n try {\n const result = await execa(\"murmur\", [\"status\", \"--json\"], {\n timeout: 5_000,\n reject: false,\n });\n return result.exitCode === 0 ? (result.stdout ?? \"\") : null;\n } catch {\n return null;\n }\n};\n\nexport function setMurmurRunnerForTests(run: MurmurRunner | null): void {\n murmurRunnerForTests = run;\n}\n\nexport function resetAgentStateCacheForTests(): void {\n murmurCache = undefined;\n}\n\nfunction unknown(reason: string, alive = true, source: StateSource = \"none\"): StateReading {\n return { state: \"unknown\", source, since: null, alive, reason };\n}\n\nfunction mapMurmurState(token: string, since: number | null): StateReading {\n let state: RuntimeState;\n switch (token) {\n case \"working\":\n state = \"busy\";\n break;\n case \"blocked\":\n state = \"needs_permission\";\n break;\n case \"idle\":\n case \"done\":\n case \"error\":\n case \"crashed\":\n state = \"needs_input\";\n break;\n default:\n return unknown(`unrecognised murmur state ${token}`, true, \"murmur\");\n }\n return { state, source: \"murmur\", since, alive: true };\n}\n\nfunction parseSince(value: string | undefined): number | null {\n return value !== undefined && /^\\d+$/.test(value) ? Number(value) : null;\n}\n\nasync function readMurmur(now: number): Promise<string | null> {\n if (murmurCache !== undefined && now - murmurCache.at < MURMUR_CACHE_MS) {\n return murmurCache.stdout;\n }\n let stdout: string | null;\n try {\n stdout = await (murmurRunnerForTests ?? defaultMurmurRunner)();\n } catch {\n stdout = null;\n }\n murmurCache = { at: now, stdout };\n return stdout;\n}\n\ninterface MurmurPane {\n local?: unknown;\n agent_name?: unknown;\n workstream?: unknown;\n activity?: unknown;\n attention?: unknown;\n freshness?: unknown;\n updated_at?: unknown;\n}\n\nfunction remotePanes(stdout: string): MurmurPane[] {\n try {\n const parsed: unknown = JSON.parse(stdout);\n if (typeof parsed !== \"object\" || parsed === null || !(\"panes\" in parsed)) return [];\n const panes = parsed.panes;\n if (!Array.isArray(panes)) return [];\n return panes.filter((pane): pane is MurmurPane => typeof pane === \"object\" && pane !== null);\n } catch {\n return [];\n }\n}\n\nfunction remoteReading(agent: StateAgentRef, panes: readonly MurmurPane[]): StateReading {\n const matches = panes.filter(\n (pane) =>\n pane.local === false &&\n pane.agent_name === agent.name &&\n pane.workstream === agent.workstreamName,\n );\n if (matches.length === 0) {\n return unknown(\n murmurExtensionLinked() ? UNKNOWN_REASON.noRow : UNKNOWN_REASON.extensionMissing,\n );\n }\n if (matches.length > 1) return unknown(ambiguousReason(matches.length));\n const row = matches[0];\n if (row === undefined) return unknown(UNKNOWN_REASON.noRow);\n if (row.freshness !== \"fresh\") return unknown(UNKNOWN_REASON.stale);\n\n const kinds = Array.isArray(row.attention)\n ? new Set(\n row.attention.flatMap((entry) =>\n typeof entry === \"object\" &&\n entry !== null &&\n \"kind\" in entry &&\n typeof entry.kind === \"string\"\n ? [entry.kind]\n : [],\n ),\n )\n : new Set<string>();\n const state: RuntimeState =\n kinds.has(\"crashed\") || kinds.has(\"error\")\n ? \"needs_input\"\n : kinds.has(\"blocked\")\n ? \"needs_permission\"\n : kinds.has(\"done\")\n ? \"needs_input\"\n : row.activity === \"running\"\n ? \"busy\"\n : \"needs_input\";\n return {\n state,\n source: \"murmur\",\n since: typeof row.updated_at === \"number\" ? row.updated_at : null,\n alive: true,\n };\n}\n\nasync function readHerdrStates(\n agents: readonly StateAgentRef[],\n mux: Awaited<ReturnType<typeof activeMux>>,\n): Promise<Map<string, StateReading>> {\n const readings = new Map<string, StateReading>();\n for (const agent of agents) {\n try {\n const status = await mux.paneStatus?.(agent.paneId);\n if (status !== undefined) {\n readings.set(agentKey(agent), {\n state: status,\n source: \"herdr\",\n since: null,\n alive: true,\n });\n continue;\n }\n const alive = await mux.paneExists(agent.paneId);\n readings.set(\n agentKey(agent),\n alive\n ? unknown(UNKNOWN_REASON.herdrNone, true, \"herdr\")\n : unknown(UNKNOWN_REASON.paneGone, false),\n );\n } catch (error) {\n readings.set(\n agentKey(agent),\n unknown(`herdr: ${error instanceof Error ? error.message : String(error)}`),\n );\n }\n }\n return readings;\n}\n\n/**\n * Read the runtime state of each agent. Order per agent: control socket\n * (pi agents) → murmur pane option → murmur remote → herdr. A pi agent\n * whose socket does not answer reads `unknown` with reason `ctl missing`\n * or `ctl refused`, never a murmur reading, so the gap stays visible.\n * `stateDir` roots the derived socket path (spawn uses the DB's directory).\n */\nexport async function readAgentStates(\n agents: readonly StateAgentRef[],\n opts: { now?: number; stateDir?: string } = {},\n): Promise<Map<string, StateReading>> {\n const ctlAgents = agents.filter(\n (a) =>\n a.cli !== undefined &&\n expectsCtl(\n { cli: a.cli, name: a.name, workstreamName: a.workstreamName },\n ctlSocketPath(a.workstreamName, a.name, opts.stateDir),\n ),\n );\n const probes = await Promise.all(\n ctlAgents.map((a) =>\n ctlProbe(ctlSocketPath(a.workstreamName, a.name, opts.stateDir), CTL_STATE_PROBE_MS),\n ),\n );\n const readings = new Map<string, StateReading>();\n const failed = new Map<string, \"missing\" | \"refused\">();\n ctlAgents.forEach((agent, i) => {\n const probe = probes[i];\n if (probe?.kind === \"ok\") {\n readings.set(agentKey(agent), {\n state: ctlRuntimeState(probe.status.state),\n source: \"ctl\",\n since: probe.status.since,\n alive: true,\n ctl: \"ok\",\n pending: probe.status.pending,\n runs: probe.status.runs,\n });\n } else {\n failed.set(agentKey(agent), probe?.kind === \"missing\" ? \"missing\" : \"refused\");\n }\n });\n const rest = agents.filter((a) => !readings.has(agentKey(a)));\n if (rest.length === 0) return readings;\n for (const [key, reading] of await readBaseStates(rest, opts)) {\n const ctl = failed.get(key);\n if (ctl === undefined) readings.set(key, reading);\n else if (!reading.alive) readings.set(key, { ...reading, ctl });\n else {\n const reason = ctl === \"missing\" ? UNKNOWN_REASON.ctlMissing : UNKNOWN_REASON.ctlRefused;\n readings.set(key, { ...unknown(reason), ctl });\n }\n }\n return readings;\n}\n\nasync function readBaseStates(\n agents: readonly StateAgentRef[],\n opts: { now?: number },\n): Promise<Map<string, StateReading>> {\n const readings = new Map<string, StateReading>();\n let mux: Awaited<ReturnType<typeof activeMux>>;\n try {\n mux = await activeMux();\n } catch (error) {\n const reason = `mux: ${error instanceof Error ? error.message : String(error)}`;\n for (const agent of agents) readings.set(agentKey(agent), unknown(reason));\n return readings;\n }\n if (mux.paneStatus !== undefined) return await readHerdrStates(agents, mux);\n\n let stdout: string;\n try {\n stdout = await tmux([\n \"list-panes\",\n \"-a\",\n \"-F\",\n \"#{pane_id}\\t#{@murmur_pane_state}\\t#{@murmur_pane_since}\",\n ]);\n } catch (error) {\n const reason = `tmux: ${error instanceof Error ? error.message : String(error)}`;\n for (const agent of agents) readings.set(agentKey(agent), unknown(reason));\n return readings;\n }\n\n const local = new Map<string, { token: string; since: number | null }>();\n for (const line of stdout.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [paneId, token = \"\", since] = line.split(\"\\t\");\n if (paneId !== undefined) local.set(paneId, { token, since: parseSince(since) });\n }\n\n const remoteCandidates: StateAgentRef[] = [];\n for (const agent of agents) {\n const pane = local.get(agent.paneId);\n if (pane === undefined) {\n readings.set(agentKey(agent), unknown(UNKNOWN_REASON.paneGone, false));\n } else if (pane.token.length > 0) {\n readings.set(agentKey(agent), mapMurmurState(pane.token, pane.since));\n } else {\n remoteCandidates.push(agent);\n }\n }\n\n if (remoteCandidates.length === 0) return readings;\n const remoteStdout = await readMurmur(opts.now ?? Date.now());\n if (remoteStdout === null) {\n const reason = murmurAvailable()\n ? UNKNOWN_REASON.noRow\n : murmurOnPath()\n ? UNKNOWN_REASON.extensionMissing\n : UNKNOWN_REASON.murmurMissing;\n for (const agent of remoteCandidates) readings.set(agentKey(agent), unknown(reason));\n return readings;\n }\n\n const panes = remotePanes(remoteStdout);\n for (const agent of remoteCandidates) readings.set(agentKey(agent), remoteReading(agent, panes));\n return readings;\n}\n","// mu — send transport: how `mu agent send` reaches an agent.\n//\n// A pi agent (speaksMuCtl) is reached through its control socket, served\n// by the mu pi extension inside the pane's own pi. `/new`, `/reload` and\n// `/compact` become the ctl `command` op; any other slash command is\n// refused (pi.sendUserMessage would hand it to the model as text). Only\n// non-pi agents and an explicit `--via mux` reach the mux paste path. No\n// silent fallback: a pi agent whose socket does not answer is an\n// AgentCtlUnreachableError, never a paste.\n\nimport { existsSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { AgentRow } from \"../agents.js\";\nimport { CtlUnknownOpError, CtlVersionError, ctlRequest, errCode } from \"../ctl/client.js\";\nimport { ctlSocketPath } from \"../ctl/path.js\";\nimport {\n CTL_COMMANDS,\n type CtlCommandName,\n type CtlReply,\n type CtlRequest,\n type CtlState,\n} from \"../ctl/protocol.js\";\nimport type { Db } from \"../db.js\";\nimport { emitEvent } from \"../logs.js\";\nimport { activeMux, type SendOptions } from \"../mux.js\";\nimport {\n AgentBusyError,\n AgentCtlUnreachableError,\n AgentExtensionOutdatedError,\n AgentFreshNeedsCtlError,\n AgentSlashCommandUnsupportedError,\n} from \"./errors.js\";\nimport { resolveCliCommand, speaksMuCtl } from \"./spawn.js\";\n\nexport type Transport = \"ctl\" | \"mux\";\n\nexport type SendResult = {\n transport: Transport;\n state?: CtlState;\n /**\n * ctl only: pi's completed-run count. For a plain send it is measured\n * before dispatch (the `mu agent wait --after-runs` baseline); for\n * `--fresh` it is the new session's count once its run started.\n * Absent from an extension that predates it.\n */\n runs?: number;\n /** ctl only: when pi entered `state` (epoch ms), from the same status. */\n since?: number;\n /** Set when the text ran as a pi session command over ctl. */\n command?: CtlCommandName;\n};\n\nexport type TransportSendOptions = SendOptions & {\n /** How a busy pi queues the message. Default followUp. ctl only. */\n mode?: \"steer\" | \"followUp\";\n /** Force a transport instead of choosing by agent and text. */\n via?: Transport;\n /** Control socket path. Default: agentCtlSocket(db, agent). */\n socket?: string;\n /** Start a new pi session and send the text into it, as one ctl op. */\n fresh?: boolean;\n /** With fresh or a session command: abandon a running turn instead of refusing. */\n force?: boolean;\n};\n\n/** The agent's derived control socket, rooted at the DB's directory (as spawn does). */\nexport function agentCtlSocket(db: Db, agent: AgentRow): string {\n return ctlSocketPath(agent.workstreamName, agent.name, dirname(db.name));\n}\n\n/**\n * True when the agent runs pi, so sends go through its control socket.\n * The cli key decides when it names pi (or MU_<KEY>_COMMAND here runs\n * pi). Otherwise the agent's own socket file decides: spawn removes any\n * stale file before the pane starts, so a file at the derived path was\n * bound by this agent's extension (or its ssh forward). That covers\n * `--cli helper --command \"pi-meta ...\"`, whose command is not stored,\n * without a schema change. `sock` defaults to the derived path; pass\n * agentCtlSocket(db, agent) when a DB is at hand.\n */\nexport function expectsCtl(\n agent: Pick<AgentRow, \"cli\" | \"name\" | \"workstreamName\">,\n sock: string = ctlSocketPath(agent.workstreamName, agent.name),\n): boolean {\n return speaksMuCtl(agent.cli, resolveCliCommand(agent.cli)) || existsSync(sock);\n}\n\n/** The transport a send to `agent` uses when not forced. */\nexport function chooseTransport(agent: AgentRow, sock?: string): Transport {\n return expectsCtl(agent, sock) ? \"ctl\" : \"mux\";\n}\n\n/** The slash commands a pi agent runs over ctl, as typed. */\nexport const CTL_SLASH_COMMANDS = CTL_COMMANDS.map((n) => `/${n}`);\n\n/**\n * `text` as a pi session command: `/new`, `/reload`, or `/compact` with\n * optional instructions. undefined for anything else (a plain prompt, or\n * another slash command: see isSlashCommand).\n */\nexport function parseSessionCommand(\n text: string,\n): { name: CtlCommandName; instructions?: string } | undefined {\n const t = text.trim();\n if (t === \"/new\") return { name: \"new\" };\n if (t === \"/reload\") return { name: \"reload\" };\n const m = /^\\/compact(?:\\s+([\\s\\S]*))?$/.exec(t);\n if (!m) return undefined;\n const instructions = m[1]?.trim();\n return instructions ? { name: \"compact\", instructions } : { name: \"compact\" };\n}\n\n/** True when `text` reads as a TUI slash command (`/word ...`), not a path or prose. */\nexport function isSlashCommand(text: string): boolean {\n return /^\\/[A-Za-z][\\w:-]*(?:\\s|$)/.test(text.trimStart());\n}\n\n/**\n * Map a CtlUnknownOpError to AgentExtensionOutdatedError, asking the\n * socket's `hello` (best effort) for the extension's build version.\n * Checked after the refusal rather than before every op: an unknown op\n * is a no-op in the extension, so the happy path pays no extra trip.\n */\nexport async function extensionOutdated(\n agent: Pick<AgentRow, \"name\" | \"workstreamName\">,\n sock: string,\n e: CtlUnknownOpError,\n): Promise<AgentExtensionOutdatedError> {\n let extVersion: string | undefined;\n try {\n const hello = await ctlRequest(sock, { op: \"hello\" });\n if (hello.ok) extVersion = hello.extVersion;\n } catch {\n // The refusal already proved the socket answers; the version is a nicety.\n }\n return new AgentExtensionOutdatedError(agent.name, agent.workstreamName, e.op, extVersion, e.ops);\n}\n\n/**\n * One ctl request to `agent`, with the failure mapping every ctl verb\n * shares: a version mismatch rethrows, an unknown op becomes\n * AgentExtensionOutdatedError, anything else AgentCtlUnreachableError\n * (missing on ENOENT, else refused).\n */\nexport async function ctlRequestFor(\n agent: Pick<AgentRow, \"name\" | \"workstreamName\">,\n sock: string,\n req: CtlRequest,\n): Promise<CtlReply> {\n try {\n return await ctlRequest(sock, req);\n } catch (e) {\n throw await ctlFailure(agent, sock, e);\n }\n}\n\n/** The error ctlRequestFor raises for a failed ctl request (see there). */\nexport async function ctlFailure(\n agent: Pick<AgentRow, \"name\" | \"workstreamName\">,\n sock: string,\n e: unknown,\n): Promise<unknown> {\n if (e instanceof CtlVersionError) return e;\n if (e instanceof CtlUnknownOpError) return extensionOutdated(agent, sock, e);\n return new AgentCtlUnreachableError(\n agent.name,\n agent.workstreamName,\n sock,\n errCode(e) === \"ENOENT\" ? \"missing\" : \"refused\",\n );\n}\n\n/** How `mu agent send` delivered: the `mode=` of an `agent.send` op. */\nexport type SendMode = \"plain\" | \"steer\" | \"interrupt\" | \"fresh\";\n\n/**\n * Record a delivered send as an `agent.send` op. `agents` is machine-local\n * and a send mutates no table, so this emit is the only record that a\n * running worker was told something, and when. `fields` carries pi's\n * pre-send state (`since` as ISO) so an audit can see a worker stuck in\n * one long turn when a plain/steer send queued behind it.\n */\nexport function recordSend(\n db: Db,\n agent: Pick<AgentRow, \"name\" | \"workstreamName\">,\n mode: SendMode,\n bytes: number,\n fields: Record<string, string | number | boolean | undefined>,\n): void {\n const detail = Object.entries({ mode, ...fields })\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${k}=${k === \"since\" ? new Date(Number(v)).toISOString() : v}`)\n .join(\" \");\n emitEvent(\n db,\n agent.workstreamName,\n \"agent.send\",\n `agent send ${agent.name} (${detail}, ${bytes} bytes)`,\n );\n}\n\nexport async function sendViaTransport(\n agent: AgentRow,\n text: string,\n // Required: only the caller knows the DB whose directory roots the socket.\n opts: TransportSendOptions & { socket: string },\n): Promise<SendResult> {\n const sock = opts.socket;\n if (opts.fresh && (opts.via === \"mux\" || !expectsCtl(agent, sock))) {\n throw new AgentFreshNeedsCtlError(agent.name, agent.workstreamName, agent.cli);\n }\n const transport = opts.fresh ? \"ctl\" : (opts.via ?? chooseTransport(agent, sock));\n if (transport === \"mux\") {\n // The paste path: non-pi CLIs, adopted panes, and an explicit --via mux.\n // Load-bearing: a send that cannot reach a pane is a failed send.\n await (await activeMux()).sendToPane(agent.paneId, text, opts);\n return { transport };\n }\n if (!opts.fresh) {\n const cmd = parseSessionCommand(text);\n if (cmd) return runSessionCommand(agent, sock, cmd, opts.force === true);\n if (isSlashCommand(text)) {\n throw new AgentSlashCommandUnsupportedError(\n agent.name,\n agent.workstreamName,\n text,\n CTL_SLASH_COMMANDS,\n );\n }\n }\n const reply = await ctlRequestFor(\n agent,\n sock,\n opts.fresh\n ? { op: \"fresh\", text, ...(opts.force ? { force: true } : {}) }\n : { op: \"send\", text, mode: opts.mode ?? \"followUp\" },\n );\n if (!reply.ok) {\n if (opts.fresh && reply.error === \"busy\") {\n throw new AgentBusyError(agent.name, agent.workstreamName);\n }\n throw new Error(`control socket refused the send: ${reply.error}`);\n }\n return {\n transport,\n ...(reply.state !== undefined ? { state: reply.state } : {}),\n ...(reply.runs !== undefined ? { runs: reply.runs } : {}),\n ...(reply.since !== undefined ? { since: reply.since } : {}),\n };\n}\n\n/** `/new`, `/reload` or `/compact` inside pi, through the ctl `command` op. */\nasync function runSessionCommand(\n agent: AgentRow,\n sock: string,\n cmd: { name: CtlCommandName; instructions?: string },\n force: boolean,\n): Promise<SendResult> {\n const reply = await ctlRequestFor(agent, sock, {\n op: \"command\",\n name: cmd.name,\n ...(cmd.instructions !== undefined ? { instructions: cmd.instructions } : {}),\n ...(force ? { force: true } : {}),\n });\n if (!reply.ok) {\n if (reply.error === \"busy\") {\n throw new AgentBusyError(agent.name, agent.workstreamName, `/${cmd.name}`);\n }\n throw new Error(`pi refused /${cmd.name}: ${reply.error}`);\n }\n return {\n transport: \"ctl\",\n command: cmd.name,\n ...(reply.state !== undefined ? { state: reply.state } : {}),\n };\n}\n","/**\n * Client for the per-agent control socket served by the mu pi extension.\n * One request per connection: connect, write one line, read one line, end.\n */\nimport { createConnection } from \"node:net\";\nimport {\n CTL_PROTOCOL_VERSION,\n type CtlReply,\n type CtlRequest,\n type CtlStatus,\n encode,\n LineDecoder,\n UNKNOWN_OP_PREFIX,\n} from \"./protocol.js\";\n\n/** Default client timeout for non-`wait` requests. */\nexport const CTL_DEFAULT_TIMEOUT_MS = 5000;\n/** Client timeout for `fresh` and `command`: the new session must start its run, or pi reload. */\nexport const CTL_FRESH_TIMEOUT_MS = 60_000;\n/** Slack added to a `wait` request's own timeoutMs for the client timeout. */\nexport const CTL_WAIT_SLACK_MS = 5000;\n\nexport type CtlProbe =\n | {\n kind: \"ok\";\n status: CtlStatus;\n /** From hello: the ops the extension serves. Absent: it predates reporting them. */\n ops?: string[];\n /** From hello: the mu version the extension was built from. */\n extVersion?: string;\n }\n | { kind: \"missing\" }\n | { kind: \"refused\"; error: string }\n | { kind: \"version\"; got: unknown };\n\nexport class CtlVersionError extends Error {\n constructor(readonly got: unknown) {\n super(`control socket speaks protocol v${String(got)}; mu expects v${CTL_PROTOCOL_VERSION}`);\n this.name = \"CtlVersionError\";\n }\n}\n\nexport class CtlTimeoutError extends Error {\n constructor(readonly timeoutMs: number) {\n super(`control socket did not reply within ${timeoutMs}ms`);\n this.name = \"CtlTimeoutError\";\n }\n}\n\n/**\n * The extension answered \"unknown op\": it was loaded before mu learned\n * `op`. `ops` lists what it serves (absent from extensions that predate\n * reporting them).\n */\nexport class CtlUnknownOpError extends Error {\n constructor(\n readonly op: string,\n readonly ops?: readonly string[],\n ) {\n super(`control socket does not serve op ${op}`);\n this.name = \"CtlUnknownOpError\";\n }\n}\n\nfunction clientTimeout(req: CtlRequest, override?: number): number | undefined {\n if (override !== undefined) return override;\n if (req.op === \"fresh\" || req.op === \"command\") return CTL_FRESH_TIMEOUT_MS;\n if (req.op !== \"wait\" && req.op !== \"interrupt\") return CTL_DEFAULT_TIMEOUT_MS;\n // An unbounded wait (or interrupt) is held until pi settles.\n return req.timeoutMs === undefined ? undefined : req.timeoutMs + CTL_WAIT_SLACK_MS;\n}\n\nfunction parseReply(line: string): CtlReply {\n const msg: unknown = JSON.parse(line);\n const v = typeof msg === \"object\" && msg !== null ? (msg as { v?: unknown }).v : undefined;\n if (v !== CTL_PROTOCOL_VERSION) throw new CtlVersionError(v);\n return msg as CtlReply;\n}\n\n/**\n * Send one request and resolve with its reply. Rejects with the socket\n * error (its `code` intact), CtlTimeoutError, CtlVersionError, or\n * CtlUnknownOpError (an ok:false \"unknown op\" reply).\n * Aborting `signal` closes the connection and rejects.\n */\nexport function ctlRequest(\n sock: string,\n req: CtlRequest,\n opts?: { timeoutMs?: number; signal?: AbortSignal },\n): Promise<CtlReply> {\n const timeoutMs = clientTimeout(req, opts?.timeoutMs);\n return new Promise((resolve, reject) => {\n const conn = createConnection(sock);\n const dec = new LineDecoder();\n let done = false;\n const finish = (err: Error | null, reply?: CtlReply): void => {\n if (done) return;\n done = true;\n if (timer) clearTimeout(timer);\n conn.destroy();\n if (err) reject(err);\n else if (reply) resolve(reply);\n };\n const timer =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => finish(new CtlTimeoutError(timeoutMs)), timeoutMs);\n const signal = opts?.signal;\n if (signal?.aborted) finish(new Error(\"control socket request aborted\"));\n signal?.addEventListener(\"abort\", () => finish(new Error(\"control socket request aborted\")), {\n once: true,\n });\n conn.setEncoding(\"utf8\");\n conn.on(\"connect\", () => conn.write(encode(req)));\n conn.on(\"data\", (chunk: string) => {\n const line = dec.push(chunk)[0];\n if (line === undefined) return;\n try {\n const reply = parseReply(line);\n if (!reply.ok && reply.error.startsWith(UNKNOWN_OP_PREFIX)) {\n finish(new CtlUnknownOpError(req.op, reply.ops));\n } else finish(null, reply);\n } catch (e) {\n finish(e instanceof Error ? e : new Error(String(e)));\n }\n });\n conn.on(\"error\", (e) => finish(e));\n conn.on(\"close\", () => finish(new Error(\"control socket closed without a reply\")));\n });\n}\n\n/** A Node system error's `code` (ENOENT, ECONNREFUSED, ...), if any. */\nexport function errCode(e: unknown): string | undefined {\n const code = typeof e === \"object\" && e !== null ? (e as { code?: unknown }).code : undefined;\n return typeof code === \"string\" ? code : undefined;\n}\n\n/**\n * hello + status. ENOENT → missing; a version mismatch → version; any\n * other failure (ECONNREFUSED, ENOTSOCK, timeout, bad reply) → refused.\n */\nexport async function ctlProbe(\n sock: string,\n timeoutMs = CTL_DEFAULT_TIMEOUT_MS,\n): Promise<CtlProbe> {\n try {\n const hello = await ctlRequest(sock, { op: \"hello\" }, { timeoutMs });\n if (!hello.ok) return { kind: \"refused\", error: hello.error };\n const st = await ctlRequest(sock, { op: \"status\" }, { timeoutMs });\n if (!st.ok) return { kind: \"refused\", error: st.error };\n const { state, since, runs, pending } = st;\n if (state === undefined || since === undefined || runs === undefined || pending === undefined) {\n return { kind: \"refused\", error: \"status reply is missing fields\" };\n }\n return {\n kind: \"ok\",\n status: { state, since, runs, pending },\n ...(hello.ops !== undefined ? { ops: hello.ops } : {}),\n ...(hello.extVersion !== undefined ? { extVersion: hello.extVersion } : {}),\n };\n } catch (e) {\n if (e instanceof CtlVersionError) return { kind: \"version\", got: e.got };\n if (errCode(e) === \"ENOENT\") return { kind: \"missing\" };\n return { kind: \"refused\", error: e instanceof Error ? e.message : String(e) };\n }\n}\n","/**\n * mu control socket protocol: JSON lines over a unix socket, one request\n * per connection. Shared by mu's client and the mu pi extension, so this\n * file imports nothing from mu — the extension loads it standalone.\n */\n\nexport const CTL_PROTOCOL_VERSION = 1;\n\nexport type CtlState = \"busy\" | \"idle\";\n\nexport type CtlRequest =\n | { op: \"hello\" }\n | { op: \"status\" }\n | { op: \"send\"; text: string; mode?: \"steer\" | \"followUp\" }\n | { op: \"wait\"; afterRuns?: number; timeoutMs?: number }\n | { op: \"abort\" }\n /**\n * Make a busy pi act on `text` now: abort the running turn, wait for\n * agent_settled (up to timeoutMs), then send `text` as a new run. Idle:\n * just send. Replies `timeout` and sends nothing when pi does not settle.\n */\n | { op: \"interrupt\"; text: string; timeoutMs?: number }\n /** New session + prompt as one operation. Refused with error \"busy\" unless force. */\n | { op: \"fresh\"; text: string; force?: boolean }\n /**\n * Run one of pi's session commands inside pi (sendUserMessage cannot run\n * built-in slash commands). Refused with error \"busy\" unless force.\n */\n | { op: \"command\"; name: CtlCommandName; instructions?: string; force?: boolean };\n\n/** Session commands the `command` op runs: `/new`, `/reload`, `/compact`. */\nexport const CTL_COMMANDS = [\"new\", \"reload\", \"compact\"] as const;\nexport type CtlCommandName = (typeof CTL_COMMANDS)[number];\n\n/**\n * Every op this protocol defines. The extension reports the ops it serves\n * in its `hello` reply (and in an unknown-op error), so mu can tell an\n * extension loaded by an older pi process from a broken one.\n */\nexport const CTL_OPS = [\n \"hello\",\n \"status\",\n \"send\",\n \"wait\",\n \"abort\",\n \"interrupt\",\n \"fresh\",\n \"command\",\n] as const satisfies readonly CtlRequest[\"op\"][];\n\nexport type CtlStatus = { state: CtlState; since: number; runs: number; pending: boolean };\n\nexport type CtlReply =\n | ({ v: 1; ok: true } & Partial<CtlStatus> & {\n agent?: string;\n workstream?: string;\n piVersion?: string;\n /** hello only: the ops this extension serves. Absent: predates reporting. */\n ops?: string[];\n /** hello only: the mu package version the extension was built from. */\n extVersion?: string;\n /** wait only: text of the settled run's final assistant message (\"\" when none). */\n lastText?: string;\n /** wait only: the error that ended the settled run (retries exhausted). */\n lastError?: string;\n /** interrupt only: pi was busy, so the turn was aborted before the send. */\n wasBusy?: boolean;\n })\n | { v: 1; ok: false; error: string; ops?: string[] };\n\n/** Error text of a reply to an op the extension does not serve. */\nexport const UNKNOWN_OP_PREFIX = \"unknown op: \";\n\n/** One frame. JSON.stringify escapes \"\\n\" inside strings, so the frame is one line. */\nexport function encode(msg: object): string {\n return `${JSON.stringify(msg)}\\n`;\n}\n\n/** Splits a stream into lines on \"\\n\" only (never U+2028/U+2029). */\nexport class LineDecoder {\n private buf = \"\";\n\n push(chunk: string): string[] {\n this.buf += chunk;\n const parts = this.buf.split(\"\\n\");\n this.buf = parts.pop() ?? \"\";\n return parts;\n }\n}\n","/**\n * Per-agent control socket path. Derived from (state dir, workstream,\n * agent) on every call; never stored. The mu pi extension serves the\n * socket at `$MU_CTL_SOCK`, and mu connects to the same derived path.\n */\nimport { createHash } from \"node:crypto\";\nimport { rmSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { type Db, defaultStateDir } from \"../db.js\";\n\n/** Env var spawn injects so the pi extension knows where to listen. */\nexport const CTL_SOCK_ENV = \"MU_CTL_SOCK\";\n\n/** macOS sun_path is 104 bytes including the trailing NUL. */\nexport const MAX_SOCK_PATH = 103;\n\n/** `<dir>/<ws>/<agent>.sock`, or `<dir>/h/<hash>.sock` past MAX_SOCK_PATH. */\nfunction sockPathUnder(dir: string, workstream: string, agent: string): string {\n const readable = join(dir, workstream, `${agent}.sock`);\n if (Buffer.byteLength(readable) <= MAX_SOCK_PATH) return readable;\n const h = createHash(\"sha1\").update(`${workstream}/${agent}`).digest(\"hex\").slice(0, 16);\n return join(dir, \"h\", `${h}.sock`);\n}\n\nexport function ctlSocketPath(workstream: string, agent: string, stateDir?: string): string {\n const dbPath = process.env.MU_DB_PATH;\n const base = stateDir ?? (dbPath ? dirname(dbPath) : defaultStateDir());\n return sockPathUnder(join(base, \"sock\"), workstream, agent);\n}\n\n/**\n * Remove an agent's local control socket file. For a remote agent ssh's\n * `-L` forward leaves that file behind when the connection dies; it\n * probes as `refused`, never `ok`, but it is litter. A local pi removes\n * its own on quit. Best-effort: callers have already deleted the row.\n */\nexport function unlinkCtlSocket(db: Db, name: string, workstream: string): void {\n if (db.memory) return;\n try {\n rmSync(ctlSocketPath(workstream, name, dirname(db.name)), { force: true });\n } catch {\n /* best-effort */\n }\n}\n\n/**\n * Default socket path ON THE REMOTE HOST for a remote pi agent:\n * `/tmp/mu-<uid>/<ws>/<agent>.sock`, shortened by the same rule. `uid`\n * is the local one (mu cannot know the remote's); the extension creates\n * the directory, and `mu agent remote-env --remote-sock` overrides it.\n */\nexport function remoteCtlSocketPath(workstream: string, agent: string, uid: number): string {\n return sockPathUnder(`/tmp/mu-${uid}`, workstream, agent);\n}\n","// mu — DB module.\n//\n// Opens <state-dir>/mu.db (or the MU_DB_PATH override; see defaultDbPath),\n// enables WAL + foreign keys, applies the schema idempotently, and\n// exposes the live Database handle.\n//\n// Schema (v11 — task substates on the v10 three-state lifecycle):\n// - 6 entity tables: workstreams, agents, tasks, task_edges,\n// task_notes, vcs_workspaces\n// - 1 ops log: ops (the single append-only record of\n// every change — VISION.md § 2b)\n// - 1 sync table: sync_peers (per-peer watermarks)\n// - 2 meta tables: schema_version, machine_identity\n// - 1 lookup table: task_substates (legal (status, substate) pairs,\n// seeded from code on every open)\n// - 3 views: ready, blocked, goals\n// => EXPECTED_TABLES is exactly 11 entries.\n//\n// v11 adds tasks.substate, which qualifies status (OPEN/parked,\n// CLOSED/wontfix, ...). A composite FK (status, substate) ->\n// task_substates, DEFERRABLE INITIALLY DEFERRED, is the only guard on\n// the pair: it is checked at COMMIT, so apply's one-field-at-a-time\n// UPDATEs pass as long as the pair is valid when the transaction ends.\n//\n// v9 is a BREAKING, migration-free redesign. It DROPS v8's four\n// separate change-recording mechanisms — `agent_logs`, `snapshots`,\n// `workstream_sync`, and the five `archived_*` tables — and replaces\n// all of them with one **ops log**. Sync, undo, and history\n// are queries or replays over that log (docs/VOCABULARY.md § op,\n// ops log, watermark).\n//\n// The surrogate-INTEGER-PK discipline introduced in v5 is unchanged:\n// per docs/architecture/sdk.md § Surrogate-PK and SDK-boundary discipline,\n// every entity table has an INTEGER PK; FKs reference INTEGER ids;\n// the operator-facing TEXT name is per-scope unique via\n// UNIQUE (<scope_id>, <name>). `ops` is the deliberate exception: it\n// holds the NATURAL key (`ws/local_id`) in `ops.key`, never a\n// surrogate id, which is exactly why keys don't collide across\n// machines.\n//\n// IMPORTANT: MIN_ACCEPTED_SCHEMA_VERSION === CURRENT_SCHEMA_VERSION\n// === 11. There is no in-place forward-bump ladder: every pre-v11 DB\n// is rejected at openDb time with SchemaTooOldError (exit 4), and a DB\n// newer than this build with SchemaTooNewError (exit 4). Migration\n// lives only in scripts/migrate.ts.\n\nimport { randomUUID } from \"node:crypto\";\nimport { mkdirSync } from \"node:fs\";\nimport { homedir, hostname } from \"node:os\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport Database, { type Database as DatabaseType } from \"better-sqlite3\";\nimport { installCapture } from \"./capture.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\nimport { TASK_SUBSTATE_ROWS } from \"./tasks/status.js\";\n\nexport type Db = DatabaseType;\n\nexport interface OpenDbOptions {\n /**\n * Absolute path to the SQLite file. Defaults to MU_DB_PATH env var or\n * the XDG state path (see `defaultDbPath`). Use a per-test temp path\n * in tests.\n */\n path?: string;\n\n /**\n * If true, opens the DB read-only. Used by `mu sql` and similar read-only\n * surfaces to enforce no-mutation guarantees at the connection level.\n */\n readonly?: boolean;\n}\n\n/**\n * Resolve the canonical mu state directory:\n * MU_STATE_DIR > $XDG_STATE_HOME/mu (absolute only) > ~/.local/state/mu\n */\nexport function defaultStateDir(): string {\n if (process.env.MU_STATE_DIR) return process.env.MU_STATE_DIR;\n return join(xdgStateHome(homedir()), \"mu\");\n}\n\n/** `$XDG_STATE_HOME`, or `<home>/.local/state` when it is unset, empty,\n * or relative: the XDG spec says such a value must be ignored, and a\n * relative one would scatter state across every cwd mu runs in. */\nfunction xdgStateHome(home: string): string {\n const xdg = process.env.XDG_STATE_HOME;\n return xdg !== undefined && isAbsolute(xdg) ? xdg : join(home, \".local\", \"state\");\n}\n\n/**\n * Resolve the canonical DB path:\n * MU_DB_PATH > <state-dir>/mu.db\n */\nexport function defaultDbPath(): string {\n if (process.env.MU_DB_PATH) return process.env.MU_DB_PATH;\n return join(defaultStateDir(), \"mu.db\");\n}\n\n/**\n * Open the mu database. Creates the parent directory and applies the schema\n * idempotently on every open. Safe to call from many short-lived processes\n * concurrently — WAL mode handles cross-process writes.\n */\nexport function openDb(options: OpenDbOptions = {}): Db {\n const path = options.path ?? defaultDbPath();\n refuseUserDbDuringTests(path);\n mkdirSync(dirname(path), { recursive: true });\n\n const db = new Database(path, { readonly: options.readonly ?? false });\n\n if (!options.readonly) {\n // Wait up to 5s for a competing writer's lock instead of throwing\n // SQLITE_BUSY immediately. Every `mu` invocation is a separate\n // short-lived process and a parallel fan-out (`for n in …; do mu\n // agent spawn … & done`) opens the same DB from N processes at\n // once — without this, the losers of a write-lock race die with\n // 'database is locked' and roll back their agent. WAL handles\n // concurrent readers; busy_timeout handles concurrent writers.\n // busy_timeout only covers a lock taken at BEGIN, so every write\n // transaction runs as `db.transaction(fn).immediate()` (or `BEGIN\n // IMMEDIATE`). A deferred one that reads first and then writes gets\n // SQLITE_BUSY_SNAPSHOT at once if another process committed in\n // between, and busy_timeout does not retry that.\n db.pragma(\"busy_timeout = 5000\");\n // Detect schema version BEFORE applySchema so a real v<11 DB is not\n // silently stamped as v11 by the CREATE-IF-NOT-EXISTS in applySchema,\n // and before `journal_mode = WAL`, which rewrites the file header:\n // a refused DB must be left byte-for-byte untouched.\n const detectedVersion = detectExistingSchemaVersion(db);\n if (detectedVersion !== null && detectedVersion > CURRENT_SCHEMA_VERSION) {\n // A newer mu wrote this DB. Its writes would fail here in\n // confusing ways (unknown columns, constraints this build does not\n // know), so refuse before applySchema touches anything.\n try {\n db.close();\n } catch {\n // best effort\n }\n throw new SchemaTooNewError(detectedVersion, CURRENT_SCHEMA_VERSION);\n }\n if (detectedVersion !== null && detectedVersion < MIN_ACCEPTED_SCHEMA_VERSION) {\n // Loud-fail: refuse to touch a pre-v11 DB. There is no in-place\n // migration to run, so leave the old file untouched and tell the\n // operator how to preserve it (see SchemaTooOldError.errorNextSteps).\n try {\n db.close();\n } catch {\n // best effort\n }\n throw new SchemaTooOldError(detectedVersion, MIN_ACCEPTED_SCHEMA_VERSION);\n }\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"foreign_keys = ON\");\n applySchema(db);\n seedMachineIdentity(db);\n // Install the op-capture triggers + the _op_ctx temp tables they\n // read. Per-connection, because SQLite forbids a main-schema\n // trigger from referencing the temp schema, so the triggers must\n // themselves be TEMP triggers (see src/capture.ts for the full\n // reasoning and the exact error messages). Must run AFTER\n // seedMachineIdentity: the triggers mint HLCs from that row.\n installCapture(db);\n } else {\n db.pragma(\"foreign_keys = ON\");\n }\n\n return db;\n}\n\n/**\n * Hard guard (Layer \"db\" of bug_test_flake_round_2): refuse to open\n * the user's REAL default mu.db when running under vitest. Tests\n * MUST point at a per-test temp DB (via MU_DB_PATH or the explicit\n * `{ path }` option). A test that forgets to override either one\n * silently mutated the dev box's live state — we observed a stray\n * 'demo' workstream row replicated from test/tui-acceptance.integration.test.ts\n * into ~/.local/state/mu/mu.db. The guard throws a useful diagnostic\n * the moment the offending openDb() call is made; the failing test's\n * stack trace then names the leak source directly.\n *\n * Test mode = `process.env.VITEST` is defined OR `NODE_ENV === \"test\"`.\n * vitest sets VITEST=\"true\" in every fork; the NODE_ENV branch is for\n * other runners that may invoke openDb during tests.\n *\n * The user's REAL DB path is computed from HOME / XDG_STATE_HOME\n * directly (NOT from defaultDbPath() — which would honour MU_DB_PATH\n * and produce the temp path the test set, defeating the check).\n * MU_STATE_DIR is deliberately not consulted: test/_setup.ts deletes\n * the user's MU_* vars in every fork, so any MU_STATE_DIR seen here was\n * set by a test to a temp dir (test/disk-recon.test.ts opens\n * <MU_STATE_DIR>/mu.db), and guarding it would refuse that temp DB.\n * Production code paths (the `mu` CLI binary) never set VITEST, so\n * the guard is a complete no-op outside the test runner.\n */\nfunction refuseUserDbDuringTests(path: string): void {\n const inTest = process.env.VITEST !== undefined || process.env.NODE_ENV === \"test\";\n if (!inTest) return;\n const home = process.env.HOME ?? homedir();\n const realDb = resolve(join(xdgStateHome(home), \"mu\", \"mu.db\"));\n if (resolve(path) === realDb) {\n throw new Error(\n `openDb refused: tests must NEVER write to the user DB (${realDb}). Set MU_DB_PATH to a per-test temp path (test/_runCli.ts does this automatically) or pass an explicit { path } argument. The leak source is the call site of openDb in this stack frame.`,\n );\n }\n}\n\n// ─── Resolve helpers (operator-facing name -> surrogate id) ───────────\n//\n// docs/architecture/sdk.md § Surrogate-PK and SDK-boundary discipline:\n//\n// PUBLIC SDK functions take operator-facing names (workstream + local\n// id + agent name). Internal helpers take surrogate ids. Resolution\n// happens at the public-function entry, exactly once.\n//\n// `resolveWorkstreamId` is the only resolve helper that throws a typed\n// error from this leaf module — `WorkstreamNotFoundError` is defined\n// here, so there is no cycle. The task / agent resolvers RETURN\n// `number | null` (`tryResolveTaskId` / `tryResolveAgentId`) and let\n// SDK callers in `src/tasks/*.ts` / `src/agents.ts` throw the typed\n// `TaskNotFoundError` / `AgentNotFoundError` they own. That keeps\n// `cli/handle.ts`'s `instanceof`-based exit-code map (3 = not-found)\n// honest: a leaf throwing a plain `Error` whose `.name` was monkey-\n// patched to `\"TaskNotFoundError\"` flunks `instanceof TaskNotFoundError`\n// and falls through to the generic exit 1\n// (review_substrate_resolve_id_anonymous_errors).\n\nexport class WorkstreamNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"WorkstreamNotFoundError\";\n constructor(public readonly workstream: string) {\n super(`no such workstream: ${workstream}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List workstreams\", command: \"mu workstream list\" },\n {\n intent: \"Initialise this workstream\",\n command: `mu workstream init ${this.workstream}`,\n },\n ];\n }\n}\n\n/** Resolve a workstream name to its INTEGER surrogate id. Throws\n * WorkstreamNotFoundError on miss. Pure: no auto-create — callers\n * that want the auto-create-or-resolve semantics use\n * `ensureWorkstream` from src/workstream.ts (which returns void;\n * follow up with `resolveWorkstreamId` if the id is needed).\n */\nexport function resolveWorkstreamId(db: Db, workstream: string): number {\n const row = db.prepare(\"SELECT id FROM workstreams WHERE name = ?\").get(workstream) as\n | { id: number }\n | undefined;\n if (!row) throw new WorkstreamNotFoundError(workstream);\n return row.id;\n}\n\n/** Resolve a workstream name to its id, returning null on miss instead\n * of throwing. Useful for read paths that want to early-return [] on\n * a non-existent workstream (e.g. listTasks). */\nexport function tryResolveWorkstreamId(db: Db, workstream: string): number | null {\n const row = db.prepare(\"SELECT id FROM workstreams WHERE name = ?\").get(workstream) as\n | { id: number }\n | undefined;\n return row ? row.id : null;\n}\n\n/** Resolve a (workstream_id, local_id) pair to the task's surrogate\n * id, returning `null` on miss. SDK callers in `src/tasks/*.ts`\n * wrap the null return in `TaskNotFoundError` so the CLI's typed-\n * error → exit-code map (3 = not-found) fires. The leaf intentionally\n * does NOT throw a typed error (it would either pull in a cyclic\n * import or — as before — fake the `.name` and silently flunk\n * `instanceof TaskNotFoundError`, falling through to exit 1).\n * Renamed from `resolveTaskId` in\n * review_substrate_resolve_id_anonymous_errors. */\nexport function tryResolveTaskId(db: Db, workstreamId: number, localId: string): number | null {\n const row = db\n .prepare(\"SELECT id FROM tasks WHERE workstream_id = ? AND local_id = ?\")\n .get(workstreamId, localId) as { id: number } | undefined;\n return row ? row.id : null;\n}\n\n/** Resolve a (workstream_id, agent_name) pair to the agent's surrogate\n * id, returning `null` on miss. SDK callers in `src/agents.ts` wrap\n * the null return in `AgentNotFoundError` so the CLI's typed-error →\n * exit-code map (3 = not-found) fires. See `tryResolveTaskId` for the\n * full rationale (review_substrate_resolve_id_anonymous_errors). */\nexport function tryResolveAgentId(db: Db, workstreamId: number, name: string): number | null {\n const row = db\n .prepare(\"SELECT id FROM agents WHERE workstream_id = ? AND name = ?\")\n .get(workstreamId, name) as { id: number } | undefined;\n return row ? row.id : null;\n}\n\n/**\n * Thrown by openDb when the on-disk DB is older than the current\n * schema. There is no in-place migration ladder; the old DB is left\n * untouched. Maps to exit code 4 (conflict) in cli.ts handle().\n */\nexport class SchemaTooOldError extends Error implements HasNextSteps {\n override readonly name = \"SchemaTooOldError\";\n constructor(\n public readonly detectedVersion: number,\n public readonly requiredVersion: number,\n ) {\n super(\n `Detected v${detectedVersion} schema; v${requiredVersion} is required. mu ships NO in-place migration — your v${detectedVersion} DB is untouched. Back it up or move it aside, then start a fresh v${requiredVersion} DB.`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Keep a copy of the old DB before anything else\",\n command: `mu db backup \"$HOME/mu-v${this.detectedVersion}-backup.db\"`,\n },\n {\n intent: \"Move the old DB aside without deleting it\",\n command: `mv \"\\${MU_DB_PATH:-$HOME/.local/state/mu/mu.db}\" \"\\${MU_DB_PATH:-$HOME/.local/state/mu/mu.db}.old\"`,\n },\n {\n intent: `Migrate the old DB into a fresh v${this.requiredVersion} DB (run in a mu git checkout: the npm package does not ship scripts/)`,\n command: `npx tsx scripts/migrate.ts \"\\${MU_DB_PATH:-$HOME/.local/state/mu/mu.db}.old\" --out /tmp/mu-v${this.requiredVersion}.db`,\n },\n {\n intent: \"Verify the fresh DB before swapping\",\n command: `MU_DB_PATH=/tmp/mu-v${this.requiredVersion}.db mu doctor --deep`,\n },\n ];\n }\n}\n\n/**\n * Thrown by openDb when the on-disk DB was written by a NEWER mu.\n * The DB is left untouched. Maps to exit code 4 (conflict).\n */\nexport class SchemaTooNewError extends Error implements HasNextSteps {\n override readonly name = \"SchemaTooNewError\";\n constructor(\n public readonly detectedVersion: number,\n public readonly supportedVersion: number,\n ) {\n super(`DB is v${detectedVersion}; this mu understands up to v${supportedVersion}. Upgrade mu.`);\n }\n errorNextSteps(): NextStep[] {\n return [{ intent: \"Check which mu is on PATH\", command: \"which mu && mu --version\" }];\n }\n}\n\n/**\n * Sniff an existing DB's schema version BEFORE applySchema runs, so we\n * can distinguish:\n * - Brand-new DB: no tables at all -> returns null (fresh, will be\n * stamped to CURRENT_SCHEMA_VERSION by applySchema).\n * - Pre-versioning DB (had the original tables before schema_version existed):\n * workstreams exists, schema_version doesn't -> returns 1.\n * - Already-versioned DB: schema_version row present -> returns its\n * value.\n */\nfunction detectExistingSchemaVersion(db: Db): number | null {\n const hasVersionTable = db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'\")\n .get() as { name: string } | undefined;\n if (hasVersionTable) {\n const row = db.prepare(\"SELECT version FROM schema_version WHERE id = 1\").get() as\n | { version: number }\n | undefined;\n return row?.version ?? null;\n }\n // No schema_version table. Check whether any of the original\n // tables exist; if so this is a pre-versioning DB.\n const hasWorkstreams = db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='workstreams'\")\n .get() as { name: string } | undefined;\n if (hasWorkstreams) return 1;\n return null;\n}\n\n/** Seed the singleton machine_identity row (id=1) on first open.\n * No-op once a row exists. The machine_id it writes is the identity\n * every op is stamped with (`ops.machine_id`) and the name of this\n * machine's segment. */\nfunction seedMachineIdentity(db: Db): void {\n const row = db.prepare(\"SELECT COUNT(*) AS count FROM machine_identity\").get() as {\n count: number;\n };\n if (row.count !== 0) return;\n db.prepare(\n `INSERT OR IGNORE INTO machine_identity (id, machine_id, hostname, created_at)\n VALUES (1, ?, ?, ?)`,\n ).run(randomUUID(), hostname(), new Date().toISOString());\n}\n\n/**\n * Apply the schema. Idempotent: tables use CREATE TABLE IF NOT EXISTS;\n * views are dropped and recreated so the latest definition always wins.\n *\n * For fresh DBs this writes the current schema shape and stamps\n * schema_version = CURRENT_SCHEMA_VERSION. For existing v11 DBs this is\n * a no-op for the table CREATEs (IF NOT EXISTS) but DOES recreate the\n * views. Pre-v11 DBs never reach this function — openDb's loud-fail\n * hook rejects them with SchemaTooOldError first.\n *\n * There is no in-place bump ladder. MIN_ACCEPTED === CURRENT, so the\n * only two shapes that reach here are \"brand new\" and \"already v11\";\n * any older-DB fix-up code would be dead by construction.\n */\nfunction applySchema(db: Db): void {\n // Apply the schema DDL atomically. CURRENT_SCHEMA includes\n // `DROP VIEW IF EXISTS goals; CREATE VIEW goals …` (views can't be\n // CREATE-IF-NOT-EXISTS + redefined in one step), so without a\n // transaction two processes opening the same fresh DB concurrently\n // can interleave one's DROP between the other's DROP and CREATE and\n // hit 'view goals already exists'. An IMMEDIATE transaction takes the\n // write lock up front (paired with busy_timeout in openDb) so the\n // whole DDL block is all-or-nothing per process.\n // bug_parallel_spawn_races_drop_agents (schema leg).\n db.exec(\"BEGIN IMMEDIATE\");\n try {\n db.exec(CURRENT_SCHEMA);\n // Seed the legal (status, substate) pairs from code. INSERT OR\n // IGNORE keeps it idempotent; the table is machine-local and never\n // synced, so every machine derives it from the same constant.\n const seed = db.prepare(\n \"INSERT OR IGNORE INTO task_substates (status, substate, is_default) VALUES (?, ?, ?)\",\n );\n for (const [status, substate, isDefault] of TASK_SUBSTATE_ROWS) {\n seed.run(status, substate, isDefault);\n }\n db.exec(\"COMMIT\");\n } catch (err) {\n db.exec(\"ROLLBACK\");\n // A concurrent process already applied the (idempotent) schema —\n // the views/tables now exist. Re-throwing would fail the spawn for\n // a benign race, so swallow the 'already exists' class and proceed;\n // any other error is a real problem and rethrows.\n const msg = err instanceof Error ? err.message : String(err);\n if (!/already exists/i.test(msg)) throw err;\n }\n // Stamp the version on a fresh DB. INSERT OR IGNORE so we don't\n // overwrite the version on an existing v11 DB.\n db.prepare(\"INSERT OR IGNORE INTO schema_version (id, version) VALUES (1, ?)\").run(\n CURRENT_SCHEMA_VERSION,\n );\n}\n\n/** The schema version a fresh DB starts at. v11 adds tasks.substate\n * and the task_substates lookup table on top of v10's three-state\n * lifecycle (OPEN, IN_PROGRESS, CLOSED). See CHANGELOG.md. */\nexport const CURRENT_SCHEMA_VERSION = 11;\n\n/** The lowest schema version `openDb` will accept. Equal to\n * CURRENT_SCHEMA_VERSION: openDb runs no migration, so every pre-v11 DB\n * throws `SchemaTooOldError` (exit 4) and is left untouched on disk. */\nconst MIN_ACCEPTED_SCHEMA_VERSION = 11;\n\n/** Tables a healthy DB must contain. Single source of truth so\n * `mu doctor` and any other consumer don't drift. Adding a new table\n * = one new entry here AND a CREATE TABLE in CURRENT_SCHEMA, plus a\n * CURRENT_SCHEMA_VERSION bump. Sorted; exactly 11 entries in v11. */\nexport const EXPECTED_TABLES: readonly string[] = [\n \"agents\",\n \"machine_identity\",\n \"ops\",\n \"schema_version\",\n \"sync_peers\",\n \"task_edges\",\n \"task_notes\",\n \"task_substates\",\n \"tasks\",\n \"vcs_workspaces\",\n \"workstreams\",\n];\n\n// ─── Syncability — which state crosses machines ───────────────────────\n//\n// Whether an op syncs is a STATIC function of its entity, so it lives\n// here as a constant rather than as a `local` column on `ops`. A\n// per-row decision is a per-row opportunity to get it wrong, and it\n// would cost a column plus a branch at every write site. Downstream\n// sync code READS these; it does not re-decide.\n//\n// Vocabulary (docs/VOCABULARY.md § portable): **portable** tables are\n// syncable across machines; **machine-local** tables never leave the\n// box. Machine-local ops are still RECORDED — `mu log` and the TUI\n// Recent card still show agent spawn/close — they simply never ship.\n// \"Not synced\" is not \"not logged\".\n\n/** Op entities that cross machines. Everything else is machine-local.\n * Readonly tuple (not `string[]`) so `SyncedEntity` is a real union\n * and downstream code gets compile-time checking, not raw strings. */\nexport const SYNCED_ENTITIES = [\"workstream\", \"task\", \"edge\", \"note\", \"message\"] as const;\n\n/** One of the op entities that sync. Derived from the tuple, so\n * adding an entity is a one-line change with no type to keep in step. */\nexport type SyncedEntity = (typeof SYNCED_ENTITIES)[number];\n\n/** Op entities this build KNOWS are machine-local: their payloads name\n * a pane id or an absolute path, so a peer sending one is a real bug\n * and `applyOp` rejects it loudly.\n *\n * WHY THIS LIST EXISTS SEPARATELY FROM \"not in SYNCED_ENTITIES\"\n * ------------------------------------------------------------\n * \"Unknown\" and \"known-local\" are different failures and were\n * conflated, which wedged a real fleet. A peer running a LATER (or\n * earlier) mu wrote `entity:\"marker\"` ops — legal on the writer, whose\n * SYNCED_ENTITIES included it. The reader treated every non-synced\n * entity as a bad peer, so `ingestSegment` recorded a defect and\n * stopped at that line FOREVER: 87% of a 20,305-line segment never\n * applied, and `mu sync --repair` (which only resets the watermark)\n * marched straight back into the same wall.\n *\n * So the rule is asymmetric on purpose: an entity we know must never\n * travel is a defect; an entity we simply do not recognise is tolerated\n * forward-compatibly — recorded in `ops`, projected nowhere, exactly\n * like 'message'. Reader vocabulary may lag writer vocabulary; that is\n * a fact of a mixed fleet, not a corruption. */\nexport const MACHINE_LOCAL_ENTITIES = [\"agent\", \"workspace\", \"event\", \"broadcast\"] as const;\n\nexport type MachineLocalEntity = (typeof MACHINE_LOCAL_ENTITIES)[number];\n\n/** **Portable** tables: their rows mean the same thing on any machine,\n * so their ops ship. Mirrors docs/VOCABULARY.md § portable exactly. */\nexport const PORTABLE_TABLES = [\"task_edges\", \"task_notes\", \"tasks\", \"workstreams\"] as const;\n\nexport type PortableTable = (typeof PORTABLE_TABLES)[number];\n\n/** **Machine-local** tables. Never bulk-copied to a peer, because a\n * row's meaning does not survive the trip:\n *\n * agents holds `pane_id` ('%17') — meaningless elsewhere.\n * vcs_workspaces holds absolute paths — /home/... vs /Users/...\n * on a mixed macOS/Linux fleet.\n * machine_identity IS the per-machine identity.\n * schema_version local bookkeeping.\n * sync_peers local bookkeeping (per-peer watermarks).\n * task_substates seeded identically on every machine from\n * TASK_SUBSTATE_ROWS; code, not data.\n * ops see below — the carrier, not cargo.\n *\n * `ops` is listed here deliberately rather than omitted. It is not\n * **portable** in the copy-the-table sense: the table is never\n * wholesale-copied. Individual op ROWS ship, one at a time, filtered\n * by SYNCED_ENTITIES and carried by per-machine **segments** — and\n * `seq` is a local-only append cursor that means nothing on a peer.\n * So for the only question this list answers — \"is this table's\n * content copied across machines?\" — the answer for `ops` is no.\n *\n * Consequence that falls out with no special case: `tasks.owner_id`\n * is an FK into `agents`, and `agents` is machine-local. Therefore\n * OWNERSHIP DOES NOT SYNC. The deleted db-sync.ts reached the same\n * conclusion via an `includeOwners` flag; here it is structural. */\nexport const MACHINE_LOCAL_TABLES = [\n \"agents\",\n \"machine_identity\",\n \"ops\",\n \"schema_version\",\n \"sync_peers\",\n \"task_substates\",\n \"vcs_workspaces\",\n] as const;\n\nexport type MachineLocalTable = (typeof MACHINE_LOCAL_TABLES)[number];\n\n// ─── View DDL — single source of truth ────────────────────────────────\n//\n// The three views (ready, blocked, goals) get DROPped + CREATEd by\n// applySchema on every openDb. Each constant is self-contained:\n// DROP IF EXISTS + CREATE. Running DROP twice in a row is harmless,\n// so callers that already DROP up-front can still re-execute these\n// without churn.\n//\n// Exported as named constants so consumers can reference the canonical\n// shape (e.g. one-shot migration scripts under scripts/) without\n// duplicating SQL.\n\nexport const READY_VIEW_SQL = `\nDROP VIEW IF EXISTS ready;\nCREATE VIEW ready AS\n SELECT t.*\n FROM tasks t\n WHERE t.status = 'OPEN'\n AND t.substate NOT IN ('parked', 'triage')\n AND NOT EXISTS (\n SELECT 1\n FROM task_edges e\n JOIN tasks b ON e.from_task_id = b.id\n WHERE e.to_task_id = t.id\n AND b.status <> 'CLOSED'\n );\n`;\n\nexport const BLOCKED_VIEW_SQL = `\nDROP VIEW IF EXISTS blocked;\nCREATE VIEW blocked AS\n SELECT t.*\n FROM tasks t\n WHERE t.status = 'OPEN'\n AND EXISTS (\n SELECT 1\n FROM task_edges e\n JOIN tasks b ON e.from_task_id = b.id\n WHERE e.to_task_id = t.id\n AND b.status <> 'CLOSED'\n );\n`;\n\n// A goal is an active endpoint of the DAG — a task with no dependents\n// that we're still working toward. CLOSED is excluded: a finished\n// leaf is not an active goal. Parked tasks STAY goals: tracks are\n// built from goals, and a parked track is still a track.\nexport const GOALS_VIEW_SQL = `\nDROP VIEW IF EXISTS goals;\nCREATE VIEW goals AS\n SELECT t.*\n FROM tasks t\n WHERE t.status <> 'CLOSED'\n AND NOT EXISTS (\n SELECT 1 FROM task_edges WHERE from_task_id = t.id\n );\n`;\n\n// ─── v11 SCHEMA ───────────────────────────────────────────────────────\n//\n// Per docs/architecture/sdk.md § Surrogate-PK and SDK-boundary discipline.\n// Every entity table has:\n// - INTEGER PRIMARY KEY AUTOINCREMENT (surrogate identity)\n// - <scope>_id INTEGER NOT NULL REFERENCES <parent>(id) ON DELETE CASCADE\n// - <name> TEXT NOT NULL (operator-facing, mutable)\n// - UNIQUE (<scope>_id, <name>)\n//\n// Foreign keys are INTEGER. Renames become single-row UPDATEs (no\n// cascade chain). The TEXT name is just an attribute. `ops` is the\n// documented exception: it has NO foreign keys at all and addresses\n// rows by their NATURAL key, because ops outlive the rows (and the\n// workstreams) they record.\n\nconst CURRENT_SCHEMA = `\n-- ─── Schema versioning ────────────────────────────────────────────────\n--\n-- Single-row table tracking which schema version this DB is at. Migrations\n-- read and update this; the row is INSERT-OR-IGNOREd by applySchema with\n-- the current version on a fresh DB.\nCREATE TABLE IF NOT EXISTS schema_version (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n version INTEGER NOT NULL\n);\n\n-- machine_identity: one durable identity per DB/machine, seeded by\n-- openDb after schema creation. hostname is advisory only.\n--\n-- last_wall / last_counter are the persisted hybrid logical clock\n-- (src/hlc.ts, VOCABULARY § HLC). They live here because every mu\n-- invocation is a fresh process — an in-memory counter would reset\n-- constantly and mint duplicate HLCs, which UNIQUE (machine_id, hlc)\n-- on the ops table would then reject.\nCREATE TABLE IF NOT EXISTS machine_identity (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n machine_id TEXT NOT NULL,\n hostname TEXT,\n created_at TEXT NOT NULL,\n last_wall INTEGER NOT NULL DEFAULT 0,\n last_counter INTEGER NOT NULL DEFAULT 0\n);\n\n-- ─── Tables ───────────────────────────────────────────────────────────\n\n-- workstreams: top of the hierarchy. name stays globally unique\n-- because it IS a tmux session name; no <scope_id> column because\n-- there's no parent.\nCREATE TABLE IF NOT EXISTS workstreams (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT UNIQUE NOT NULL,\n created_at TEXT NOT NULL -- ISO 8601\n);\n\n\n-- agents: one row per managed pane. Per-workstream unique on name.\nCREATE TABLE IF NOT EXISTS agents (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n workstream_id INTEGER NOT NULL REFERENCES workstreams (id) ON DELETE CASCADE,\n name TEXT NOT NULL, -- per-workstream unique\n cli TEXT NOT NULL DEFAULT 'pi',\n pane_id TEXT NOT NULL,\n status TEXT NOT NULL, -- DEPRECATED (2.0): always 'spawning'; runtime state lives in murmur. Drop at the first v11 migration.\n role TEXT NOT NULL DEFAULT 'full-access',\n tab TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE (workstream_id, name),\n CHECK (status IN (\n 'spawning', 'busy', 'needs_input', 'needs_permission',\n 'free', 'unreachable', 'terminated'\n )),\n CHECK (role IN ('full-access', 'read-only'))\n);\n\nCREATE INDEX IF NOT EXISTS idx_agents_workstream ON agents (workstream_id);\nCREATE INDEX IF NOT EXISTS idx_agents_status ON agents (status);\n\n-- task_substates: the legal (status, substate) pairs. Seeded from\n-- TASK_SUBSTATE_ROWS (src/tasks/status.ts) by applySchema; exactly one\n-- default per status.\nCREATE TABLE IF NOT EXISTS task_substates (\n status TEXT NOT NULL,\n substate TEXT NOT NULL,\n is_default INTEGER NOT NULL DEFAULT 0 CHECK (is_default IN (0, 1)),\n PRIMARY KEY (status, substate)\n);\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_task_substates_one_default\n ON task_substates (status) WHERE is_default = 1;\n\n-- tasks: per-workstream unique on local_id (TRULY local now —\n-- different workstreams may reuse the same local_id).\nCREATE TABLE IF NOT EXISTS tasks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n workstream_id INTEGER NOT NULL REFERENCES workstreams (id) ON DELETE CASCADE,\n local_id TEXT NOT NULL, -- per-workstream unique\n title TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'OPEN',\n -- OPEN | IN_PROGRESS | CLOSED — see VOCABULARY.md.\n substate TEXT NOT NULL,\n -- Qualifies status. No DEFAULT on purpose: a writer that forgets it\n -- must fail loudly rather than silently pick one.\n impact INTEGER NOT NULL,\n effort_days REAL NOT NULL,\n owner_id INTEGER REFERENCES agents (id) ON DELETE SET NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE (workstream_id, local_id),\n CHECK (impact BETWEEN 1 AND 100),\n CHECK (effort_days > 0),\n FOREIGN KEY (status, substate) REFERENCES task_substates (status, substate)\n DEFERRABLE INITIALLY DEFERRED\n);\n\nCREATE INDEX IF NOT EXISTS idx_tasks_workstream ON tasks (workstream_id);\nCREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks (status);\nCREATE INDEX IF NOT EXISTS idx_tasks_owner ON tasks (owner_id);\n\n-- task_edges: composite PK by pair. INTEGER FKs into tasks.id.\nCREATE TABLE IF NOT EXISTS task_edges (\n from_task_id INTEGER NOT NULL REFERENCES tasks (id) ON DELETE CASCADE,\n to_task_id INTEGER NOT NULL REFERENCES tasks (id) ON DELETE CASCADE,\n created_at TEXT NOT NULL,\n PRIMARY KEY (from_task_id, to_task_id),\n CHECK (from_task_id <> to_task_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_task_edges_to ON task_edges (to_task_id);\n\n-- task_notes: append-only context. author stays free-text\n-- (\"orchestrator\", \"user\", \"π - mu\", \"system\") — not always a\n-- registered agent.\nCREATE TABLE IF NOT EXISTS task_notes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n task_id INTEGER NOT NULL REFERENCES tasks (id) ON DELETE CASCADE,\n author TEXT,\n content TEXT NOT NULL,\n created_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_task_notes_task ON task_notes (task_id);\n\n-- ─── The ops log (VISION.md § 2b, VOCABULARY.md § op / ops log) ──────\n--\n-- The single append-only record of every change. Sync, undo, and\n-- history are all queries or replays over this one table.\n--\n-- Deliberately FK-free. An op must stay readable after the row (and\n-- the workstream) it records is gone — a tombstone op for a destroyed\n-- workstream is the whole point — so 'key' holds the NATURAL key\n-- ('<workstream>/<local_id>'), never a surrogate id. That is also why\n-- keys don't collide across machines.\n--\n-- seq local-only append cursor (AUTOINCREMENT never recycles)\n-- hlc the ORDERING key across machines (VOCABULARY § HLC)\n-- machine_id which peer wrote this op\n-- group_id the undo unit: all ops of one operator action\n-- actor who caused it (may not be a registered worker)\n-- intent semantic label ('task.close', 'agent.spawn')\n-- entity/key what it addresses ('task', 'mu/ops-log')\n-- op 'put' (semantic partial update) | 'del' (tombstone)\n-- payload JSON of ONLY the columns that changed\n--\n-- UNIQUE (machine_id, hlc) is load-bearing: it makes ingest idempotent\n-- for free, which is what lets 'mu sync --repair' be nothing more than\n-- \"re-read that peer's segment from zero\".\nCREATE TABLE IF NOT EXISTS ops (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n hlc TEXT NOT NULL,\n machine_id TEXT NOT NULL,\n group_id TEXT NOT NULL,\n actor TEXT,\n intent TEXT,\n entity TEXT NOT NULL,\n key TEXT NOT NULL,\n op TEXT NOT NULL CHECK (op IN ('put','del')),\n payload TEXT NOT NULL,\n created_at TEXT NOT NULL,\n UNIQUE (machine_id, hlc)\n);\n\n-- Three indexes, one per read shape that actually exists:\n-- hlc — replay/rebuild walks the log in HLC order.\n-- entity,key — \"what happened to this task\" (history + per-field merge).\n-- group_id — 'mu undo <group>' gathers one action's ops.\n-- (machine_id lookups ride the UNIQUE (machine_id, hlc) index; seq is\n-- the PK. No speculative index beyond these.)\nCREATE INDEX IF NOT EXISTS idx_ops_hlc ON ops (hlc);\nCREATE INDEX IF NOT EXISTS idx_ops_entity_key ON ops (entity, key);\nCREATE INDEX IF NOT EXISTS idx_ops_group ON ops (group_id);\n\n-- sync_peers: one row per known peer, holding its watermark — how far\n-- into that peer's segment we have applied. One integer suffices\n-- because segments are append-only and ordered. Rows are created on\n-- demand at first ingest; there is no membership list to configure.\n-- A Syncthing conflict copy gets its own row, keyed by its file stem.\nCREATE TABLE IF NOT EXISTS sync_peers (\n machine_id TEXT PRIMARY KEY,\n last_applied_seq INTEGER NOT NULL DEFAULT 0,\n last_seen_at TEXT\n);\n\n-- vcs_workspaces: one isolated working copy per agent.\n-- UNIQUE (agent_id) enforces the 1:1 invariant; workstream_id is\n-- denormalised for query convenience. path is UNIQUE because two\n-- agents pointing at the same on-disk workspace would defeat the\n-- purpose.\nCREATE TABLE IF NOT EXISTS vcs_workspaces (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n agent_id INTEGER NOT NULL UNIQUE REFERENCES agents (id) ON DELETE CASCADE,\n workstream_id INTEGER NOT NULL REFERENCES workstreams (id) ON DELETE CASCADE,\n backend TEXT NOT NULL CHECK (backend IN ('jj', 'sl', 'git', 'none')),\n path TEXT NOT NULL UNIQUE,\n parent_ref TEXT,\n created_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_vcs_workspaces_workstream ON vcs_workspaces (workstream_id);\n\n-- ─── Views (always replaced so the latest definition wins) ────────────\n-- See READY_VIEW_SQL / BLOCKED_VIEW_SQL / GOALS_VIEW_SQL above for the\n-- canonical DDL — interpolated here so applySchema is one db.exec().\n${READY_VIEW_SQL}\n${BLOCKED_VIEW_SQL}\n${GOALS_VIEW_SQL}\n`;\n","// mu — op capture: SQLite triggers that record every change to a\n// portable table as an op, inside the SAME TRANSACTION as the mutation.\n//\n// WHY TRIGGERS AND NOT HAND-EMITTED OPS\n// -------------------------------------\n// Capture fires inside the same transaction as the row change, in the\n// same file. So capture is ATOMIC with the change: the two cannot\n// drift, not even on power loss mid-write. Either both landed or\n// neither did.\n//\n// Hand-emitted ops (an `emitOp(...)` next to each mutation) were\n// rejected because they can be FORGOTTEN. A forgotten op is no\n// longer \"sync missed something\" — undo, sync and history are\n// ALL projections of this one log, so one missing op is silent\n// corruption of all four at once. A future SDK function that mutates\n// `tasks` must not be ABLE to skip capture. Triggers make that\n// structural rather than a convention someone has to remember.\n//\n// WHY THE TRIGGERS ARE TEMP TRIGGERS (the load-bearing surprise)\n// -------------------------------------------------------------\n// The triggers need per-invocation context (intent / actor / group /\n// the echo guard) which lives in the `_op_ctx` TEMP table. SQLite\n// REFUSES to let a main-schema trigger reference the temp schema:\n//\n// sqlite> CREATE TRIGGER x AFTER INSERT ON tasks\n// > WHEN (SELECT applying FROM _op_ctx) = 0 ...\n// Error: no such table: main._op_ctx\n// -- and qualifying it explicitly fails differently:\n// Error: trigger x cannot reference objects in database temp\n//\n// So the triggers themselves must live in the TEMP schema, where the\n// temp table IS visible. Verified empirically (see the probe results in\n// this task's note). Consequences, all of them fine:\n//\n// * Triggers are (re)created per connection, by `installCapture`,\n// which `openDb` calls on every open. There is no persistent\n// trigger in the DB file, so no stale-DDL-in-file problem and no\n// schema-version churn when the trigger set changes.\n// * A connection that never runs `installCapture` captures nothing.\n// That is only reachable inside this codebase (readonly opens), not\n// by a normal `mu` invocation.\n// * A TEMP trigger cannot write to a qualified `main.ops` —\n// \"qualified table names are not allowed on INSERT, UPDATE, and\n// DELETE statements within triggers\" — so the INSERTs here are\n// UNQUALIFIED and resolve to `main` because no temp table shadows\n// those names. `_op_ctx` / `_op_clock` / `_op_dying` are prefixed\n// to make that shadowing impossible by accident.\n//\n// SEMANTIC PARTIAL UPDATES — ONLY CHANGED COLUMNS\n// -----------------------------------------------\n// The UPDATE triggers compare `NEW.<col> IS NOT OLD.<col>` per column\n// and build the payload from just the columns that actually changed, so\n// `task.close` carries `{\"status\":\"CLOSED\"}` and nothing else. This is\n// the single detail that makes per-field merge FREE: \"apply in HLC\n// order\" then converges without column version vectors.\n//\n// Dumping the whole row instead would look identical in every\n// single-machine test and silently regress the design from field-level\n// to row-level last-writer-wins. mu runs autonomous agent crews, so a\n// devserver crew closing a task while the operator edits that task's\n// impact on a laptop is concurrent multi-machine writing BY\n// CONSTRUCTION. Field-level merge keeps both edits; row-level LWW\n// discards one at random. `IS NOT` (not `<>`) because `<>` is NULL for\n// a NULL operand, which would drop every set-to-NULL and\n// NULL-to-a-value transition — e.g. releasing a claim\n// (`owner_id -> NULL`) would never be captured.\n\n// TYPE-ONLY import: db.ts imports this module, so a value import would\n// be a runtime cycle (and an import cycle here shows up as `node\n// dist/cli.js` printing nothing at all). `typeof PORTABLE_TABLES` is a\n// type position, so this is erased at compile time.\nimport type { PORTABLE_TABLES } from \"./db.js\";\n\n/** Sentinel key segment for a natural key whose parent row could not be\n * resolved. Should be unreachable — the dying-ancestor stash below\n * exists precisely so cascades can still resolve — but a capture that\n * records a degraded key is strictly better than one that records\n * nothing, so this FAILS SAFE rather than failing silent. */\nconst UNRESOLVED = \"<unresolved>\";\n\n// ─── The op context temp tables ───────────────────────────────────────\n\n/**\n * DDL for the three per-connection temp tables the triggers read.\n * Created by `installCapture` before the triggers that reference them.\n *\n * _op_ctx the **op context** (docs/VOCABULARY.md): group_id /\n * actor / intent / applying. Exactly ONE row, seeded with\n * defaults so a mutation occurring outside any SDK context\n * is still CAPTURED, just with a null intent. Fail safe,\n * never fail silent.\n *\n * _op_clock a one-row scratchpad holding \"now\" for the current\n * trigger firing. The HLC advance needs to compare `now`\n * against `last_wall` and then reuse the SAME `now` to\n * write it; calling unixepoch() twice could straddle a\n * millisecond boundary and mint a non-monotonic pair. So\n * the value is stashed once per op and read from here.\n *\n * _op_dying natural keys of rows that are mid-DELETE — see the\n * DELETE section below. Keyed by (kind, id).\n *\n * Temp tables are per-connection and mu is one connection per\n * short-lived process, so there is no cross-process leakage and no\n * cleanup to schedule.\n */\nexport const OP_CTX_DDL = `\nCREATE TEMP TABLE IF NOT EXISTS _op_ctx (\n group_id TEXT,\n actor TEXT,\n intent TEXT,\n applying INTEGER NOT NULL DEFAULT 0\n);\nCREATE TEMP TABLE IF NOT EXISTS _op_clock (\n now_ms INTEGER NOT NULL DEFAULT 0\n);\nCREATE TEMP TABLE IF NOT EXISTS _op_dying (\n kind TEXT NOT NULL,\n id INTEGER NOT NULL,\n key TEXT NOT NULL,\n PRIMARY KEY (kind, id)\n);\n`;\n\n/** Seed the singleton rows. Separate from the DDL because CREATE TABLE\n * IF NOT EXISTS is idempotent but INSERT is not. */\nconst OP_CTX_SEED = `\nINSERT INTO _op_ctx (group_id, actor, intent, applying)\n SELECT NULL, NULL, NULL, 0 WHERE NOT EXISTS (SELECT 1 FROM _op_ctx);\nINSERT INTO _op_clock (now_ms)\n SELECT 0 WHERE NOT EXISTS (SELECT 1 FROM _op_clock);\n`;\n\n// ─── Shared SQL fragments ─────────────────────────────────────────────\n\n/** Echo guard. Every trigger carries it. `applying = 1` means we are\n * INGESTING a peer's op, where writing a fresh local op would be an\n * echo that propagates back and never terminates. v2-sync sets it. */\nconst NOT_APPLYING = \"(SELECT applying FROM _op_ctx) = 0\";\n\n/** Advance the persisted HLC by one tick, then read it back formatted.\n * Mirrors `nextHlc` in src/hlc.ts EXACTLY — same monotonic rule (if\n * now > last_wall take now with counter 0, else hold the wall and step\n * the counter) and same `%015d.%06d.%s` serialization. It is\n * reimplemented in SQL rather than called from JS because a trigger\n * cannot call back into JS, and the whole point is that capture\n * happens inside the mutation's transaction.\n *\n * Kept honest by test/capture.test.ts, which asserts the SQL-minted\n * HLC parses with `parseHlc` and interleaves monotonically with\n * JS-minted ones. If the format changes in one place the other fails. */\nconst ADVANCE_CLOCK = `\n UPDATE _op_clock SET now_ms = CAST(unixepoch('subsec') * 1000 AS INTEGER);\n UPDATE machine_identity\n SET last_wall = MAX(last_wall, (SELECT now_ms FROM _op_clock)),\n last_counter = CASE WHEN (SELECT now_ms FROM _op_clock) > last_wall\n THEN 0 ELSE last_counter + 1 END\n WHERE id = 1;`;\n\n/** The just-advanced HLC, formatted. Must be read AFTER ADVANCE_CLOCK. */\nconst HLC = `(SELECT printf('%015d.%06d.%s', last_wall, last_counter, machine_id)\n FROM machine_identity WHERE id = 1)`;\n\n/** Column list shared by every op INSERT. */\nconst OP_COLS = \"(hlc, machine_id, group_id, actor, intent, entity, key, op, payload, created_at)\";\n\n/** The context/identity columns, in OP_COLS order after `hlc`.\n * `group_id` falls back to a per-op random hex string so the column is\n * never NULL: an op outside any SDK context is its own group of one,\n * which is exactly right for undo. */\nconst OP_CTX_VALUES = `\n (SELECT machine_id FROM machine_identity WHERE id = 1),\n COALESCE((SELECT group_id FROM _op_ctx), lower(hex(randomblob(8)))),\n (SELECT actor FROM _op_ctx),\n (SELECT intent FROM _op_ctx)`;\n\n/** ISO-8601 wall time, matching what the SDK writes elsewhere.\n * Advisory only — `hlc` is the ordering key, this is for humans. */\nconst CREATED_AT = `strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;\n\n// ─── Natural keys ─────────────────────────────────────────────────────\n//\n// `ops.key` is ALWAYS the natural key, never a surrogate id. This is\n// exactly why mu's keys don't collide across machines the way an\n// AUTOINCREMENT rowid would: two machines both minting `tasks.id = 7`\n// is inevitable and meaningless, whereas `demo/fix-auth` denotes the\n// same task everywhere.\n//\n// workstream <name> demo\n// task <workstream>/<local_id> demo/fix-auth\n// note <workstream>/<local_id>#<id> demo/fix-auth#12\n// edge <blocker> -> <blocked> demo/a->demo/b\n//\n// Notes keep their surrogate `id` as a suffix because a note has no\n// natural identity of its own — same author and text twice is two\n// notes. Notes are append-only and never edited, so the suffix only\n// needs to be locally unique, and `<task-key>#<id>` is.\n\n/** Workstream name for a workstream id, or the dying-stash value if the\n * workstream row is already gone (FK cascade). */\nconst wsKey = (idExpr: string): string => `COALESCE(\n (SELECT w.name FROM workstreams w WHERE w.id = ${idExpr}),\n (SELECT d.key FROM _op_dying d WHERE d.kind = 'workstream' AND d.id = ${idExpr}),\n '${UNRESOLVED}')`;\n\n/** `<workstream>/<local_id>` for a task id, falling back to the dying\n * stash when the task row itself is already gone. */\nconst taskKey = (idExpr: string): string => `COALESCE(\n (SELECT ${wsKey(\"t.workstream_id\")} || '/' || t.local_id\n FROM tasks t WHERE t.id = ${idExpr}),\n (SELECT d.key FROM _op_dying d WHERE d.kind = 'task' AND d.id = ${idExpr}),\n '${UNRESOLVED}')`;\n\n// ─── Per-table trigger DDL ────────────────────────────────────────────\n\n/** Build the changed-columns-only payload expression for an UPDATE.\n *\n * Nests one CASE per column around a `json_insert` so a column lands\n * in the payload only when it actually changed:\n *\n * CASE WHEN NEW.impact IS NOT OLD.impact\n * THEN json_insert(<inner>, '$.impact', NEW.impact)\n * ELSE <inner> END\n *\n * With six columns and one change that yields a 1-key object. */\nfunction changedPayload(cols: readonly string[]): string {\n let expr = \"'{}'\";\n for (const col of cols) {\n expr = `CASE WHEN NEW.${col} IS NOT OLD.${col}\n THEN json_insert(${expr}, '$.${col}', NEW.${col})\n ELSE ${expr} END`;\n }\n return expr;\n}\n\n/** Whole-row payload for an INSERT: an insert changes every column by\n * definition, so there is nothing to diff against. */\nfunction fullPayload(cols: readonly string[]): string {\n const pairs = cols.map((c) => `'${c}', NEW.${c}`).join(\", \");\n return `json_object(${pairs})`;\n}\n\n/** Whole-row payload for a DELETE, from `OLD.*`.\n *\n * Tombstones are normally EMPTY (`'{}'`): the key identifies the row\n * and the puts before it describe it, so carrying the body again would\n * just bloat the log. Notes are the exception, and the reason is their\n * key.\n *\n * A note's key embeds its rowid (`<ws>/<task>#<id>`), which is NOT\n * portable — a rebuild, a v8/v9 reprojection, or any path that\n * reinserts a note assigns a fresh one (see src/drift.ts, which\n * deliberately matches notes on the task-key PREFIX for exactly this\n * reason). When that happens the historical `put` stays under the old\n * key while a later `del` is emitted under the current one, and\n * nothing joins the two.\n *\n * Undo folds the puts for a key to reconstruct a tombstoned row, so a\n * note in that state reconstructed to `{}` and was silently never\n * restored (drift-641: `mu undo` of a workstream teardown brought back\n * every task and edge but only the notes whose rowid happened to be\n * unchanged). A self-describing note tombstone closes that hole: the\n * op that removed the row also carries what the row WAS, so the\n * inverse never depends on finding a matching put. */\nfunction fullOldPayload(cols: readonly string[]): string {\n const pairs = cols.map((c) => `'${c}', OLD.${c}`).join(\", \");\n return `json_object(${pairs})`;\n}\n\n/** A note tombstone's payload. The full row only when the log holds no\n * put under the same key (a reprojection gave the note a new rowid, so\n * undo could not fold it back: drift-641); otherwise `{}`, because\n * undo's fold and apply's delete both read the content from that put.\n * Without this, every workstream teardown wrote each note a second time. */\nfunction noteTombstonePayload(keyExpr: string, cols: readonly string[]): string {\n return `CASE WHEN EXISTS (\n SELECT 1 FROM ops WHERE entity = 'note' AND key = ${keyExpr} AND op = 'put'\n ) THEN '{}' ELSE ${fullOldPayload(cols)} END`;\n}\n\n/** `WHEN` clause restricting an UPDATE trigger to real changes. A\n * no-op UPDATE (SET x = x, or rewriting identical values) produces NO\n * op at all, which keeps the log free of churn that would otherwise\n * make every `mu log` read noisy and every sync flush bigger. */\nfunction anyChanged(cols: readonly string[]): string {\n return cols.map((c) => `NEW.${c} IS NOT OLD.${c}`).join(\"\\n OR \");\n}\n\n/** Emit one op. `keyExpr`/`payloadExpr` are SQL expressions. */\nfunction emitOp(\n entity: string,\n opKind: \"put\" | \"del\",\n keyExpr: string,\n payloadExpr: string,\n): string {\n return `${ADVANCE_CLOCK}\n INSERT INTO ops ${OP_COLS} VALUES (\n ${HLC},${OP_CTX_VALUES},\n '${entity}',\n ${keyExpr},\n '${opKind}',\n ${payloadExpr},\n ${CREATED_AT}\n );`;\n}\n\n/** Columns captured per portable table. Deliberately EXCLUDES surrogate\n * ids and FK columns that are already encoded in the natural key\n * (`tasks.workstream_id`, `task_notes.task_id`, the edge endpoints),\n * because shipping a peer our local rowids would be worse than\n * useless. Changing one of those would re-key the row, which no op\n * can express, so KEY_COLUMNS below refuses it instead. `tasks.owner_id` IS captured: it is an FK into the\n * machine-local `agents` table, so it never syncs (see\n * MACHINE_LOCAL_TABLES), but it must still be captured for local\n * history and undo of a claim/release. */\nconst CAPTURED_COLUMNS = {\n workstreams: [\"name\", \"created_at\"],\n tasks: [\n \"local_id\",\n \"title\",\n \"status\",\n \"substate\",\n \"impact\",\n \"effort_days\",\n \"owner_id\",\n \"created_at\",\n \"updated_at\",\n ],\n task_notes: [\"author\", \"content\", \"created_at\"],\n task_edges: [\"created_at\"],\n} as const satisfies Record<(typeof PORTABLE_TABLES)[number], readonly string[]>;\n\n/** Columns that make up (or resolve to) each table's natural key.\n *\n * An UPDATE of one of these moves the row to a different `ops.key`\n * (`a/t1` -> `b/t1`), and every op already in the log stays filed under\n * the old key. The UPDATE triggers above diff only CAPTURED_COLUMNS, so\n * such an update used to emit NOTHING: no tombstone for the old key, no\n * put for the new one. Sync, undo, rebuild and `mu doctor` then disagree\n * with the live row. Children re-key with it (a task's notes and edges\n * embed its key), so capturing a move would mean re-emitting a subtree.\n *\n * Natural keys are immutable instead: apply (NEVER_APPLY) and undo\n * (NEVER_RESTORE) already never write these columns, and a raw\n * `mu sql` UPDATE is refused here with an explicit error rather than\n * committing a row the log cannot explain. The guard is NOT gated on\n * the echo guard: no mu path re-keys a row, ingest included. */\nconst KEY_COLUMNS = {\n workstreams: [\"name\"],\n tasks: [\"workstream_id\", \"local_id\"],\n task_notes: [\"id\", \"task_id\"],\n task_edges: [\"from_task_id\", \"to_task_id\"],\n} as const satisfies Record<(typeof PORTABLE_TABLES)[number], readonly string[]>;\n\n/** BEFORE UPDATE trigger refusing a natural-key change on `table`. */\nfunction keyGuard(table: keyof typeof KEY_COLUMNS): string {\n const cols = KEY_COLUMNS[table];\n const msg =\n `mu: ${table}.${cols.join(\"/\")} is part of the natural key that every op is filed under; ` +\n \"changing it would desync the ops log, sync and undo. \" +\n \"Add a new row (mu task add / mu workstream init) instead of moving or renaming one.\";\n return `\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_${table}_key\nBEFORE UPDATE OF ${cols.join(\", \")} ON ${table}\n WHEN ${anyChanged(cols)}\nBEGIN\n SELECT RAISE(ABORT, '${msg.replaceAll(\"'\", \"''\")}');\nEND;`;\n}\n\n/**\n * THE DELETE SUBTLETY\n * -------------------\n * Every other op can resolve its natural key lazily, because the row is\n * still there. DELETE cannot: by the time anything downstream looks,\n * the row is gone. So the key is captured INLINE, in the trigger, while\n * the row still exists.\n *\n * That alone is not enough, because of FK CASCADE. Empirically (this\n * was the thing most worth checking): SQLite FK `ON DELETE CASCADE`\n * DOES fire triggers on the cascaded child rows — and it does so\n * regardless of the `recursive_triggers` pragma, which governs only\n * trigger-initiated recursion, not FK actions. Good: destroying a\n * workstream captures its tasks, notes and edges without any explicit\n * walk in the SDK.\n *\n * The trap is ORDERING. With AFTER DELETE triggers, cascaded children\n * fire after the parent row is already deleted, so joining to the\n * parent to build the child's key yields nothing:\n *\n * DELETE FROM workstreams WHERE id = 1\n * -> edge op key \"<unresolved>-><unresolved>\"\n * -> note op key \"<unresolved>#100\"\n * -> task op key \"<unresolved>/t1\"\n *\n * Two mechanisms fix it together:\n *\n * 1. BEFORE DELETE, not AFTER. The row and its ancestors are all\n * still present when the trigger body runs, so `OLD.*` plus a\n * normal join resolves the key.\n * 2. The `_op_dying` stash. BEFORE DELETE fixes the FIRST level only:\n * cascade children still fire after their parent's row is gone.\n * So each parent's BEFORE DELETE trigger records its own natural\n * key into `_op_dying` before emitting, and the child key\n * resolvers (`wsKey` / `taskKey`) consult the stash when the live\n * join misses. Rows are never removed from the stash: it is a\n * per-connection temp table on a short-lived process, ids are not\n * reused within a transaction, and an INSERT OR REPLACE keyed on\n * (kind, id) keeps it correct even if an id is recycled later.\n *\n * The alternative — having the parent trigger emit ops for its whole\n * subtree and suppressing the cascade triggers — also works and was\n * prototyped, but it duplicates the subtree walk in SQL four times and\n * breaks the moment a new FK is added. The stash is smaller and\n * self-maintaining.\n */\nconst DYING_STASH = (kind: \"workstream\" | \"task\", keyExpr: string): string =>\n `INSERT OR REPLACE INTO _op_dying (kind, id, key) VALUES ('${kind}', OLD.id, ${keyExpr});`;\n\n/** All capture triggers, as one idempotent DDL string. */\nfunction buildTriggerDdl(): string {\n const wsCols = CAPTURED_COLUMNS.workstreams;\n const taskCols = CAPTURED_COLUMNS.tasks;\n const noteCols = CAPTURED_COLUMNS.task_notes;\n const edgeCols = CAPTURED_COLUMNS.task_edges;\n\n const taskKeyNew = `${wsKey(\"NEW.workstream_id\")} || '/' || NEW.local_id`;\n const taskKeyOld = `${wsKey(\"OLD.workstream_id\")} || '/' || OLD.local_id`;\n const edgeKeyNew = `${taskKey(\"NEW.from_task_id\")} || '->' || ${taskKey(\"NEW.to_task_id\")}`;\n const edgeKeyOld = `${taskKey(\"OLD.from_task_id\")} || '->' || ${taskKey(\"OLD.to_task_id\")}`;\n\n return `\n-- ─── natural-key guards (see KEY_COLUMNS) ───────────────────────────\n${keyGuard(\"workstreams\")}\n${keyGuard(\"tasks\")}\n${keyGuard(\"task_notes\")}\n${keyGuard(\"task_edges\")}\n\n-- ─── workstreams ────────────────────────────────────────────────────\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_workstreams_ins\nAFTER INSERT ON workstreams WHEN ${NOT_APPLYING}\nBEGIN\n ${emitOp(\"workstream\", \"put\", \"NEW.name\", fullPayload(wsCols))}\nEND;\n\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_workstreams_upd\nAFTER UPDATE ON workstreams WHEN ${NOT_APPLYING}\n AND (${anyChanged(wsCols)})\nBEGIN\n ${emitOp(\"workstream\", \"put\", \"NEW.name\", changedPayload(wsCols))}\nEND;\n\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_workstreams_del\nBEFORE DELETE ON workstreams WHEN ${NOT_APPLYING}\nBEGIN\n ${DYING_STASH(\"workstream\", \"OLD.name\")}\n ${emitOp(\"workstream\", \"del\", \"OLD.name\", \"'{}'\")}\nEND;\n\n-- ─── tasks ──────────────────────────────────────────────────────────\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_tasks_ins\nAFTER INSERT ON tasks WHEN ${NOT_APPLYING}\nBEGIN\n ${emitOp(\"task\", \"put\", taskKeyNew, fullPayload(taskCols))}\nEND;\n\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_tasks_upd\nAFTER UPDATE ON tasks WHEN ${NOT_APPLYING}\n AND (${anyChanged(taskCols)})\nBEGIN\n ${emitOp(\"task\", \"put\", taskKeyNew, changedPayload(taskCols))}\nEND;\n\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_tasks_del\nBEFORE DELETE ON tasks WHEN ${NOT_APPLYING}\nBEGIN\n ${DYING_STASH(\"task\", taskKeyOld)}\n ${emitOp(\"task\", \"del\", taskKeyOld, \"'{}'\")}\nEND;\n\n-- ─── task_notes ─────────────────────────────────────────────────────\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_task_notes_ins\nAFTER INSERT ON task_notes WHEN ${NOT_APPLYING}\nBEGIN\n ${emitOp(\"note\", \"put\", `${taskKey(\"NEW.task_id\")} || '#' || NEW.id`, fullPayload(noteCols))}\nEND;\n\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_task_notes_upd\nAFTER UPDATE ON task_notes WHEN ${NOT_APPLYING}\n AND (${anyChanged(noteCols)})\nBEGIN\n ${emitOp(\"note\", \"put\", `${taskKey(\"NEW.task_id\")} || '#' || NEW.id`, changedPayload(noteCols))}\nEND;\n\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_task_notes_del\nBEFORE DELETE ON task_notes WHEN ${NOT_APPLYING}\nBEGIN\n ${emitOp(\"note\", \"del\", `${taskKey(\"OLD.task_id\")} || '#' || OLD.id`, noteTombstonePayload(`${taskKey(\"OLD.task_id\")} || '#' || OLD.id`, noteCols))}\nEND;\n\n-- ─── task_edges ─────────────────────────────────────────────────────\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_task_edges_ins\nAFTER INSERT ON task_edges WHEN ${NOT_APPLYING}\nBEGIN\n ${emitOp(\"edge\", \"put\", edgeKeyNew, fullPayload(edgeCols))}\nEND;\n\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_task_edges_upd\nAFTER UPDATE ON task_edges WHEN ${NOT_APPLYING}\n AND (${anyChanged(edgeCols)})\nBEGIN\n ${emitOp(\"edge\", \"put\", edgeKeyNew, changedPayload(edgeCols))}\nEND;\n\nCREATE TEMP TRIGGER IF NOT EXISTS _cap_task_edges_del\nBEFORE DELETE ON task_edges WHEN ${NOT_APPLYING}\nBEGIN\n ${emitOp(\"edge\", \"del\", edgeKeyOld, \"'{}'\")}\nEND;\n`;\n}\n\n/** The capture trigger DDL. Built once at module load; pure string. */\nexport const CAPTURE_TRIGGER_DDL = buildTriggerDdl();\n\n/**\n * How many `total_changes()` the capture triggers themselves added while\n * stamping the ops of `groupId`. `mu sql --confirm-rows` subtracts this\n * from its delta so it can count user rows on the SAME capture-on run it\n * commits (no probe-then-rerun).\n *\n * Mirrors the trigger bodies above: every `emitOp` is exactly 3 changes\n * (the `_op_clock` UPDATE, the `machine_identity` UPDATE, the ops\n * INSERT), and every workstream/task tombstone also writes 1\n * `DYING_STASH` row. Change a trigger body and this must change too;\n * test/sql-capture.test.ts pins it with cascading deletes.\n */\nexport function captureBookkeepingChanges(db: CaptureDb, groupId: string): number {\n const row = db\n .prepare(\n `SELECT COUNT(*) AS ops,\n COALESCE(SUM(op = 'del' AND entity IN ('workstream', 'task')), 0) AS stashed\n FROM ops WHERE group_id = ?`,\n )\n .get(groupId) as { ops: number; stashed: number };\n return 3 * row.ops + row.stashed;\n}\n\n// ─── Installation ─────────────────────────────────────────────────────\n\n/**\n * Create the op-context temp tables and the capture triggers on this\n * connection. Called by `openDb` on every non-readonly open.\n *\n * Everything here is idempotent (CREATE TEMP TABLE / TRIGGER IF NOT\n * EXISTS + guarded seed INSERTs) so calling it twice is harmless.\n *\n * No transaction and no 'already exists' swallow, unlike\n * `applySchema`: the temp schema is PRIVATE to this connection, so the\n * concurrent-openDb race that forced those measures on the main schema\n * cannot occur here. Two processes opening the same file each build\n * their own temp schema without contending.\n */\nexport function installCapture(db: CaptureDb): void {\n db.exec(OP_CTX_DDL);\n db.exec(OP_CTX_SEED);\n db.exec(CAPTURE_TRIGGER_DDL);\n}\n\n/** The slice of better-sqlite3's Database that capture needs. Declared\n * structurally rather than importing `Db` from db.ts, because db.ts\n * imports THIS module — a nominal import would be a cycle, and an\n * import cycle here silently breaks `node dist/cli.js`. */\nexport interface CaptureDb {\n exec(sql: string): unknown;\n prepare(sql: string): {\n run(...params: readonly unknown[]): unknown;\n get(...params: readonly unknown[]): unknown;\n };\n}\n","// mu — TaskStatus enum + helpers.\n//\n// Single source of truth for \"what statuses and substates can a task\n// have\". The schema (db.ts) seeds the task_substates lookup table from\n// TASK_SUBSTATE_ROWS, and a composite FK from tasks (status, substate)\n// into it is the only guard on both columns. Add a status or substate\n// here only.\n//\n// Extracted from src/tasks.ts as part of refactor_split_large_src_files.\n\nexport type TaskStatus = \"OPEN\" | \"IN_PROGRESS\" | \"CLOSED\";\n\n/** Every legal task status, in canonical order (matches the seeded\n * task_substates rows). Exported so CLI surfaces (`--status` validators,\n * --help text, error messages) name them all in one place; missing\n * one used to silently lie about the supported set. */\nexport const TASK_STATUSES: readonly TaskStatus[] = [\"OPEN\", \"IN_PROGRESS\", \"CLOSED\"];\n\nexport function isTaskStatus(s: string): s is TaskStatus {\n return (TASK_STATUSES as readonly string[]).includes(s);\n}\n\n/** Pipe-separated list of every legal status, e.g.\n * 'OPEN | IN_PROGRESS | CLOSED'. Single source of truth for\n * --help text and error messages so adding a new status doesn't\n * leave stale lists rotting in the CLI surface. */\nexport const TASK_STATUS_LIST = TASK_STATUSES.join(\" | \");\n\n/** Legal substates per status. `substate` qualifies `status` and never\n * touches edge semantics: only `status === \"CLOSED\"` satisfies a\n * `blocks` edge. Open: `parked` = set aside on purpose; `triage` = a\n * proposed task (e.g. a review finding) not yet accepted as work. Both\n * stay out of `ready` and `claim`. Closed: `rejected` = the proposal\n * was declined; `wontfix` = valid, but not worth doing. */\nexport const TASK_SUBSTATES = {\n OPEN: [\"todo\", \"parked\", \"triage\"],\n IN_PROGRESS: [\"active\"],\n CLOSED: [\"done\", \"rejected\", \"wontfix\", \"duplicate\", \"superseded\"],\n} as const satisfies Record<TaskStatus, readonly string[]>;\n\nexport type TaskSubstate = (typeof TASK_SUBSTATES)[TaskStatus][number];\n\n/** The substate a status takes when none is given. Never null: an\n * absent value must not carry meaning. */\nexport const DEFAULT_SUBSTATE: {\n readonly [S in TaskStatus]: (typeof TASK_SUBSTATES)[S][number];\n} = { OPEN: \"todo\", IN_PROGRESS: \"active\", CLOSED: \"done\" };\n\nexport interface TaskPair {\n status: TaskStatus;\n substate: TaskSubstate;\n}\n\n/** Rows for seeding task_substates: [status, substate, isDefault]. */\nexport const TASK_SUBSTATE_ROWS: ReadonlyArray<readonly [TaskStatus, TaskSubstate, 0 | 1]> =\n TASK_STATUSES.flatMap((status) =>\n TASK_SUBSTATES[status].map(\n (substate) => [status, substate, substate === DEFAULT_SUBSTATE[status] ? 1 : 0] as const,\n ),\n );\n\nexport function isValidPair(status: string, substate: string): boolean {\n if (!isTaskStatus(status)) return false;\n return (TASK_SUBSTATES[status] as readonly string[]).includes(substate);\n}\n\n/** Retired v9 statuses and the pair each one now means. */\nconst LEGACY_STATUS_PAIRS: Readonly<Record<string, TaskPair>> = {\n REJECTED: { status: \"CLOSED\", substate: \"rejected\" },\n DEFERRED: { status: \"OPEN\", substate: \"parked\" },\n};\n\n/** REJECTED -> {CLOSED, rejected}; DEFERRED -> {OPEN, parked}; else null. */\nexport function mapLegacyStatus(value: string): TaskPair | null {\n const pair = LEGACY_STATUS_PAIRS[value];\n return pair ? { ...pair } : null;\n}\n\n/** Map a stored/remote pair onto a legal one. Legacy status -> mapped pair\n * (ignores substate arg). Unknown status -> null. Known status with\n * unknown/missing/mismatched substate -> {status, DEFAULT_SUBSTATE[status]}. */\nexport function resolvePair(status: string, substate: unknown): TaskPair | null {\n const legacy = mapLegacyStatus(status);\n if (legacy) return legacy;\n if (!isTaskStatus(status)) return null;\n if (typeof substate === \"string\" && isValidPair(status, substate)) {\n return { status, substate: substate as TaskSubstate };\n }\n return { status, substate: DEFAULT_SUBSTATE[status] };\n}\n\n/** OPEN substates that keep a task out of `ready`, `next` and `claim`. */\nexport const UNSCHEDULED_SUBSTATES: readonly TaskSubstate[] = [\"parked\", \"triage\"];\n\nexport function isUnscheduled(pair: { status: string; substate: string }): boolean {\n return (\n pair.status === \"OPEN\" && (UNSCHEDULED_SUBSTATES as readonly string[]).includes(pair.substate)\n );\n}\n\n/** \"OPEN\" for default pairs, \"OPEN/parked\" otherwise. Shared by CLI + TUI. */\nexport function formatPair(pair: TaskPair): string {\n return pair.substate === DEFAULT_SUBSTATE[pair.status]\n ? pair.status\n : `${pair.status}/${pair.substate}`;\n}\n","// mu — the log surface, as a thin shim over the **ops log**.\n//\n// @deprecated — v2-log-verb replaces this module. v9 dropped the\n// `agent_logs` table; `ops` is now the single append-only record of\n// every change (VISION.md § 2b). Rather than rewrite ~25 consumers in\n// the schema commit, this module keeps its legacy signatures\n// (appendLog / listLogs / latestSeq / emitEvent) and reads and writes\n// `ops` rows underneath. v2-capture makes triggers the real writers\n// and v2-log-verb re-renders `mu log` from intents; both delete\n// chunks of this file.\n//\n// Shim mapping (legacy log row -> op):\n// kind -> ops.entity ('message' | 'event' | ...)\n// source -> ops.actor\n// workstream -> ops.key (natural key; '' = machine-wide)\n// payload -> ops.payload (raw text, not JSON, until v2-capture)\n// seq -> ops.seq (same AUTOINCREMENT cursor semantics)\n//\n// `ops.hlc` is now a REAL HLC minted by `nextHlc` (src/hlc.ts). The old\n// placeholder `<iso>|<uuid>` is gone: `parseHlc` deliberately rejects\n// that shape as a tripwire, so leaving it would have failed loudly the\n// moment anything downstream read the log in HLC order.\n//\n// `group_id` is still a fresh uuid per entry rather than the ambient\n// **op context** group. That is correct for this module's remaining\n// callers, which are log APPENDS (`mu log \"<text>\"`, event breadcrumbs) —\n// each is its own user-visible action, not part of a row-mutation\n// group. Rows written through the capture triggers get the ambient\n// group; these are hand-written log lines, not captured mutations.\n\nimport { randomUUID } from \"node:crypto\";\nimport { type Db, SYNCED_ENTITIES } from \"./db.js\";\nimport { nextHlc } from \"./hlc.js\";\nimport { intentSpellings } from \"./legacy-ops.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\n\nexport type LogKind = \"message\" | \"event\" | \"broadcast\" | string;\n\nexport interface LogRow {\n /** Monotonic AUTOINCREMENT id. Use as the cursor for `--since`. */\n seq: number;\n /** Workstream this entry belongs to, or `null` for machine-wide. */\n workstreamName: string | null;\n /** Free TEXT: agent name, \"system\", \"user\", or anything a caller picks.\n * Captured ops that ran outside any actor context have no actor, and\n * render as \"system\" rather than the string \"null\". */\n source: string;\n /** Structured intent ('task.close', 'agent.spawn', ...). Null only for\n * operator-authored prose lines (`mu log \"<text>\"` / a `--kind` ledger),\n * which name no state change. The formatter in src/log-render.ts\n * renders from this — never from the payload text. */\n intent: string | null;\n /** Undo group: every op of one operator action shares it. Surfaced so\n * `mu log` can print it and `mu log --group <id>` can filter to it\n * (undo discoverability). */\n group: string;\n /** 'put' (semantic partial update) or 'del' (tombstone). The formatter\n * needs it to tell \"edge removed\" from \"row touched\". */\n op: string;\n /** Free TEXT: \"message\" (default), \"event\" (auto state changes),\n * \"broadcast\" (explicit cross-agent), or any caller-defined value. */\n kind: LogKind;\n /** Free utf-8 string. May be JSON if the kind suggests structure. */\n payload: string;\n /** ISO 8601 timestamp set at insert time. */\n createdAt: string;\n}\n\ninterface RawLogRow {\n seq: number;\n /** `ops.key` verbatim (the natural key). '' means machine-wide. */\n workstream: string | null;\n /** `ops.actor`, which IS nullable: a captured op outside any actor\n * context has none. Normalised in `rowFromDb`. */\n source: string | null;\n intent: string | null;\n group_id: string;\n op: string;\n kind: string;\n payload: string;\n created_at: string;\n}\n\n/** SELECT clause mapping op columns back onto the legacy log row shape.\n * `ops.key` already holds the operator-facing workstream name (the\n * natural key), so there is no join to do; '' means machine-wide and\n * is normalised back to NULL in `rowFromDb`. */\nconst SELECT_LOG_COLS = `\n l.seq AS seq,\n l.key AS workstream,\n l.actor AS source,\n l.intent AS intent,\n l.group_id AS group_id,\n l.op AS op,\n l.entity AS kind,\n l.payload AS payload,\n l.created_at AS created_at\n`;\n\nconst LOG_FROM_JOIN = \"FROM ops l\";\n\n// WHY THERE IS NO LONGER AN ENTITY FILTER (v2-retire-log-shim)\n//\n// v2-capture briefly left `ops` holding the SAME change twice: once as\n// a typed op from the trigger (intent='task.update', key='demo/t1',\n// JSON payload) and once as a prose breadcrumb from `emitEvent`\n// (intent=NULL, key='demo', free text). A filter pinning `mu log` to\n// the prose entities was needed so a single `task add` did not surface\n// twice.\n//\n// The duplicate prose emits are gone, so the filter's reason to exist\n// went with them. `mu log` now reads EVERY op, which is what\n// docs/VOCABULARY.md § log entry has always claimed (\"a rendered op...\n// not a distinct table\"). Payloads for captured ops are still raw JSON;\n// v2-log-verb renders prose from `intent`. Uglier for now, honest, and\n// no longer lossy: a peer's synced ops appear in `mu log` too, which\n// prose events could never do (they were machine-local by accident of\n// entity='event' not being in SYNCED_ENTITIES).\n//\n// `latestSeq` deliberately shares this same \"no filter\" shape. When the\n// two disagreed, `mu log --tail` started its cursor past rows the\n// non-tail view had shown and silently skipped them.\n\n/** Scope a log query to one workstream.\n *\n * `ops.key` is the NATURAL key, so it is the bare workstream name for\n * workstream-scoped rows ('demo') and a qualified ref for everything\n * inside it ('demo/t1', 'demo/t1#1', 'demo/t2->demo/t1'). Matching\n * only `key = 'demo'` would therefore hide every task, note, and edge\n * op in the workstream — which is most of them. Exact-or-prefix is the\n * smallest predicate that reads \"belongs to this workstream\". */\nexport function workstreamScopeSql(column = \"l.key\"): string {\n return `(${column} = ? OR ${column} LIKE ? ESCAPE '\\\\')`;\n}\n\n/** Bind params for `workstreamScopeSql`. The LIKE pattern escapes the\n * operator-supplied name so a workstream containing '%' or '_' cannot\n * widen the match. */\nexport function workstreamScopeParams(workstream: string): [string, string] {\n const escaped = workstream.replace(/[\\\\%_]/g, (c) => `\\\\${c}`);\n return [workstream, `${escaped}/%`];\n}\n\n/**\n * Hide parent-row TOUCH ops from the log surface.\n *\n * Adding a note or an edge bumps its task's `updated_at` so\n * `--sort recency` works. That UPDATE fires the tasks capture trigger,\n * producing a second op in the same group whose payload is ONLY\n * `updated_at` — no new information, and it renders as a duplicate of\n * the note/edge line the operator actually cares about. A `task note`\n * therefore appeared twice in `mu log`.\n *\n * These rows stay in `ops` (they are real state changes, and per-field\n * merge needs them); they are just not LOG LINES. The predicate is\n * shared by `listLogs` and `latestSeq` — those two MUST return the same\n * row set, or `--tail` starts its cursor past rows the non-tail view\n * already showed (hit in R4 and again in R7).\n *\n * `json_extract` is not used: SQLite's JSON1 is compiled in by default\n * for better-sqlite3, but a plain LENGTH test needs no extension and is\n * cheaper. A payload with only `updated_at` is always short.\n */\nconst TOUCH_OP_FILTER = `NOT (\n l.intent IS NOT NULL\n AND l.op = 'put'\n AND l.payload LIKE '{\"updated_at\":%'\n AND LENGTH(l.payload) < 45\n)`;\n\n/** Sentinel stored in `ops.key` for machine-wide entries (ops.key is\n * NOT NULL). Exported so the few call sites that query `ops` directly\n * agree with the shim. */\nexport const MACHINE_WIDE_KEY = \"\";\n\n// ─── group-id prefix resolution (shared) ──────────────────────────\n//\n// Group ids are uuids, which nobody types. `mu undo` prints 8-char\n// prefixes and accepts them, so `mu log --group` must too — otherwise the\n// documented workflow (`mu undo` to see the id → `mu log --group <id>` to\n// inspect it → `mu undo <id> --yes`) breaks in the middle, and it breaks\n// SILENTLY: an unmatched prefix returns zero rows, which reads as \"this\n// group did nothing\" rather than \"you gave me a prefix I ignored\".\n// (bug_group_id_prefix_asymmetry — found by following the workflow across\n// R9 and R10; each verb was self-consistent on its own.)\n//\n// This lives HERE, next to the ops reader, so both verbs share ONE\n// resolution rule. `src/undo.ts` delegates to it rather than keeping a\n// second copy. Same affordance git gives for abbreviated shas, and the\n// same shape as `src/tasks/id.ts`'s resolve-or-raise helpers.\n\n/** Raised when a group-id prefix matches more than one group.\n * Astronomically unlikely with uuids, but silently picking one of two\n * candidate groups to UNDO is not an acceptable failure mode. */\nexport class GroupIdAmbiguousError extends Error implements HasNextSteps {\n constructor(\n readonly prefix: string,\n readonly candidates: readonly string[],\n ) {\n super(\n `group id ${JSON.stringify(prefix)} is ambiguous: matches ${candidates.length} groups (${candidates\n .slice(0, 4)\n .map((c) => c.slice(0, 12))\n .join(\", \")}${candidates.length > 4 ? \", …\" : \"\"})`,\n );\n this.name = \"GroupIdAmbiguousError\";\n }\n\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List recent groups with their ids\", command: \"mu undo\" },\n {\n intent: \"Retry with more characters\",\n command: `mu log --group ${this.candidates[0] ?? \"<full-id>\"}`,\n },\n ];\n }\n}\n\n/**\n * Resolve a possibly-abbreviated group id to the full one.\n *\n * Returns null when nothing matches, so callers choose their own\n * not-found error (undo raises `UndoGroupNotFoundError`; `mu log`\n * filters to nothing but says so). Throws `GroupIdAmbiguousError` when a\n * prefix matches several groups — that is a genuine conflict the\n * operator must resolve, not something to guess at.\n *\n * An EXACT match always wins over prefix matching, so a full uuid can\n * never be shadowed by a longer id that happens to start with it.\n */\nexport function groupIdFromPrefix(db: Db, prefix: string): string | null {\n if (prefix === \"\") return null;\n const rows = db\n .prepare(\n `SELECT DISTINCT group_id AS groupId FROM ops\n WHERE group_id = @exact OR group_id LIKE @prefix || '%' ESCAPE '\\\\'`,\n )\n .all({ exact: prefix, prefix: prefix.replace(/[\\\\%_]/g, (c) => `\\\\${c}`) }) as {\n groupId: string;\n }[];\n const exact = rows.find((r) => r.groupId === prefix);\n if (exact !== undefined) return exact.groupId;\n const first = rows[0];\n if (rows.length === 1 && first !== undefined) return first.groupId;\n if (rows.length === 0) return null;\n throw new GroupIdAmbiguousError(\n prefix,\n rows.map((r) => r.groupId),\n );\n}\n\n/** This machine's identity — the peer id every op is stamped with. */\nfunction machineId(db: Db): string {\n const row = db.prepare(\"SELECT machine_id FROM machine_identity WHERE id = 1\").get() as\n | { machine_id: string }\n | undefined;\n return row?.machine_id ?? \"unknown\";\n}\n\nfunction rowFromDb(row: RawLogRow): LogRow {\n return {\n seq: row.seq,\n workstreamName: row.workstream === MACHINE_WIDE_KEY ? null : row.workstream,\n source: row.source ?? \"system\",\n intent: row.intent,\n group: row.group_id,\n op: row.op,\n kind: row.kind,\n payload: row.payload,\n createdAt: row.created_at,\n };\n}\n\nexport interface AppendLogOptions {\n /** Workstream this entry belongs to. `null` for machine-wide. */\n workstream: string | null;\n /** Who emitted this. Agent name, \"system\", \"user\", or arbitrary. */\n source: string;\n /** Defaults to \"message\". */\n kind?: LogKind;\n /** Free utf-8. Multi-line allowed. */\n payload: string;\n /** Structured intent. Set by `emitEvent` for the local-only changes\n * no trigger can see. Stays null for operator-authored `mu log\n * write` lines, which are prose by nature and have no state change\n * to name. */\n intent?: string;\n}\n\n/** Kinds a log line may not use: the entities that sync AND project\n * into a portable table. A log line's kind is stored as `ops.entity`,\n * so `mu log \"x\" --kind task` wrote a prose op that sync, rebuild and\n * apply then read as a real task op (\"malformed task key\"), wedging\n * `mu rebuild` and every peer's ingest. 'message' syncs but projects\n * nowhere, so it stays allowed (it is the default). */\nconst RESERVED_LOG_KINDS: ReadonlySet<string> = new Set(\n SYNCED_ENTITIES.filter((e) => e !== \"message\"),\n);\n\n/** Raised by `appendLog` for a kind in RESERVED_LOG_KINDS. Usage lane\n * (exit 2): the operator picked a tag that names a projectable entity. */\nexport class LogKindReservedError extends Error implements HasNextSteps {\n override readonly name = \"LogKindReservedError\";\n constructor(readonly kind: string) {\n super(\n `--kind ${JSON.stringify(kind)} is reserved: ${[...RESERVED_LOG_KINDS].join(\", \")} name projectable op entities, and a log line under one would be synced and replayed as a real ${kind} change`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Use a channel tag that names your ledger\",\n command: `mu log --kind ${this.kind}-log '<text>'`,\n },\n ];\n }\n}\n\n/**\n * Append a log entry. Returns the inserted row (with assigned `seq`).\n * Constant-time. Single INSERT; safe to call from any state-changing\n * verb without a transaction wrapper.\n */\nexport function appendLog(db: Db, opts: AppendLogOptions): LogRow {\n const kind = opts.kind ?? \"message\";\n if (RESERVED_LOG_KINDS.has(kind)) throw new LogKindReservedError(kind);\n const createdAt = new Date().toISOString();\n // `ops.key` is the NATURAL key, so the workstream NAME goes in\n // verbatim — no resolution, and the op stays readable after the\n // workstream is destroyed (which is the point of an ops log).\n const key = opts.workstream ?? MACHINE_WIDE_KEY;\n const group = randomUUID();\n const result = db\n .prepare(\n `INSERT INTO ops (hlc, machine_id, group_id, actor, intent, entity, key, op, payload, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, 'put', ?, ?)`,\n )\n .run(\n nextHlc(db),\n machineId(db),\n group,\n opts.source,\n opts.intent ?? null,\n kind,\n key,\n opts.payload,\n createdAt,\n );\n return {\n seq: Number(result.lastInsertRowid),\n workstreamName: opts.workstream,\n source: opts.source,\n intent: opts.intent ?? null,\n group,\n op: \"put\",\n kind,\n payload: opts.payload,\n createdAt,\n };\n}\n\nexport interface ListLogsOptions {\n /** Filter by workstream. `undefined` = every workstream + machine-wide.\n * `null` = ONLY machine-wide entries. */\n workstream?: string | null;\n /** Strictly > this seq. Use to resume a tail. */\n since?: number;\n /** Cap the result. With `since`, returns the FIRST N matching (oldest\n * first). Without `since`, returns the LAST N (most recent),\n * re-sorted oldest-first. */\n limit?: number;\n source?: string;\n /** Filter by `ops.entity` (the legacy `kind` column). */\n kind?: string;\n /** Filter by structured `ops.intent`, e.g. 'task.close'. */\n intent?: string;\n /** Filter to one undo group (`ops.group_id`). */\n group?: string;\n}\n\n/**\n * List log entries. Always returns oldest-first. Use `since` for\n * cursor-based reads (the canonical tail pattern); use `limit` alone\n * for \"show me the most recent N\" reads.\n */\nexport function listLogs(db: Db, opts: ListLogsOptions = {}): LogRow[] {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (opts.workstream === null) {\n conditions.push(\"l.key = ?\");\n params.push(MACHINE_WIDE_KEY);\n } else if (opts.workstream !== undefined) {\n conditions.push(workstreamScopeSql());\n params.push(...workstreamScopeParams(opts.workstream));\n }\n if (opts.since !== undefined) {\n conditions.push(\"l.seq > ?\");\n params.push(opts.since);\n }\n if (opts.source === \"system\") {\n // rowFromDb renders a NULL actor as \"system\", so the filter must\n // match what the listing shows.\n conditions.push(\"(l.actor = ? OR l.actor IS NULL)\");\n params.push(opts.source);\n } else if (opts.source !== undefined) {\n conditions.push(\"l.actor = ?\");\n params.push(opts.source);\n }\n if (opts.kind !== undefined) {\n conditions.push(\"l.entity = ?\");\n params.push(opts.kind);\n }\n if (opts.intent !== undefined) {\n // Accept every historical spelling, so a renamed intent does not\n // truncate the user's history at the release boundary.\n const spellings = intentSpellings(opts.intent);\n conditions.push(`l.intent IN (${spellings.map(() => \"?\").join(\", \")})`);\n params.push(...spellings);\n }\n if (opts.group !== undefined) {\n conditions.push(\"l.group_id = ?\");\n params.push(opts.group);\n }\n // Same predicate as latestSeq — see TOUCH_OP_FILTER.\n conditions.push(TOUCH_OP_FILTER);\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\n\n // Two query shapes:\n // - When `since` is set, ascending order is what we want directly.\n // - When `limit` is set without `since`, fetch the most-recent N\n // (descending) then reverse so the caller still sees oldest-first.\n if (opts.limit !== undefined && opts.since === undefined) {\n const rowsDesc = db\n .prepare(`SELECT ${SELECT_LOG_COLS} ${LOG_FROM_JOIN} ${where} ORDER BY l.seq DESC LIMIT ?`)\n .all(...params, opts.limit) as RawLogRow[];\n return rowsDesc.reverse().map(rowFromDb);\n }\n\n let sql = `SELECT ${SELECT_LOG_COLS} ${LOG_FROM_JOIN} ${where} ORDER BY l.seq ASC`;\n if (opts.limit !== undefined) {\n sql += \" LIMIT ?\";\n params.push(opts.limit);\n }\n const rows = db.prepare(sql).all(...params) as RawLogRow[];\n return rows.map(rowFromDb);\n}\n\n/**\n * Return the latest seq currently in the table (or 0 if empty). Used\n * by `mu log --tail` to start the cursor at \"now\" so the subscriber\n * only sees NEW entries unless they explicitly pass `--since 0`.\n */\nexport function latestSeq(db: Db, workstream?: string): number {\n // MUST stay consistent with listLogs' row set — this is the cursor\n // INTO it. When the two disagreed (a filter here that listLogs did\n // not apply, or vice versa) `--tail` started past rows the non-tail\n // view had already shown and silently skipped them.\n const row =\n workstream === undefined\n ? (db.prepare(`SELECT MAX(seq) AS s FROM ops l WHERE ${TOUCH_OP_FILTER}`).get() as {\n s: number | null;\n })\n : (db\n .prepare(\n `SELECT MAX(seq) AS s FROM ops l WHERE ${workstreamScopeSql()} AND ${TOUCH_OP_FILTER}`,\n )\n .get(...workstreamScopeParams(workstream)) as { s: number | null });\n return row.s ?? 0;\n}\n\n/**\n * Record a change that NO capture trigger can see.\n *\n * The triggers in src/capture.ts cover the four **portable** tables\n * (workstreams, tasks, task_edges, task_notes), so every task and\n * workstream mutation is already an op with a real `intent` and a real\n * natural key. Anything that mutates one of those tables must NOT call\n * this — that was the duplication v2-retire-log-shim deleted: 13 call\n * sites each writing a second, prose, intent-less copy of a change the\n * trigger had already captured properly.\n *\n * What legitimately remains is state that lives OUTSIDE those tables:\n *\n * agent.* spawn / close / free / adopt / kick — `agents` is\n * machine-local (it holds `pane_id`), so there is no\n * trigger and never will be.\n * workspace.* create / free / refresh — `vcs_workspaces`\n * is machine-local (absolute paths).\n * agent.stall a pure observation; nothing is mutated.\n *\n * `intent` is REQUIRED (and typed), not optional, because the whole\n * point is that these rows render through the same formatter as\n * captured ops. An intent-less op cannot be rendered without\n * prefix-matching prose, which is the brittleness\n * (`classifyEventVerb`, `CLAIM_EVENT_PREFIX`) the ops log deletes.\n *\n * These entities are deliberately NOT in SYNCED_ENTITIES: every one of\n * them describes something about THIS machine (a pane id, a filesystem\n * path) that is meaningless on a peer. They are still recorded, so\n * `mu log` and the TUI show them locally.\n */\nexport function emitEvent(\n db: Db,\n workstream: string | null,\n intent: LocalIntent,\n payload: string,\n source = \"system\",\n): void {\n appendLog(db, { workstream, source, kind: entityForIntent(intent), payload, intent });\n}\n\n/**\n * Intents for changes no trigger can capture. Closed union rather than\n * `string` so a typo is a compile error and the set stays auditable —\n * `mu log`'s formatter (v2-log-verb) switches on exactly these.\n */\nexport type LocalIntent =\n | \"agent.spawn\"\n | \"agent.close\"\n | \"agent.adopt\"\n | \"agent.kick\"\n | \"agent.send\"\n | \"agent.stall\"\n | \"workspace.create\"\n | \"workspace.free\"\n | \"workspace.refresh\";\n\n/** Entity for a local intent: the token before the dot. Keeps\n * `entity`/`intent` consistent by construction instead of asking each\n * call site to pass both and agree. */\nfunction entityForIntent(intent: LocalIntent): string {\n const dot = intent.indexOf(\".\");\n return dot === -1 ? intent : intent.slice(0, dot);\n}\n\n// ─── claim attribution ─────────────────────────────────────────────\n//\n// mu once stored claim attribution as PROSE and re-parsed it: a\n// tab-delimited `task.claim<TAB><id><TAB>actor=<a><TAB>...` prefix was\n// bolted onto the payload precisely because prefix-matching the prose\n// was brittle (review_code_last_claim_actor_brittle). Two helpers then\n// existed only to put that prefix on and take it back off.\n//\n// The ops log makes all of it unnecessary. `withOpContext` seeds\n// _op_ctx.actor, the capture trigger copies it into `ops.actor`, and\n// the intent is `task.claim`. So attribution is two indexed columns,\n// not a string to parse — including on the `--self` path, where\n// tasks.owner_id stays NULL by design and the payload therefore\n// CANNOT name the actor. Reading ops.actor is the whole fix.\n\n/** The most recent `task.claim` op for a task: its actor and when.\n *\n * `ops.key` is the natural key, so the task's own ops are keyed\n * '<ws>/<localId>' exactly — no LIKE, no wildcard escaping, and no\n * way for `foo` to cross-match `foo_2`. Unbounded by design: the old\n * limit=100 ceiling silently dropped attribution on long-lived\n * workstreams. */\nfunction lastClaimOp(\n db: Db,\n workstream: string,\n localId: string,\n): { actor: string | null; created_at: string } | null {\n const row = db\n .prepare(\n `SELECT actor, created_at FROM ops\n WHERE key = ? AND intent = 'task.claim'\n ORDER BY seq DESC LIMIT 1`,\n )\n .get(`${workstream}/${localId}`) as { actor: string | null; created_at: string } | undefined;\n return row ?? null;\n}\n\n/**\n * Actor of the most recent claim of this task. Surfaces \"who is working\n * on this\" when `tasks.owner_id IS NULL` — the `--self` anonymous-claim\n * path. Null when the task was never claimed.\n */\nexport function lastClaimActor(db: Db, workstream: string, localId: string): string | null {\n return lastClaimOp(db, workstream, localId)?.actor ?? null;\n}\n\n/**\n * Find the `created_at` timestamp of the most recent claim of a task.\n *\n * Used by `mu task notes --since-claim` to slice the note timeline at\n * the most recent claim, so an operator dispatching a worker sees only\n * the post-claim notes (the spec was written before the claim; the\n * worker's progress lands after).\n *\n * Returns null when the task was never claimed — `--since-claim` then\n * degrades gracefully to no filter.\n */\nexport function lastClaimEventAt(db: Db, workstream: string, localId: string): string | null {\n return lastClaimOp(db, workstream, localId)?.created_at ?? null;\n}\n\n// ─── retired: prose verb classification ───────────────────────────────\n//\n// The old renderer needed EVENT_VERB_PREFIXES + classifyEventVerb + ClassifiedEvent +\n// logRowSubject to render a log line: every consumer prefix-matched a\n// payload's leading two words to find its verb, and CLAIM_EVENT_PREFIX\n// was bolted on because that matching kept breaking.\n//\n// v2-log-verb deletes all four. Rendering now reads `intent` (+ `key` +\n// named payload fields) in src/log-render.ts, which cannot be broken by\n// rewording a payload. If you find yourself wanting to string-match a\n// payload to decide what an op IS, that is the bug this removed.\n","// mu — the hybrid logical clock that orders every op (VOCABULARY § HLC).\n//\n// WHY NOT A WALL-CLOCK TIMESTAMP\n// ------------------------------\n// `ops` are merged across machines by last-writer-wins on `hlc`. If that\n// key were `new Date().toISOString()`, a laptop that sleeps for three\n// days and wakes with a clock skewed BEHIND the devserver would stamp\n// every new edit with a time that already passed — so on merge the\n// laptop's fresh work looks older than the devserver's stale work and is\n// silently discarded. No error, no conflict, just missing edits. That is\n// the #1 way homegrown sync corrupts data.\n//\n// An HLC uses wall time as a HINT and a logical counter as the truth:\n// the pair only ever increases, on this machine, forever. A backwards\n// clock jump costs ordering PRECISION (ops bunch up on one millisecond)\n// but never ordering CORRECTNESS.\n//\n// SERIALIZATION FORMAT\n// --------------------\n// A single lexicographically sortable TEXT, because `ORDER BY hlc` must\n// equal causal order and SQLite compares TEXT bytewise:\n//\n// <wall_ms:15 digits>.<counter:6 digits>.<machine_id>\n//\n// Example (wall=1780000000123, counter=7):\n//\n// 001780000000123.000007.9f1c8a2e-4b6d-4f0a-9c31-2f7c5d3e8a10\n//\n// - Both numeric parts are ZERO-PADDED to a fixed width. Without the\n// padding \"9\" would sort after \"10\" and the whole ordering guarantee\n// collapses.\n// - 15 digits of wall_ms covers every representable JS timestamp\n// (max 8_640_000_000_000_000 is 16 digits, but that is year 275760;\n// 15 digits runs to year 318857 — enough, and checked below).\n// - 6 digits of counter allows 1_000_000 ops inside one millisecond\n// before the clock advances. Overflow throws rather than wrapping,\n// because wrapping would silently regress the order.\n// - The separator is `.` — it cannot appear in a uuid (hex + `-` only),\n// so parsing is unambiguous and a machine_id can never spill into a\n// numeric field. It sorts below every digit and hex letter, so the\n// field boundary itself never perturbs the order.\n// - machine_id is the LAST field and is a tiebreak only: two machines\n// that mint the same (wall, counter) get a stable, arbitrary but\n// total order instead of comparing equal.\n//\n// PERSISTENCE\n// -----------\n// Every mu invocation is a separate short-lived process, so an\n// in-memory counter would reset constantly and mint duplicates. The\n// clock state lives in the singleton `machine_identity` row\n// (`last_wall`, `last_counter`), which is also where `machine_id`\n// already lives — one row, one read, one write.\n\nimport type { Db } from \"./db.js\";\n\n/** Zero-pad width for the wall-clock millisecond field. */\nconst WALL_WIDTH = 15;\n/** Zero-pad width for the logical counter field. */\nconst COUNTER_WIDTH = 6;\n/** Field separator. Cannot appear in a uuid. */\nconst SEP = \".\";\n/** Largest value each fixed-width field can hold. */\nconst MAX_WALL = 10 ** WALL_WIDTH - 1;\nconst MAX_COUNTER = 10 ** COUNTER_WIDTH - 1;\n\n/** A parsed HLC. Never construct by hand — use `parseHlc` or `formatHlc`. */\nexport interface Hlc {\n /** Wall-clock hint, milliseconds since the epoch. */\n wallMs: number;\n /** Logical counter; breaks ties within one `wallMs`. */\n counter: number;\n /** The machine that minted this HLC (`machine_identity.machine_id`). */\n machineId: string;\n}\n\n/** Thrown when a TEXT value is not a well-formed HLC. */\nexport class HlcParseError extends Error {\n constructor(readonly value: string) {\n super(`Malformed HLC: ${JSON.stringify(value)}`);\n this.name = \"HlcParseError\";\n }\n}\n\n/** Thrown when a field would not fit its fixed width. A counter\n * overflow means >1e6 ops landed in one millisecond; wrapping would\n * silently regress causal order, so we fail loudly instead. */\nexport class HlcOverflowError extends Error {\n constructor(\n readonly field: \"wall\" | \"counter\",\n readonly value: number,\n ) {\n super(\n field === \"counter\"\n ? `HLC counter overflow (${value} > ${MAX_COUNTER}): more than ${MAX_COUNTER + 1} ops in one millisecond`\n : `HLC wall-clock overflow (${value} > ${MAX_WALL}): system clock is far in the future`,\n );\n this.name = \"HlcOverflowError\";\n }\n}\n\n/** Thrown when the singleton `machine_identity` row is missing. */\nexport class MachineIdentityMissingError extends Error {\n constructor() {\n super(\"machine_identity row (id = 1) is missing; the DB was not opened via openDb()\");\n this.name = \"MachineIdentityMissingError\";\n }\n}\n\n/** Serialize `(wall_ms, counter, machine_id)` to the sortable TEXT form.\n * See the module comment for the exact shape and an example. */\nexport function formatHlc(hlc: Hlc): string {\n const { wallMs, counter, machineId } = hlc;\n if (!Number.isInteger(wallMs) || wallMs < 0) throw new HlcParseError(String(wallMs));\n if (!Number.isInteger(counter) || counter < 0) throw new HlcParseError(String(counter));\n if (wallMs > MAX_WALL) throw new HlcOverflowError(\"wall\", wallMs);\n if (counter > MAX_COUNTER) throw new HlcOverflowError(\"counter\", counter);\n if (machineId.length === 0 || machineId.includes(SEP)) throw new HlcParseError(machineId);\n return `${String(wallMs).padStart(WALL_WIDTH, \"0\")}${SEP}${String(counter).padStart(\n COUNTER_WIDTH,\n \"0\",\n )}${SEP}${machineId}`;\n}\n\n/** Inverse of `formatHlc`. Throws `HlcParseError` on anything else —\n * including the legacy placeholder `<iso>|<uuid>` shape. */\nexport function parseHlc(value: string): Hlc {\n const parts = value.split(SEP);\n if (parts.length !== 3) throw new HlcParseError(value);\n const [wall, counter, machineId] = parts;\n if (wall === undefined || counter === undefined || machineId === undefined) {\n throw new HlcParseError(value);\n }\n if (wall.length !== WALL_WIDTH || counter.length !== COUNTER_WIDTH || machineId.length === 0) {\n throw new HlcParseError(value);\n }\n if (!/^\\d+$/.test(wall) || !/^\\d+$/.test(counter)) throw new HlcParseError(value);\n return { wallMs: Number(wall), counter: Number(counter), machineId };\n}\n\n/** Total order over serialized HLCs: -1 / 0 / 1. Identical to bytewise\n * string comparison (that is the whole point of the format), so\n * `ORDER BY hlc` in SQL and `.sort(compareHlc)` in JS agree. */\nexport function compareHlc(a: string, b: string): -1 | 0 | 1 {\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n}\n\ninterface ClockRow {\n machine_id: string;\n last_wall: number;\n last_counter: number;\n}\n\n/**\n * Mint the next HLC for this machine and persist the advance.\n *\n * now = wall clock ms\n * if now > last_wall: wall = now, counter = 0\n * else: wall = last_wall, counter = last_counter + 1\n *\n * The `else` branch is the monotonicity guarantee: when the clock\n * stalls, jumps backwards, or two ops land in the same millisecond, the\n * counter carries the order instead.\n *\n * ATOMICITY — a SINGLE `UPDATE … RETURNING` statement, not an explicit\n * transaction. SQLite makes one statement atomic and takes the write\n * lock for its duration, so the read-modify-write cannot interleave\n * with a competing `mu` process; `busy_timeout = 5000` (set by openDb)\n * makes the loser wait rather than fail. A read-then-write pair inside\n * BEGIN DEFERRED would be upgrade-deadlock prone under the parallel\n * spawn fan-out, and BEGIN IMMEDIATE would be a strictly bigger lock\n * for the same effect. `receiveHlc` cannot use this trick (its\n * three-way max is not expressible as one clean statement) so it does\n * take BEGIN IMMEDIATE.\n *\n * @param now Injectable clock, for tests. Defaults to `Date.now()`.\n */\nexport function nextHlc(db: Db, now: number = Date.now()): string {\n const row = db\n .prepare(\n `UPDATE machine_identity\n SET last_wall = MAX(last_wall, @now),\n last_counter = CASE WHEN @now > last_wall THEN 0 ELSE last_counter + 1 END\n WHERE id = 1\n RETURNING machine_id, last_wall, last_counter`,\n )\n .get({ now: Math.floor(now) }) as ClockRow | undefined;\n if (!row) throw new MachineIdentityMissingError();\n return formatHlc({ wallMs: row.last_wall, counter: row.last_counter, machineId: row.machine_id });\n}\n\n/**\n * Advance the local clock past a peer's HLC while INGESTING their op,\n * and return the local HLC that now dominates it. This is what makes\n * \"laptop edits after seeing the devserver's op\" order correctly: the\n * laptop's next mint is guaranteed greater than anything it has seen,\n * even if its own wall clock is days behind.\n *\n * wall = max(local_wall, remote_wall, now)\n *\n * The counter has four explicit cases, by which of the three won:\n * - `now` strictly won -> counter = 0 (fresh millisecond)\n * - local and remote tie at max -> counter = max(local_c, remote_c) + 1\n * - only local is at max -> counter = local_c + 1\n * - only remote is at max -> counter = remote_c + 1\n *\n * A remote HLC from the PAST therefore never drags the local clock\n * backwards — `max` keeps `local_wall`, and the counter still steps.\n *\n * ATOMICITY — BEGIN IMMEDIATE (`.immediate()`), because the three-way\n * max needs the old row in JS before the new value can be computed, so\n * unlike `nextHlc` it genuinely is a read-then-write pair.\n *\n * @param now Injectable clock, for tests. Defaults to `Date.now()`.\n */\nexport function receiveHlc(db: Db, remoteHlc: string, now: number = Date.now()): string {\n const remote = parseHlc(remoteHlc);\n const wallNow = Math.floor(now);\n const read = db.prepare(\n \"SELECT machine_id, last_wall, last_counter FROM machine_identity WHERE id = 1\",\n );\n const write = db.prepare(\n \"UPDATE machine_identity SET last_wall = @wall, last_counter = @counter WHERE id = 1\",\n );\n\n const advance = db.transaction((): string => {\n const row = read.get() as ClockRow | undefined;\n if (!row) throw new MachineIdentityMissingError();\n const wall = Math.max(row.last_wall, remote.wallMs, wallNow);\n const localAtMax = row.last_wall === wall;\n const remoteAtMax = remote.wallMs === wall;\n let counter: number;\n if (!localAtMax && !remoteAtMax) {\n counter = 0; // `now` strictly won: a genuinely fresh millisecond.\n } else if (localAtMax && remoteAtMax) {\n counter = Math.max(row.last_counter, remote.counter) + 1;\n } else if (localAtMax) {\n counter = row.last_counter + 1;\n } else {\n counter = remote.counter + 1;\n }\n if (counter > MAX_COUNTER) throw new HlcOverflowError(\"counter\", counter);\n write.run({ wall, counter });\n return formatHlc({ wallMs: wall, counter, machineId: row.machine_id });\n });\n\n return advance.immediate();\n}\n","// Historical op shapes remain readable after their command surface is removed.\n// These intents carried prose payloads under otherwise projectable entities.\nexport const LEGACY_LOG_ONLY_INTENTS: ReadonlySet<string> = new Set([\"workstream.export\"]);\n\nexport function isLegacyLogOnlyIntent(intent: string | null): boolean {\n return intent !== null && LEGACY_LOG_ONLY_INTENTS.has(intent);\n}\n\n/**\n * The same exclusion as a SQL predicate, for queries that must not even\n * TOUCH these rows.\n *\n * A JS-side filter is not always enough: SQLite's `json_type` /\n * `json_extract` raise `malformed JSON` while STEPPING, so a query that\n * merely evaluates a json function over one of these prose payloads\n * fails before any row reaches JS. Callers that read payloads by key\n * therefore have to exclude them in the WHERE clause.\n *\n * Kept next to the set it derives from so the two can never disagree.\n * Emitted as a literal rather than a bound parameter because it is\n * interpolated into prepared SQL; every member is a fixed identifier in\n * this file, never operator input.\n */\nexport const LEGACY_LOG_ONLY_SQL_EXCLUSION: string = `(intent IS NULL OR intent NOT IN (${[\n ...LEGACY_LOG_ONLY_INTENTS,\n]\n .map((i) => `'${i}'`)\n .join(\", \")}))`;\n\n/**\n * Intents renamed after ops carrying the old name were already on\n * disk. Key = current name, value = every historical spelling.\n *\n * The op log is append-only, so a rename cannot rewrite history: the\n * old string stays on the rows it was written to, forever. Everything\n * that READS by intent therefore has to accept both, or a rename\n * silently truncates the user's history at the release boundary —\n * `mu log --intent <new>` would miss every pre-rename op and\n * `mu undo` would not find those groups.\n *\n * `workstream.destroy` became `workstream.teardown` in 1.2.0 because\n * \"destroy\" claims irreversibility that is not true (tombstones are\n * written; `mu undo` restores the rows) and operators were hoarding\n * pre-flight DB copies because of it. ~5k ops carry the old name.\n */\nexport const LEGACY_INTENT_SYNONYMS: ReadonlyMap<string, readonly string[]> = new Map([\n [\"workstream.teardown\", [\"workstream.destroy\"]],\n]);\n\n/** Every spelling of `intent`, current first. A single-element list for\n * the common case of an intent that was never renamed. */\nexport function intentSpellings(intent: string): readonly string[] {\n const legacy = LEGACY_INTENT_SYNONYMS.get(intent);\n return legacy === undefined ? [intent] : [intent, ...legacy];\n}\n","// mu — multiplexer backend hub.\n//\n// Concrete backends live in the cohesive src/mux/ cluster: one file per\n// backend plus shared types and detection. This root file is the public\n// `import { ... } from \"./mux.js\"` surface, mirroring src/vcs.ts.\n//\n// A \"mux\" is the terminal multiplexer that owns panes. Exactly one is\n// active per mu invocation, resolved by `activeMux()`. Everything\n// backend-specific — topology, the send protocol, capture, pane-id\n// validation, identity fallback — lives behind `MuxBackend`.\n//\n// See docs/VOCABULARY.md § \"mux backend\" and docs/architecture/mux.md\n// § \"One mux session per workstream\".\n\nexport {\n type AttachTarget,\n activeMux,\n type CaptureOptions,\n detectMux,\n HerdrCommandOverrideError,\n HerdrError,\n HerdrNotImplementedError,\n HerdrSyntaxError,\n HerdrUnsupportedCliError,\n HerdrWorkspaceGroupCloseError,\n herdrBackend,\n isHerdrStatusUsable,\n type MuxBackend,\n type MuxBackendName,\n type MuxCommand,\n type MuxDiagnostics,\n MuxError,\n type MuxHealth,\n type MuxPane,\n type MuxPaneStatus,\n type MuxSession,\n type MuxWindow,\n muxByName,\n type NewSessionOptions,\n type NewSessionWithPaneOptions,\n type NewWindowOptions,\n NoMultiplexerError,\n PaneNotFoundError,\n parseAgentNameFromTitle,\n resetHerdrExecutor,\n resetMux,\n type SendOptions,\n type SendWarning,\n type SplitWindowOptions,\n type StartAgentInPaneOptions,\n setHerdrExecutor,\n setMuxForTests,\n tmuxBackend,\n} from \"./mux/index.js\";\n","// mu — herdr substrate (TOPOLOGY half).\n//\n// Single source of truth for all herdr interactions. Every herdr\n// invocation goes through `herdr(args)`, which wraps execa and produces\n// structured `HerdrError`s carrying args + the server's JSON error.\n//\n// ─── The mapping ──────────────────────────────────────────────────────\n//\n// herdr's own \"session\" is SERVER-level (one socket per named session),\n// so it is NOT the workstream unit. The locked mapping (see\n// docs/architecture/mux.md § \"One mux session per workstream\") is:\n//\n// mu workstream = herdr WORKSPACE, labelled `mu-<name>`\n// mu window = herdr TAB\n// mu agent = herdr PANE\n//\n// mu addresses workspaces BY LABEL (`MuxSession.name`), because that is\n// the only handle mu persists. herdr addresses them by opaque id (`w1`).\n// Every session-taking method therefore resolves label → id first; see\n// `resolveWorkspaceId`.\n//\n// ─── Protocol notes ───────────────────────────────────────────────────\n//\n// - Every command emits JSON on stdout under `.result`. IDs are READ\n// from creation responses, never predicted or constructed. herdr's\n// skill doc is explicit about this and closed ids are not reused.\n// - `--no-focus` on EVERY mutating call. mu must never steal the\n// user's focus out from under them.\n// - Server errors are JSON on STDERR with exit 1 → `HerdrError`\n// (a `MuxError`, exit 5: the substrate said no).\n// - SYNTAX errors exit 2 → `HerdrSyntaxError`, which is deliberately\n// NOT a `MuxError`. A herdr CLI that renamed a flag under us is a\n// BUG IN MU, and must not masquerade as \"herdr is down\".\n//\n// ─── Scope ────────────────────────────────────────────────────────────\n//\n// This module implements ALL of `MuxBackend` for herdr: sessions,\n// windows, panes, pane-id validation, availability, identity, send,\n// capture, native pane status, and the two-step spawn\n// (`startAgentInPane`).\n//\n// ─── Two exceptions to \"everything is JSON\" ───────────────────────────\n//\n// `herdr status` and `herdr pane read` both emit PLAIN TEXT. They go\n// through `currentExecutor` directly rather than `herdr()`, which would\n// reject their unparseable stdout as CLI drift.\n//\n// ─── Why spawn is two steps ───────────────────────────────────────────\n//\n// herdr has NO create-and-run form: `workspace create` / `tab create` /\n// `pane split` always start a plain shell, and `agent start` never\n// creates, splits or moves layout — it requires an ALREADY-EXISTING\n// pane sitting at its interactive prompt. So the creation verbs below\n// REFUSE a non-empty `opts.command` (silently dropping it would leave\n// an empty shell mu believes hosts an agent — the worst failure mode),\n// and `startAgentInPane` is the second step mu calls afterwards.\n// See `MuxBackend.startAgentInPane` for how spawn.ts branches on it.\n\nimport { execa } from \"execa\";\nimport type { NextStep } from \"../output.js\";\nimport { sleep } from \"./tmux.js\";\nimport {\n type AttachTarget,\n type CaptureOptions,\n type MuxBackend,\n type MuxCommand,\n MuxError,\n type MuxHealth,\n type MuxPane,\n type MuxPaneStatus,\n type MuxSession,\n type MuxWindow,\n type NewSessionOptions,\n type NewSessionWithPaneOptions,\n type NewWindowOptions,\n PaneNotFoundError,\n type SendOptions,\n type SendWarning,\n type SplitWindowOptions,\n type StartAgentInPaneOptions,\n} from \"./types.js\";\n\n// ─── Error types ───────────────────────────────────────────────────────\n\n/**\n * \"herdr itself failed\" — the server returned a JSON error, or the\n * binary could not be reached. A `MuxError`, so `handle()` maps the\n * whole family to exit 5 with one `instanceof`. Mirrors `TmuxError`.\n */\nexport class HerdrError extends MuxError {\n constructor(\n public readonly args: readonly string[],\n public readonly stderr: string,\n public readonly stdout: string,\n public readonly exitCode: number | null,\n /** herdr's machine-readable error code (e.g. `pane_not_found`), when\n * the stderr payload parsed as the documented JSON envelope. */\n public readonly code?: string,\n ) {\n const detail = parseErrorEnvelope(stderr)?.message ?? (stderr.trim() || stdout.trim());\n super(`herdr ${args.join(\" \")} failed (exit ${exitCode}): ${detail || \"no output\"}`);\n this.name = \"HerdrError\";\n }\n override errorNextSteps(): NextStep[] {\n return [\n { intent: \"Run health check\", command: \"mu doctor\" },\n { intent: \"Verify the herdr server is running\", command: \"herdr status\" },\n {\n intent: \"Check the failing herdr command in isolation\",\n command: `herdr ${this.args.join(\" \")}`,\n },\n ];\n }\n}\n\n/**\n * herdr rejected our ARGUMENTS (exit 2). Deliberately not a `MuxError`:\n * the substrate is healthy and mu asked it something ungrammatical.\n * That is a bug in this file — most likely CLI drift after a herdr\n * upgrade — and must surface as one instead of being swallowed into\n * the \"herdr is down\" bucket, where it would waste hours.\n */\nexport class HerdrSyntaxError extends Error {\n override readonly name = \"HerdrSyntaxError\";\n constructor(\n public readonly args: readonly string[],\n public readonly output: string,\n ) {\n super(\n `herdr rejected the command line (exit 2) — this is a bug in mu, not a herdr outage. ` +\n `Ran: herdr ${args.join(\" \")}. herdr said: ${output.trim().split(\"\\n\")[0] ?? \"(no output)\"}`,\n );\n }\n}\n\n/**\n * `mu agent spawn --cli <key>` named something herdr does not recognise\n * as an interactive agent kind.\n *\n * A REFUSAL, not a fallback. herdr's `pane run <command>` would happily\n * start the binary, but herdr would then never classify that pane, and\n * on this backend herdr's report is mu's runtime state source. A pane\n * mu can start but never observe is the same family of failure as a pane mu records\n * with nothing running in it: it looks fine and lies forever.\n *\n * Not a `MuxError`: the substrate is healthy and answered precisely.\n * This is an operator asking for something the active backend cannot\n * do, which `handle()` maps to exit 2 alongside the usage family.\n *\n * herdr is the AUTHORITY on the kind list — mu does not hardcode one,\n * it forwards `--cli` and translates herdr's rejection. A herdr release\n * that adds a kind therefore works without a mu change.\n */\nexport class HerdrUnsupportedCliError extends Error {\n override readonly name = \"HerdrUnsupportedCliError\";\n constructor(\n public readonly cli: string,\n public readonly detail: string,\n ) {\n super(\n `herdr does not recognise --cli ${cli} as an interactive agent kind, so it cannot classify that pane's status. herdr said: ${detail}`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"List the agent kinds this herdr knows\",\n command: \"herdr agent start --help\",\n },\n {\n intent: \"Spawn with a kind herdr supports\",\n command: \"mu agent spawn <name> --cli pi -w <workstream>\",\n },\n {\n intent: \"Or spawn this cli under tmux, which runs any command\",\n command: `MU_MUX=tmux mu agent spawn <name> --cli ${this.cli}`,\n },\n ];\n }\n}\n\n/**\n * The operator pinned an exact command line — `--command \"…\"` or\n * `MU_<UPPER_CLI>_COMMAND` — and herdr cannot honour it.\n *\n * `herdr agent start --kind <k>` resolves the canonical executable\n * ITSELF; there is no \"use this binary instead\" flag. Args after `--`\n * are passed to the agent, NOT used to choose it (verified against\n * 0.8.0: `agent start x --kind pi -- --model m` reports `argv:[\"pi\"]`;\n * 0.9.0 still exposes no override flag), so forwarding an override\n * there would append a binary\n * NAME as an argument and start the wrong thing while reporting success.\n *\n * Accepting the override and not honouring it is the one outcome ruled\n * out, so mu refuses and names the variable.\n */\nexport class HerdrCommandOverrideError extends Error {\n override readonly name = \"HerdrCommandOverrideError\";\n constructor(\n public readonly cli: string,\n public readonly command: string,\n /** The `MU_<UPPER_CLI>_COMMAND` var, when that is where the override\n * came from; undefined for an explicit `--command`. */\n public readonly envVar?: string,\n ) {\n super(\n (envVar === undefined\n ? `--command ${JSON.stringify(command)} cannot be honoured on the herdr backend`\n : `${envVar}=${command} cannot be honoured on the herdr backend`) +\n `: 'herdr agent start --kind ${cli}' resolves the executable itself and has no override flag. ` +\n `mu refuses rather than starting a different binary than you asked for.`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n ...(this.envVar === undefined\n ? [\n {\n intent: \"Spawn without --command so herdr resolves the agent itself\",\n command: `mu agent spawn <name> --cli ${this.cli} -w <workstream>`,\n },\n ]\n : [\n {\n intent: `Unset the override so herdr resolves ${this.cli} itself`,\n command: `unset ${this.envVar}`,\n },\n ]),\n {\n intent: \"Or spawn under tmux, which runs the command you name\",\n command: `MU_MUX=tmux mu agent spawn <name> --cli ${this.cli}`,\n },\n ];\n }\n}\n\n/** herdr's refusal (0.9.0+) to close a workspace that has linked\n * worktree workspaces without explicit group intent. */\nconst GROUP_CLOSE_REQUIRED_CODE = \"workspace_group_close_required\";\n\n/**\n * `mu workstream destroy` asked herdr to close the workstream's\n * workspace, and herdr refused because worktree workspaces are linked\n * to it (herdr 0.9.0+).\n *\n * mu does NOT retry with `--group`. Those sibling workspaces are not\n * mu's: they were created by `herdr worktree`, they host panes mu never\n * spawned, and mu has no way to know whether the user still wants them.\n * Closing them to satisfy a teardown would destroy unsaved work with no\n * undo, which is the one outcome ruled out.\n *\n * Not a `MuxError`: the substrate is healthy and answered precisely.\n * This is a decision only the operator can make, so it lands in the\n * usage lane (exit 2) beside the other herdr refusals. Thrown BEFORE\n * any DB rows are deleted — `teardownWorkstream` kills the mux session\n * first for exactly this reason — so the workstream is left intact and\n * the command is safe to re-run.\n */\nexport class HerdrWorkspaceGroupCloseError extends Error {\n override readonly name = \"HerdrWorkspaceGroupCloseError\";\n constructor(\n public readonly session: string,\n public readonly workspaceId: string,\n public readonly detail: string,\n ) {\n super(\n `herdr refused to close workspace ${workspaceId} (${session}): it has linked worktree workspaces, ` +\n `which mu did not create and will not close on your behalf. herdr said: ${detail}`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"See which workspaces are linked as a group\",\n command: \"herdr workspace list\",\n },\n {\n intent: \"Remove the worktree workspaces you no longer want\",\n command: \"herdr worktree remove <workspace-id>\",\n },\n {\n intent: \"Or close the whole group yourself, then re-run the teardown\",\n command: `herdr workspace close ${this.workspaceId} --group`,\n },\n ];\n }\n}\n\n/** A creation verb was handed a command to run. herdr cannot do that\n * in one step (see `rejectCommand`), so this is a bug in the caller:\n * spawn passes no command on herdr and calls `startAgentInPane`. */\nexport class HerdrNotImplementedError extends Error {\n override readonly name = \"HerdrNotImplementedError\";\n constructor(method: string) {\n super(\n `herdr backend: ${method} is not supported: herdr creation verbs always start a plain shell; ` +\n \"create the pane without a command, then start the agent with startAgentInPane\",\n );\n }\n}\n\n// ─── Pane / workspace / tab ID validation ──────────────────────────────\n//\n// herdr's public ids are opaque stable handles with a documented shape:\n// workspace `w1`, tab `w1:t1`, pane `w1:p1`. Pane ids are workspace-\n// qualified, so a pane MOVED between workspaces gets a NEW id — mu must\n// re-read it from `.result.move_result.pane.pane_id`, never reuse the old.\n//\n// This is exactly why `isValidPaneId` is a backend method: a tmux `%15`\n// must be rejected here, and a herdr `w1:p1` must be rejected by tmux.\n//\n// The ORDINALS ARE NOT DECIMAL. Verified on herdr 0.8.0 by opening 38\n// panes: the sequence runs `p1 … p9, pA, pB, … pH, pJ, pK, pM, pN, pP …\n// pZ, p10, p11 …` — Crockford base32 (digits plus A-Z minus I, L, O, U).\n// Workspace and tab ordinals use the same alphabet (`wA` after `w9`).\n// A decimal-only pattern therefore works for exactly nine panes and then\n// starts rejecting live ids, which on the spawn path is a TypeError on\n// the 10th agent in a workstream. Matched as an opaque `[0-9A-Z]+` run\n// rather than by spelling out the alphabet: mu's job is to tell a herdr\n// id from a tmux one, not to re-derive herdr's numbering.\n\nconst HERDR_ORDINAL = \"[0-9A-Z]+\";\nexport const PANE_ID_RE = new RegExp(`^w${HERDR_ORDINAL}:p${HERDR_ORDINAL}$`);\nconst WORKSPACE_ID_RE = new RegExp(`^w${HERDR_ORDINAL}$`);\nconst TAB_ID_RE = new RegExp(`^w${HERDR_ORDINAL}:t${HERDR_ORDINAL}$`);\n\nexport function isValidPaneId(s: string): boolean {\n return PANE_ID_RE.test(s);\n}\n\nexport function assertValidPaneId(s: string): void {\n if (!isValidPaneId(s)) {\n throw new TypeError(\n `invalid herdr pane id: ${JSON.stringify(s)} (expected ${PANE_ID_RE.source})`,\n );\n }\n}\n\n// ─── Executor (swappable for tests) ────────────────────────────────────\n\nexport interface HerdrExecResult {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n}\n\nexport type HerdrExecutor = (args: readonly string[]) => Promise<HerdrExecResult>;\n\n/**\n * Optional `--session <name>` prefix spliced in front of every args\n * vector. When `MU_HERDR_SESSION=<name>` is set, mu drives a NAMED\n * herdr server (its own socket under\n * `~/.config/herdr/sessions/<name>/herdr.sock`) instead of the user's\n * default one. Mirrors `MU_TMUX_SOCKET`: it is the isolation seam any\n * integration test must use so the suite can never observe — or\n * destroy — the user's real panes.\n *\n * Read fresh on every call so a setup hook mutating the env mid-run\n * takes effect immediately.\n */\nfunction herdrGlobalFlags(): readonly string[] {\n const session = process.env.MU_HERDR_SESSION;\n if (session === undefined || session.length === 0) return [];\n return [\"--session\", session];\n}\n\nconst realExecutor: HerdrExecutor = async (args) => {\n const result = await execa(\"herdr\", [...herdrGlobalFlags(), ...args], { reject: false });\n return {\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? \"\",\n exitCode: result.exitCode ?? null,\n };\n};\n\nlet currentExecutor: HerdrExecutor = realExecutor;\n\n/**\n * Install a custom executor (for tests). Returns the previous executor\n * so tests can restore it cleanly. Production code never calls this.\n * Required: the fast test tier may not shell out.\n */\nexport function setHerdrExecutor(executor: HerdrExecutor): HerdrExecutor {\n const previous = currentExecutor;\n currentExecutor = executor;\n return previous;\n}\n\n/** Restore the real (execa-backed) executor. */\nexport function resetHerdrExecutor(): void {\n currentExecutor = realExecutor;\n}\n\n// ─── JSON envelope ─────────────────────────────────────────────────────\n\n/** herdr's error payload: `{\"error\":{\"code\":\"…\",\"message\":\"…\"},\"id\":\"…\"}`. */\ninterface HerdrErrorEnvelope {\n code: string;\n message: string;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" ? v : undefined;\n}\n\nfunction parseErrorEnvelope(stderr: string): HerdrErrorEnvelope | undefined {\n const trimmed = stderr.trim();\n if (trimmed.length === 0) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n return undefined;\n }\n if (!isRecord(parsed)) return undefined;\n const err = parsed.error;\n if (!isRecord(err)) return undefined;\n const code = asString(err.code);\n const message = asString(err.message);\n if (code === undefined) return undefined;\n return { code, message: message ?? code };\n}\n\n/**\n * Run a herdr command and return the parsed `.result` object.\n *\n * The single point of contact with the herdr binary; every higher-level\n * operation in this module goes through it.\n *\n * Throws `HerdrSyntaxError` on exit 2 (mu bug), `HerdrError` on any\n * other non-zero exit (substrate failure).\n */\nexport async function herdr(args: readonly string[]): Promise<Record<string, unknown>> {\n const result = await currentExecutor(args);\n if (result.exitCode === 2) {\n throw new HerdrSyntaxError([...args], result.stderr || result.stdout);\n }\n if (result.exitCode !== 0) {\n throw new HerdrError(\n [...args],\n result.stderr,\n result.stdout,\n result.exitCode,\n parseErrorEnvelope(result.stderr)?.code,\n );\n }\n const trimmed = result.stdout.trim();\n if (trimmed.length === 0) return {};\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n // A zero exit with unparseable stdout means the CLI stopped speaking\n // JSON — same class of problem as exit 2, but we cannot prove it is\n // ours, so keep it in the substrate bucket.\n throw new HerdrError([...args], result.stderr, result.stdout, result.exitCode);\n }\n if (!isRecord(parsed)) return {};\n const inner = parsed.result;\n return isRecord(inner) ? inner : {};\n}\n\n/** Like `herdr()` but returns undefined instead of throwing when the\n * server's error code is one of `tolerate`. Used for the idempotent\n * destructive verbs (\"succeeds even if it is already gone\"). */\nasync function herdrTolerating(\n args: readonly string[],\n tolerate: readonly string[],\n): Promise<Record<string, unknown> | undefined> {\n try {\n return await herdr(args);\n } catch (err) {\n if (err instanceof HerdrError && err.code !== undefined && tolerate.includes(err.code)) {\n return undefined;\n }\n throw err;\n }\n}\n\n// ─── Response shapes ───────────────────────────────────────────────────\n//\n// Narrow readers over herdr's JSON: they pull only the fields mu uses\n// and drop anything malformed, so one odd row never poisons a listing.\n\ninterface HerdrWorkspace {\n workspaceId: string;\n label: string;\n}\n\nfunction readWorkspace(v: unknown): HerdrWorkspace | undefined {\n if (!isRecord(v)) return undefined;\n const workspaceId = asString(v.workspace_id);\n if (workspaceId === undefined) return undefined;\n return { workspaceId, label: asString(v.label) ?? \"\" };\n}\n\nfunction readTab(v: unknown): MuxWindow | undefined {\n if (!isRecord(v)) return undefined;\n const id = asString(v.tab_id);\n if (id === undefined) return undefined;\n // herdr auto-labels a tab with its ordinal (\"1\"), which is the closest\n // analogue to a tmux window name; mu labels every tab it creates.\n return { id, name: asString(v.label) ?? \"\" };\n}\n\nfunction readPane(v: unknown): MuxPane | undefined {\n if (!isRecord(v)) return undefined;\n const paneId = asString(v.pane_id);\n if (paneId === undefined) return undefined;\n return {\n paneId,\n // herdr's optional `label` is what `pane rename` writes and is\n // therefore mu's pane-title equivalent.\n title: asString(v.label) ?? \"\",\n // herdr reports no foreground command on `pane list` (that costs a\n // `pane process-info` round trip per pane), but it does report the\n // agent kind it detected (`pi`, `claude`, `codex`, ...). That is\n // what `command` is used for: reconcile's orphan recogniser matches\n // it against the agent CLIs. Empty for a plain shell.\n command: asString(v.agent) ?? \"\",\n windowId: asString(v.tab_id),\n };\n}\n\nfunction readArray(result: Record<string, unknown>, key: string): unknown[] {\n const v = result[key];\n return Array.isArray(v) ? v : [];\n}\n\n// ─── Workspaces (= mu sessions) ────────────────────────────────────────\n\nasync function listWorkspaces(): Promise<HerdrWorkspace[]> {\n const result = await herdr([\"workspace\", \"list\"]);\n const out: HerdrWorkspace[] = [];\n for (const raw of readArray(result, \"workspaces\")) {\n const ws = readWorkspace(raw);\n if (ws) out.push(ws);\n }\n return out;\n}\n\nexport async function listSessions(): Promise<MuxSession[]> {\n return (await listWorkspaces()).map((ws) => ({ name: ws.label }));\n}\n\n/**\n * Resolve a mu session name (= herdr workspace LABEL) to herdr's opaque\n * workspace id. Returns undefined when no workspace carries that label.\n *\n * Labels are not unique in herdr — an unlabelled `workspace create`\n * defaults the label to the cwd's basename, so a user could plausibly\n * have two. mu only ever creates `mu-<workstream>` labels and refuses\n * duplicate workstream names, so the first match is the right one; the\n * ambiguity is inherent to addressing by label and is why every mutating\n * path resolves once and then works in ids.\n */\nasync function resolveWorkspaceId(name: string): Promise<string | undefined> {\n for (const ws of await listWorkspaces()) {\n if (ws.label === name) return ws.workspaceId;\n }\n return undefined;\n}\n\n/** Resolve or throw. Used by paths where a missing workspace is a real\n * substrate failure rather than an expected \"already gone\". */\nasync function requireWorkspaceId(name: string): Promise<string> {\n const id = await resolveWorkspaceId(name);\n if (id === undefined) {\n throw new HerdrError(\n [\"workspace\", \"list\"],\n `{\"error\":{\"code\":\"workspace_not_found\",\"message\":\"no herdr workspace labelled ${name}\"}}`,\n \"\",\n 1,\n \"workspace_not_found\",\n );\n }\n return id;\n}\n\nexport async function sessionExists(name: string): Promise<boolean> {\n return (await resolveWorkspaceId(name)) !== undefined;\n}\n\n/**\n * Create a herdr workspace labelled `name`.\n *\n * `opts.detached` is not a knob on herdr: mu ALWAYS passes `--no-focus`,\n * because stealing the user's focus is the one thing a background agent\n * manager must never do. A caller asking for `detached: false` gets a\n * detached workspace anyway; use `workspace focus` explicitly if you\n * really mean to move the user.\n *\n * `opts.windowName` labels the implicit first tab (see `labelRootTab`).\n */\nexport async function newSession(name: string, opts: NewSessionOptions = {}): Promise<void> {\n rejectCommand(\"newSession\", opts.command);\n const args = [\"workspace\", \"create\", \"--label\", name, \"--no-focus\"];\n if (opts.cwd) args.push(\"--cwd\", opts.cwd);\n appendEnvFlags(args, opts.env);\n await labelRootTab(await herdr(args), opts.windowName);\n}\n\n/**\n * Create a workspace and return its root pane's id.\n *\n * `opts.command` cannot be honoured here: `workspace create` always\n * starts a plain shell. Spawn runs the agent afterwards with\n * `startAgentInPane` (see `rejectCommand`).\n */\nexport async function newSessionWithPane(\n name: string,\n opts: NewSessionWithPaneOptions,\n): Promise<string> {\n rejectCommand(\"newSessionWithPane\", opts.command);\n const args = [\"workspace\", \"create\", \"--label\", name, \"--no-focus\"];\n if (opts.cwd) args.push(\"--cwd\", opts.cwd);\n appendEnvFlags(args, opts.env);\n const result = await herdr(args);\n const paneId = readCreatedPaneId(result, \"root_pane\", args);\n await labelRootTab(result, opts.windowName);\n return paneId;\n}\n\n/**\n * herdr labels a new workspace's implicit first tab \"1\". mu finds tabs\n * by window name (attach focuses the agent's tab, spawn reuses a tab by\n * `--tab`, init checks for `_mu`), so rename it to `windowName`, the\n * label `newWindow` gives every later tab. Load-bearing: an unlabelled\n * first tab is invisible to all three lookups. On failure the new\n * workspace is closed before the error propagates.\n */\nasync function labelRootTab(\n created: Record<string, unknown>,\n windowName: string | undefined,\n): Promise<void> {\n if (windowName === undefined || windowName.length === 0) return;\n const root = created.root_pane;\n const tab = created.tab;\n const tabId =\n (isRecord(root) ? asString(root.tab_id) : undefined) ??\n (isRecord(tab) ? asString(tab.tab_id) : undefined);\n try {\n if (tabId === undefined) {\n throw new HerdrError(\n [\"workspace\", \"create\"],\n `herdr response had no root tab id: ${JSON.stringify(created)}`,\n \"\",\n 0,\n );\n }\n await herdr([\"tab\", \"rename\", tabId, windowName]);\n } catch (err) {\n await closeCreatedWorkspace(created);\n throw err;\n }\n}\n\n/**\n * Best-effort rollback for a workspace this call just created: the caller\n * never got a pane id, so spawn's rollback cannot reach it. The original\n * error is what the caller must see, so a failed close is swallowed.\n */\nasync function closeCreatedWorkspace(created: Record<string, unknown>): Promise<void> {\n const ws = created.workspace;\n const root = created.root_pane;\n const id =\n (isRecord(ws) ? asString(ws.workspace_id) : undefined) ??\n (isRecord(root) ? asString(root.workspace_id) : undefined);\n if (id === undefined) return;\n try {\n await herdrTolerating([\"workspace\", \"close\", id], [\"workspace_not_found\"]);\n } catch {\n // Best effort; the rename error propagates.\n }\n}\n\n/**\n * Idempotent: succeeds even if the workspace is already gone.\n *\n * REFUSES, and does not retry with `--group`, when herdr reports\n * `workspace_group_close_required` (0.9.0+): the workspace has linked\n * worktree workspaces mu did not create, and closing the whole group\n * would take down panes outside this workstream. herdr's own skill doc\n * calls adding `--group` to bypass that error out by name. The operator\n * decides; mu names the command.\n */\nexport async function killSession(name: string): Promise<void> {\n const id = await resolveWorkspaceId(name);\n if (id === undefined) return;\n try {\n await herdrTolerating([\"workspace\", \"close\", id], [\"workspace_not_found\"]);\n } catch (err) {\n if (err instanceof HerdrError && err.code === GROUP_CLOSE_REQUIRED_CODE) {\n throw new HerdrWorkspaceGroupCloseError(name, id, err.message);\n }\n throw err;\n }\n}\n\n// ─── Tabs (= mu windows) ───────────────────────────────────────────────\n\nexport async function listWindows(session?: string): Promise<MuxWindow[]> {\n if (session !== undefined) {\n const id = await resolveWorkspaceId(session);\n // A workspace that vanished mid-reconcile lists as empty, matching\n // the tmux backend's behaviour for a dead session.\n if (id === undefined) return [];\n return readTabs(await herdr([\"tab\", \"list\", \"--workspace\", id]));\n }\n // Cross-workspace: herdr has no `--all`, so fan out and tag each row\n // with the workspace LABEL, which is what MuxWindow.sessionName means.\n const windows: MuxWindow[] = [];\n for (const ws of await listWorkspaces()) {\n for (const tab of readTabs(await herdr([\"tab\", \"list\", \"--workspace\", ws.workspaceId]))) {\n windows.push({ ...tab, sessionName: ws.label });\n }\n }\n return windows;\n}\n\nfunction readTabs(result: Record<string, unknown>): MuxWindow[] {\n const out: MuxWindow[] = [];\n for (const raw of readArray(result, \"tabs\")) {\n const tab = readTab(raw);\n if (tab) out.push(tab);\n }\n return out;\n}\n\n/**\n * Create a tab in `opts.session`'s workspace and return its root pane id.\n * `opts.command` is refused (see `rejectCommand`).\n */\nexport async function newWindow(opts: NewWindowOptions): Promise<string> {\n rejectCommand(\"newWindow\", opts.command);\n const args = [\"tab\", \"create\"];\n if (opts.session !== undefined) {\n args.push(\"--workspace\", await requireWorkspaceId(opts.session));\n }\n args.push(\"--label\", opts.name, \"--no-focus\");\n if (opts.cwd) args.push(\"--cwd\", opts.cwd);\n appendEnvFlags(args, opts.env);\n return readCreatedPaneId(await herdr(args), \"root_pane\", args);\n}\n\n/**\n * NO-OP. herdr has no layout-algorithm concept: splits are explicit and\n * geometry is the user's, not mu's. Kept so the contract is total.\n */\nexport async function selectLayout(_window: string, _layout: string): Promise<void> {\n // Intentionally empty.\n}\n\n// ─── Panes (= mu agents) ───────────────────────────────────────────────\n\n/**\n * List every pane in a workspace, addressed by mu session name (= label).\n *\n * Returns `[]` rather than throwing when the workspace is gone: herdr\n * closes a workspace when its last pane exits, so \"it was here a moment\n * ago\" is normal during reconciliation. Same contract as the tmux backend.\n */\nexport async function listPanesInSession(session: string): Promise<MuxPane[]> {\n const id = await resolveWorkspaceId(session);\n if (id === undefined) return [];\n const result = await herdrTolerating(\n [\"pane\", \"list\", \"--workspace\", id],\n [\"workspace_not_found\"],\n );\n if (result === undefined) return [];\n return readPanes(result).map((p) => ({ ...p, sessionName: session }));\n}\n\n/**\n * List panes.\n *\n * - `\"*\"` or omitted → every pane in every workspace, tagged with its\n * workspace label. (tmux's default target is \"the current session\";\n * herdr has no ambient current workspace outside a managed pane, so\n * `$HERDR_WORKSPACE_ID` is consulted first and \"everything\" is the\n * fallback.)\n * - a workspace id (`w1`) → that workspace.\n * - a tab id (`w1:t2`) → that tab's panes.\n * - a mu session name → that workspace, by label.\n */\nexport async function listPanes(target?: string): Promise<MuxPane[]> {\n if (target !== undefined && target !== \"*\") {\n if (TAB_ID_RE.test(target)) {\n const workspaceId = target.split(\":\")[0] ?? target;\n const panes = readPanes(await herdr([\"pane\", \"list\", \"--workspace\", workspaceId]));\n return panes.filter((p) => p.windowId === target);\n }\n if (WORKSPACE_ID_RE.test(target)) {\n return readPanes(await herdr([\"pane\", \"list\", \"--workspace\", target]));\n }\n return listPanesInSession(target);\n }\n const ambient = process.env.HERDR_WORKSPACE_ID;\n if (target === undefined && ambient !== undefined && WORKSPACE_ID_RE.test(ambient)) {\n return readPanes(await herdr([\"pane\", \"list\", \"--workspace\", ambient]));\n }\n const panes: MuxPane[] = [];\n for (const ws of await listWorkspaces()) {\n for (const pane of readPanes(await herdr([\"pane\", \"list\", \"--workspace\", ws.workspaceId]))) {\n panes.push({ ...pane, sessionName: ws.label });\n }\n }\n return panes;\n}\n\nfunction readPanes(result: Record<string, unknown>): MuxPane[] {\n const out: MuxPane[] = [];\n for (const raw of readArray(result, \"panes\")) {\n const pane = readPane(raw);\n if (pane) out.push(pane);\n }\n return out;\n}\n\n/**\n * Split a pane and return the new pane's id.\n *\n * `opts.horizontal` maps to herdr's `--direction`: horizontal (mu's\n * default, side-by-side) is `right`; a vertical split is `down`.\n * `opts.target` must be a herdr pane id — herdr splits panes, not\n * windows, so there is no \":WindowName\" form to accept.\n */\nexport async function splitWindow(opts: SplitWindowOptions): Promise<string> {\n rejectCommand(\"splitWindow\", opts.command);\n assertValidPaneId(opts.target);\n const args = [\n \"pane\",\n \"split\",\n opts.target,\n \"--direction\",\n opts.horizontal === false ? \"down\" : \"right\",\n \"--no-focus\",\n ];\n if (opts.cwd) args.push(\"--cwd\", opts.cwd);\n appendEnvFlags(args, opts.env);\n return readCreatedPaneId(await herdr(args), \"pane\", args);\n}\n\n/** Idempotent: succeeds even if the pane is already gone. */\nexport async function killPane(paneId: string): Promise<void> {\n assertValidPaneId(paneId);\n await herdrTolerating([\"pane\", \"close\", paneId], [\"pane_not_found\", \"workspace_not_found\"]);\n}\n\nexport async function paneExists(paneId: string): Promise<boolean> {\n if (!isValidPaneId(paneId)) return false;\n const pane = await getPane(paneId);\n return pane !== undefined;\n}\n\n/** `pane get`, or undefined when the pane (or its workspace) is gone. */\nasync function getPane(paneId: string): Promise<MuxPane | undefined> {\n const result = await herdrTolerating(\n [\"pane\", \"get\", paneId],\n [\"pane_not_found\", \"workspace_not_found\"],\n );\n if (result === undefined) return undefined;\n return readPane(result.pane);\n}\n\n/**\n * Look up the TTY device path backing a pane (e.g. `/dev/pts/3`). Used\n * by `mu agent kick` to signal the foreground process group directly.\n *\n * herdr reports the pane's `shell_pid` rather than a TTY, so mu resolves\n * the device through `/proc/<pid>/fd/0`. That is Linux-only by\n * construction; on a kernel without procfs this throws a `HerdrError`\n * naming the limitation rather than returning a plausible-looking lie.\n *\n * Throws `PaneNotFoundError` when the pane has vanished.\n */\nexport async function paneTTY(paneId: string): Promise<string> {\n assertValidPaneId(paneId);\n const result = await herdrTolerating(\n [\"pane\", \"process-info\", \"--pane\", paneId],\n [\"pane_not_found\", \"workspace_not_found\"],\n );\n if (result === undefined) throw new PaneNotFoundError(paneId, herdrBackend);\n const info = result.process_info;\n const pid = isRecord(info) ? info.shell_pid : undefined;\n if (typeof pid !== \"number\") throw new PaneNotFoundError(paneId, herdrBackend);\n const { readlink } = await import(\"node:fs/promises\");\n try {\n return await readlink(`/proc/${pid}/fd/0`);\n } catch (err) {\n throw new HerdrError(\n [\"pane\", \"process-info\", \"--pane\", paneId],\n `cannot resolve the TTY for herdr pane ${paneId} (shell pid ${pid}) via /proc: ${String(err)}`,\n \"\",\n 1,\n );\n }\n}\n\n// ─── Identity ──────────────────────────────────────────────────────────\n\n/** herdr's pane `label` is mu's pane title. */\nexport async function setPaneTitle(paneId: string, title: string): Promise<void> {\n assertValidPaneId(paneId);\n await herdr([\"pane\", \"rename\", paneId, title]);\n}\n\nexport async function getPaneTitle(paneId: string): Promise<string | undefined> {\n if (!isValidPaneId(paneId)) return undefined;\n const pane = await getPane(paneId).catch(() => undefined);\n if (pane === undefined) return undefined;\n return pane.title.length > 0 ? pane.title : undefined;\n}\n\n/**\n * The agent name of the pane this process is running in, read from the\n * `$HERDR_PANE_ID` that herdr injects into every managed pane.\n *\n * Fallback rung only: `$MU_AGENT_NAME` is checked first by\n * `src/tasks/claim.ts` and is backend-independent. Titles are composed\n * as `name · task_id · <glyph>`, so the name is the first token.\n */\nexport async function currentAgentName(): Promise<string | undefined> {\n const paneId = process.env.HERDR_PANE_ID;\n if (paneId === undefined || !isValidPaneId(paneId)) return undefined;\n const title = await getPaneTitle(paneId);\n if (title === undefined) return undefined;\n const idx = title.indexOf(\" · \");\n return idx === -1 ? title.trim() : title.slice(0, idx).trim();\n}\n\n/**\n * Label of the workspace the CALLER is running inside, or undefined\n * outside a managed pane. Backs the `mu-<name>` rung of workstream\n * auto-detection.\n *\n * herdr injects `$HERDR_WORKSPACE_ID` into every pane it manages, so\n * this is one lookup rather than tmux's `display-message -p '#S'`. The\n * env var carries the opaque id; mu keys on the LABEL, so resolve it.\n */\nexport async function currentSessionName(): Promise<string | undefined> {\n const workspaceId = process.env.HERDR_WORKSPACE_ID;\n if (workspaceId === undefined || !WORKSPACE_ID_RE.test(workspaceId)) return undefined;\n for (const ws of await listWorkspaces()) {\n if (ws.workspaceId === workspaceId) return ws.label;\n }\n return undefined;\n}\n\n// ─── IO ────────────────────────────────────────────────────────────────\n//\n// THE SEND PROTOCOL IS ONE CALL. Read the header of ./tmux.ts for what\n// that replaces: six steps, of which THREE (awaitPaneQuiescence,\n// the MU_SEND_DELAY_MS gap, and the confirm-the-Enter-took retry loop)\n// exist purely because a TUI rendering a modal SWALLOWS a `send-keys\n// Enter`, stranding the pasted text while `mu agent send` reports exit 0\n// (dogfood_send_after_new_dropped).\n//\n// `herdr agent prompt` submits the text AND an encoded Enter atomically,\n// honouring the pane's live bracketed-paste mode, so the Enter cannot be\n// separated from the paste and cannot be swallowed. There is nothing\n// left for a quiescence poll to protect against — porting one here would\n// re-add latency to work around a bug this substrate does not have.\n//\n// Do not add a scrollback poll to this file. If a send ever looks\n// unconfirmed on herdr, that is a herdr bug, and the `agent_prompt_\n// stalled` guard below is how it surfaces.\n\n// ─── Spawn, step 2 ─────────────────────────────────────────────────────\n//\n// The creation verbs above make a BARE pane; this turns it into a running\n// agent. Both halves exist because herdr has no create-and-run form.\n\n/**\n * Retry budget for `agent start` while herdr reports the target pane is\n * not yet an available shell.\n *\n * VERIFIED RACE (0.8.0): `agent start` immediately after `pane split`\n * fails `agent_pane_busy` ~100% of the time; with a 200ms gap it\n * succeeded every time. The pane exists the moment split returns, but its\n * shell has not drawn a prompt yet, and `agent start` requires an\n * interactive prompt with nothing in the foreground.\n *\n * A retry rather than a fixed pre-sleep: the condition is observable, so\n * mu polls instead of guessing a constant that is simultaneously too long\n * for a fast box and too short for a loaded one. ~3s nominal, because\n * losing the race means a rolled-back spawn while a pane that never frees\n * up is a real failure and must not be spun on forever. Counted in\n * ATTEMPTS not wall-clock, so `setSleepForTests` alone makes the loop\n * instant and deterministic in the fast tier.\n */\nconst PANE_READY_RETRY_INTERVAL_MS = 50;\nconst PANE_READY_MAX_ATTEMPTS = 60;\n\n/**\n * Turn an existing bare pane into a running agent, and return only once\n * herdr considers that agent ready for input.\n *\n * `herdr agent start <name> --kind <cli> --pane <id>` blocks until herdr\n * has DETECTED the expected agent in that terminal and it is\n * interactive-ready (30s default timeout, herdr's own). That subsumes\n * mu's tmux-side `awaitSpawnLiveness` scrollback polling completely,\n * which is why `src/agents/spawn.ts` skips it when a backend implements\n * this method. Nothing here re-implements it.\n *\n * Three refusals, all deliberate (see each error class):\n * - an operator-pinned command → `HerdrCommandOverrideError`\n * - a `--cli` herdr does not know → `HerdrUnsupportedCliError`\n * - an agent name herdr rejects → the raw `HerdrError`\n * On any of them nothing has started, and the caller's rollback kills the\n * bare pane. No path leaves mu holding an agent row for a pane with\n * nothing running in it.\n *\n * Addressed BY PANE ID: the herdr-level agent NAME is cleared when the\n * occupant exits, so the pane id is the only durable handle (and the\n * only one mu persists). Matching `sendToPane`'s choice.\n */\nexport async function startAgentInPane(opts: StartAgentInPaneOptions): Promise<void> {\n assertValidPaneId(opts.paneId);\n // Refuse BEFORE touching the server: an override we cannot honour is\n // decidable locally, and failing fast keeps the pane pristine for the\n // caller's rollback.\n if (opts.commandSource !== \"cli-key\") {\n throw new HerdrCommandOverrideError(\n opts.cli,\n opts.command,\n // Name the env var only when that is actually where the override\n // came from; \"unset MU_PI_COMMAND\" is useless advice to someone who\n // typed --command.\n opts.commandSource === \"env\" ? envVarNameForCli(opts.cli) : undefined,\n );\n }\n const args = [\"agent\", \"start\", opts.name, \"--kind\", opts.cli, \"--pane\", opts.paneId];\n for (let attempt = 1; ; attempt += 1) {\n try {\n await herdr(args);\n return;\n } catch (err) {\n // An unknown --kind is a SYNTAX error to herdr's arg parser (exit\n // 2), because `--kind` is a closed enum. Everywhere else exit 2\n // means mu drifted from the CLI, so translate it here — an\n // operator typo must not read as \"bug in mu\".\n if (err instanceof HerdrSyntaxError) {\n throw new HerdrUnsupportedCliError(opts.cli, err.output.trim().split(\"\\n\")[0] ?? \"\");\n }\n const busy = err instanceof HerdrError && err.code === \"agent_pane_busy\";\n if (!busy || attempt >= PANE_READY_MAX_ATTEMPTS) throw err;\n await sleep(PANE_READY_RETRY_INTERVAL_MS);\n }\n }\n}\n\n/**\n * `MU_<UPPER_CLI>_COMMAND`, mirroring `envVarNameForCli` in\n * src/agents/spawn.ts. Duplicated rather than imported because the mux\n * cluster may not depend on the agent layer (imports run cluster →\n * root, never sideways into another feature); it is one string join and\n * `test/mux-herdr-spawn.test.ts` asserts the two stay identical.\n */\nfunction envVarNameForCli(cli: string): string {\n return `MU_${cli.toUpperCase().replace(/-/g, \"_\")}_COMMAND`;\n}\n\n// ─── IO (owned by mux-herdr-io) ────────────────────────────────────────\n\n/** herdr's error code for \"submitted, but the agent's lifecycle never\n * moved within its 5s guard\". Not an outage — a suspicious send. */\nconst PROMPT_STALLED_CODE = \"agent_prompt_stalled\";\n\n/** Codes meaning \"the pane hosts no RECOGNIZED agent\", so the `agent`\n * surface cannot address it and we fall back to the `pane` surface. */\nconst NO_AGENT_CODES: readonly string[] = [\"agent_not_found\", \"agent_not_recognized\"];\n\n/** Default handler for an unconfirmed send: warn on stderr. Loud by\n * design, exactly as on tmux — silence is the failure mode. */\nfunction defaultUndeliveredWarning(warning: SendWarning): void {\n console.warn(`warning: ${warning.message}`);\n}\n\n/**\n * Submit `text` to a pane and press Enter, atomically.\n *\n * WHICH SURFACE (topology note 9): `agent prompt` only accepts targets\n * herdr has RECOGNIZED as an agent — a plain shell pane is\n * `agent_not_found` there. mu therefore tries the agent surface FIRST\n * (the overwhelmingly common case: every mu-managed pane runs a coding\n * CLI, and it is the only surface that understands lifecycle state) and\n * falls back to `pane run`, herdr's atomic text+Enter for raw shells,\n * only when herdr says there is no agent. The happy path is one call;\n * the fallback path is two, and never polls.\n *\n * `opts.readinessMs === 0` drops `--wait`, the herdr analogue of tmux's\n * \"bare protocol\" opt-out: submit and return without waiting for a\n * settled state. Any other value is ignored — there is no readiness to\n * budget for. `opts.delayMs` is likewise meaningless here: the gap it\n * sizes does not exist. Neither is an error; the tmux-shaped knobs stay\n * accepted so callers need no backend branch.\n *\n * No `--until`: `--wait` already defaults to the first settled idle /\n * done / blocked, which is precisely what mu's callers want, and herdr's\n * skill doc calls repeating those defaults a bug.\n *\n * DELIVERY CONTRACT, same as tmux: an unconfirmed send WARNS and keeps\n * going, never throws. herdr returns `agent_prompt_stalled` when a\n * prompt from a non-working state produces no lifecycle change within\n * 5s — the same observable as tmux's stranded paste, so it maps onto the\n * existing `paste-vanished` reason: the pane was calm, we submitted, and\n * nothing happened.\n */\nexport async function sendToPane(\n paneId: string,\n text: string,\n opts: SendOptions = {},\n): Promise<void> {\n assertValidPaneId(paneId);\n const wait = opts.readinessMs === 0 ? [] : [\"--wait\"];\n try {\n await herdr([\"agent\", \"prompt\", paneId, text, ...wait]);\n return;\n } catch (err) {\n if (!(err instanceof HerdrError) || err.code === undefined) throw err;\n if (err.code === PROMPT_STALLED_CODE) {\n const warn = opts.onUndelivered ?? defaultUndeliveredWarning;\n warn({\n paneId,\n reason: \"paste-vanished\",\n message: `send to ${paneId} was submitted but the agent's state never changed (herdr: ${PROMPT_STALLED_CODE}). The agent may NOT have seen it. Read the pane, or re-send.`,\n });\n return;\n }\n if (!NO_AGENT_CODES.includes(err.code)) throw err;\n }\n // No recognized agent in this pane: raw shell. `pane run` is herdr's\n // atomic text+Enter for that case. It has no lifecycle to wait on, so\n // there is nothing to confirm and nothing to warn about.\n await herdr([\"pane\", \"run\", paneId, text]);\n}\n\n/**\n * Read pane output as plain text.\n *\n * - no options → everything herdr has available, soft wraps joined\n * - `lines: 0` → the rendered viewport only (`--source visible`)\n * - `lines: N` → the last N rows, soft wraps joined\n *\n * `recent-unwrapped` is the source herdr recommends for logs and\n * transcripts: it joins soft wraps, so a wrapped line reads as one line\n * rather than as terminal-width fragments.\n *\n * KNOWN LIMIT — do not paper over this: a pane running its agent on the\n * terminal's ALTERNATE screen does not spill rows into herdr's host\n * scrollback. Rows that have left the alternate screen are GONE, so a\n * bigger `--lines` cannot recover them. If a read looks truncated, that\n * is why, and the fix is to ask the agent to write its output to a file\n * — not to raise the line count.\n *\n * `pane read` emits PLAIN TEXT, so it bypasses `herdr()` (which would\n * treat unparseable stdout as CLI drift) and reads the executor result\n * directly, mapping herdr's JSON error envelope by hand.\n */\nexport async function capturePane(paneId: string, opts: CaptureOptions = {}): Promise<string> {\n assertValidPaneId(paneId);\n const args = [\"pane\", \"read\", paneId];\n if (opts.lines === 0) {\n args.push(\"--source\", \"visible\");\n } else {\n args.push(\"--source\", \"recent-unwrapped\");\n if (opts.lines !== undefined) args.push(\"--lines\", String(opts.lines));\n }\n const result = await currentExecutor(args);\n if (result.exitCode === 2) throw new HerdrSyntaxError(args, result.stderr || result.stdout);\n if (result.exitCode !== 0) {\n throw new HerdrError(\n args,\n result.stderr,\n result.stdout,\n result.exitCode,\n parseErrorEnvelope(result.stderr)?.code,\n );\n }\n return result.stdout;\n}\n\n// ─── Native status ─────────────────────────────────────────────────────\n\n/**\n * herdr's five agent lifecycle states, mapped onto mu's.\n *\n * working → busy\n * blocked → needs_permission (herdr recognized an approval/question UI)\n * idle → needs_input\n * done → needs_input\n * unknown → needs_input\n *\n * `unknown` MUST NOT become `free`. herdr's skill doc is explicit that\n * unknown \"does not prove completion\"; a false `free` makes mu hand the\n * worker a second task while the first is still running. `needs_input`\n * is the safe reading of \"an agent is there and we cannot classify it\".\n * (`free` is not a detected status at all — it is set by `mu agent\n * free`, i.e. by the user, and no detector may mint it.)\n *\n * `done` is herdr's idle-after-UNSEEN-background-work, and CLI reads\n * deliberately do NOT mark a tab seen. mu polling therefore never clears\n * the user's done badge — which is the point, and why no read path in\n * this file calls a focus or seen-marking verb (only the attach steps\n * the user asks for name one).\n */\nexport function mapAgentStatus(agentStatus: string): MuxPaneStatus {\n switch (agentStatus) {\n case \"working\":\n return \"busy\";\n case \"blocked\":\n return \"needs_permission\";\n default:\n // idle / done / unknown, and any state a future herdr adds: the\n // conservative reading is \"waiting on a human\", never \"free\".\n return \"needs_input\";\n }\n}\n\n/**\n * The pane's status as HERDR classifies it, or undefined when the pane\n * is gone (or reports no state at all).\n *\n * herdr watches the terminal continuously across every agent kind it\n * knows; mu takes that report rather than reading pane text.\n */\nexport async function paneStatus(paneId: string): Promise<MuxPaneStatus | undefined> {\n if (!isValidPaneId(paneId)) return undefined;\n const result = await herdrTolerating(\n [\"pane\", \"get\", paneId],\n [\"pane_not_found\", \"workspace_not_found\"],\n );\n if (result === undefined) return undefined;\n const pane = result.pane;\n const status = isRecord(pane) ? asString(pane.agent_status) : undefined;\n if (status === undefined) return undefined;\n return mapAgentStatus(status);\n}\n\n// ─── Chrome ────────────────────────────────────────────────────────────\n\n/**\n * NO-OP. herdr owns its own pane chrome and exposes no border knobs;\n * mu-managed panes are distinguished by their label instead. Returns 0\n * (windows decorated) to keep the contract total.\n */\nexport async function enableMuPaneBordersForSession(_session: string): Promise<number> {\n return 0;\n}\n\n/** NO-OP. See `enableMuPaneBordersForSession`. */\nexport async function enableMuPaneBordersForPane(_paneId: string): Promise<void> {\n // Intentionally empty.\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────\n\n/**\n * Push one `--env KEY=VALUE` flag per entry. Keys must be non-empty and\n * free of `=`; throwing a TypeError keeps the failure at the call site\n * instead of letting herdr reject the line as a syntax error (exit 2),\n * which would look like CLI drift.\n */\nfunction appendEnvFlags(args: string[], env: Record<string, string> | undefined): void {\n if (!env) return;\n for (const [k, v] of Object.entries(env)) {\n if (k.length === 0) throw new TypeError(\"herdr env key must be non-empty\");\n if (k.includes(\"=\")) {\n throw new TypeError(`herdr env key must not contain '=': ${JSON.stringify(k)}`);\n }\n args.push(\"--env\", `${k}=${v}`);\n }\n}\n\n/**\n * herdr's creation verbs start a plain shell; there is no\n * create-and-run-this form. Running the agent CLI in the new pane is a\n * separate `agent start` step (`startAgentInPane`), which spawn calls\n * after creating the bare pane. Refuse loudly rather than silently\n * dropping the command on the floor.\n */\nfunction rejectCommand(method: string, command: string | undefined): void {\n if (command === undefined || command.trim().length === 0) return;\n throw new HerdrNotImplementedError(`${method} with a command`);\n}\n\n/**\n * Read a pane id out of a creation response. Never predict an id: herdr\n * does not reuse closed ids and a moved pane is renumbered, so the\n * response is the only trustworthy source.\n */\nfunction readCreatedPaneId(\n result: Record<string, unknown>,\n key: string,\n args: readonly string[],\n): string {\n const pane = readPane(result[key]);\n if (pane === undefined) {\n throw new HerdrError(\n args,\n `herdr response had no ${key}.pane_id: ${JSON.stringify(result)}`,\n \"\",\n 0,\n );\n }\n assertValidPaneId(pane.paneId);\n return pane.paneId;\n}\n\n// ─── Backend record ────────────────────────────────────────────────────\n\n/**\n * True iff a `herdr status` payload describes a server mu can drive.\n *\n * Pure, and EXPORTED because test/_mux.ts gates the real-herdr\n * integration tier on the same question — one parser, not two that\n * drift (they already had, across the 0.8 → 0.9 rename below).\n *\n * WHICH COMPATIBILITY LINE. herdr 0.9.0 split the old single\n * `compatible: yes|no` into two, and they do NOT mean the same thing:\n *\n * endpoint_compatible — the stable public API generation.\n * `no` means the server predates\n * endpoint generation 1 and needs a\n * one-time upgrade before ANY verb\n * works. Load-bearing.\n * private_protocol_compatible — the internal client↔server protocol.\n * Since 0.9.0 a mismatch here only\n * disables the affected action and\n * leaves running agents untouched, so\n * it must NOT gate availability: a\n * client one release ahead of its\n * server still drives panes fine.\n *\n * The bare `compatible:` line is herdr ≤0.8.x and is still honoured, so\n * mu does not silently start trusting an old incompatible server. It\n * cannot collide with the two 0.9 lines: `^\\s*` will not consume the\n * `endpoint_` / `private_protocol_` prefixes.\n *\n * A MISSING line is fine in every case — absence of evidence is not\n * incompatibility, and herdr omits these entirely when no server runs\n * (which the `status: running` check has already rejected).\n */\nexport function isHerdrStatusUsable(stdout: string): boolean {\n if (!/^\\s*status:\\s*running\\s*$/m.test(stdout)) return false;\n if (/^\\s*endpoint_compatible:\\s*no\\s*$/m.test(stdout)) return false;\n return !/^\\s*compatible:\\s*no\\s*$/m.test(stdout);\n}\n\n/**\n * True iff herdr can be reached right now. `herdr status` rather than a\n * PATH probe, and it must report a RUNNING, COMPATIBLE server: a herdr\n * binary whose server is down cannot drive a single pane, so it is not\n * an available backend.\n *\n * Note `herdr status` is plain text, not JSON — the one exception to the\n * \"everything is JSON\" rule, so it is parsed here rather than through\n * `herdr()`.\n */\nasync function herdrAvailable(): Promise<boolean> {\n const result = await currentExecutor([\"status\"]).catch(() => undefined);\n if (result === undefined || result.exitCode !== 0) return false;\n return isHerdrStatusUsable(result.stdout);\n}\n\n// ─── Attach ─────────────────────────────────────────────────────────────\n//\n// The mu session name is a workspace LABEL inside one herdr server, so\n// attaching means FOCUSING that workspace (or the agent's tab), and\n// herdr addresses both by opaque id: resolve label → id first.\n// `herdr session attach <name>` is NOT the analogue: it attaches a\n// whole named SERVER, and pointing it at a workspace label starts a\n// new, empty server under that name.\n//\n// `MU_HERDR_SESSION` picks the server mu drives, and the user's shell\n// does not read it, so every emitted line carries the same\n// `--session <name>` prefix as `herdrGlobalFlags()`.\n//\n// Focus moves the server's view; the user only sees it through a\n// client. Inside a herdr pane (`$HERDR_ENV=1`, the signal detect.ts\n// uses) the caller's client already shows it. Outside, a second step\n// opens a client. `target.inside` is tmux's evidence (`$TMUX`) and is\n// ignored here.\n\n/** The focus argv for `target` in workspace `workspaceId`. Prefers the\n * agent's tab (`tab focus` also switches the workspace) and falls back\n * to the workspace when no tab carries that label. */\nasync function attachFocusArgs(workspaceId: string, target: AttachTarget): Promise<string[]> {\n if (target.window !== undefined) {\n const tabs = readTabs(await herdr([\"tab\", \"list\", \"--workspace\", workspaceId]));\n const tab = tabs.find((t) => t.name === target.window);\n if (tab !== undefined) return [\"tab\", \"focus\", tab.id];\n }\n return [\"workspace\", \"focus\", workspaceId];\n}\n\nfunction insideHerdrClient(): boolean {\n return process.env.HERDR_ENV === \"1\";\n}\n\n/**\n * Copy-pasteable line landing the user on `target`. Never throws: it is\n * a hint, so an unresolvable label degrades to the manual recipe.\n */\nexport async function attachHint(target: AttachTarget): Promise<string> {\n const base = [\"herdr\", ...herdrGlobalFlags()].join(\" \");\n const focus = await resolveWorkspaceId(target.session)\n .then((id) => (id === undefined ? undefined : attachFocusArgs(id, target)))\n .catch(() => undefined);\n if (focus === undefined) {\n return `${base} workspace list # then: ${base} workspace focus <id of ${target.session}>`;\n }\n const line = `${base} ${focus.join(\" \")}`;\n return insideHerdrClient() ? line : `${line} && ${base}`;\n}\n\n/** The same attach as argv steps. Throws `HerdrError` when no workspace\n * carries the label (the TUI shows it in the footer). */\nexport async function attachCommands(target: AttachTarget): Promise<readonly MuxCommand[]> {\n const flags = herdrGlobalFlags();\n const focus = await attachFocusArgs(await requireWorkspaceId(target.session), target);\n const steps: MuxCommand[] = [{ command: \"herdr\", args: [...flags, ...focus] }];\n // Bare `herdr` launches a client attached to the (named) server.\n if (!insideHerdrClient()) steps.push({ command: \"herdr\", args: [...flags] });\n return steps;\n}\n\n// ─── Diagnostics ────────────────────────────────────────────────────────\n\nexport function paneNotFoundNextSteps(paneId: string): NextStep[] {\n return [\n {\n intent: `Verify the pane id ${paneId} actually exists`,\n command: `herdr pane get ${paneId}`,\n },\n {\n intent: \"List panes in the current workspace\",\n command: 'herdr pane list --workspace \"$HERDR_WORKSPACE_ID\"',\n },\n ];\n}\n\n/**\n * Version probe plus the ambient facts herdr injects into every managed\n * pane. Returns DATA; `mu doctor` owns the strings.\n *\n * `herdr status` is the one command that is NOT JSON (see the task note\n * on mux-herdr-topology), so this parses the plain-text `version:` line\n * from the CLIENT block rather than routing through `herdr()`.\n */\nexport async function healthCheck(): Promise<MuxHealth> {\n let version: string | null = null;\n try {\n const result = await currentExecutor([\"status\"]);\n if (result.exitCode === 0) {\n const match = /^\\s*version:\\s*(\\S+)\\s*$/m.exec(result.stdout);\n version = match?.[1] ?? null;\n }\n } catch {\n version = null;\n }\n return {\n name: \"herdr\",\n ok: version !== null,\n version,\n env: [\n { name: \"$HERDR_ENV\", value: process.env.HERDR_ENV ?? null },\n { name: \"$HERDR_WORKSPACE_ID\", value: process.env.HERDR_WORKSPACE_ID ?? null },\n { name: \"$HERDR_PANE_ID\", value: process.env.HERDR_PANE_ID ?? null },\n ],\n remediation: \"start the herdr server (herdr status) or install herdr\",\n };\n}\n\n/**\n * The herdr implementation of `MuxBackend`. A frozen record of the\n * module's functions — no state of its own, so swapping backends is a\n * pointer assignment (see ./detect.ts).\n */\nexport const herdrBackend: MuxBackend = Object.freeze({\n name: \"herdr\" as const,\n available: herdrAvailable,\n\n isValidPaneId,\n assertValidPaneId,\n\n listSessions,\n sessionExists,\n newSession,\n newSessionWithPane,\n killSession,\n\n listWindows,\n newWindow,\n selectLayout,\n\n listPanes,\n listPanesInSession,\n splitWindow,\n killPane,\n paneExists,\n paneTTY,\n\n setPaneTitle,\n getPaneTitle,\n currentAgentName,\n currentSessionName,\n\n startAgentInPane,\n\n sendToPane,\n capturePane,\n paneStatus,\n\n enableMuPaneBordersForSession,\n enableMuPaneBordersForPane,\n\n attachHint,\n attachCommands,\n paneNotFoundNextSteps,\n healthCheck,\n});\n","// mu — tmux substrate.\n//\n// Single source of truth for all tmux interactions. Every tmux invocation\n// goes through `tmux(args)`, which wraps execa and produces structured\n// `TmuxError`s carrying args + stderr.\n//\n// The send protocol is the bracketed-paste sequence for NON-pi agents\n// (claude-code, codex, adopted panes) and an explicit `--via mux`. pi\n// agents never reach it: they use the control socket\n// (src/agents/transport.ts). Canonical implementation in `sendToPane`:\n// 0. await pane quiescence (MU_SEND_READINESS_MS, default 15s)\n// 1. copy-mode -q (silent if not in copy mode)\n// 2. set-buffer (load text into a uniquely named buffer)\n// 3. paste-buffer -p -d -r (bracketed paste, delete buffer, preserve LF)\n// 4. delay (MU_SEND_DELAY_MS, default 500)\n// 5. send-keys Enter\n// 6. confirm the Enter took; re-send it, then warn loudly if not\n//\n// Naive `tmux send-keys \"<text>\"` is broken: characters like /, ?, f get\n// interpreted by the agent's TUI (Claude, Codex, less, vim) or by tmux's\n// copy mode if the user has scrolled up. Use `sendToPane()`.\n//\n// Steps 0 and 6 exist because of dogfood_send_after_new_dropped: a TUI\n// rendering a modal SWALLOWS the Enter, stranding the pasted text in the\n// input box while `mu agent send` reported exit 0. See\n// `awaitPaneQuiescence`.\n\nimport { execa } from \"execa\";\nimport type { NextStep } from \"../output.js\";\nimport { paneLooksBusy } from \"./input-timing.js\";\nimport {\n type AttachTarget,\n type CaptureOptions,\n type MuxBackend,\n type MuxCommand,\n MuxError,\n type MuxHealth,\n type MuxPane,\n type MuxSession,\n type MuxWindow,\n type NewSessionOptions,\n type NewSessionWithPaneOptions,\n type NewWindowOptions,\n PaneNotFoundError,\n parseAgentNameFromTitle,\n type SendOptions,\n type SendWarning,\n type SplitWindowOptions,\n} from \"./types.js\";\n\n// Re-exported so the historical `import { ... } from \"./tmux.js\"` surface\n// keeps resolving these names (47 test files and 12 src modules use it).\nexport {\n type AttachTarget,\n type CaptureOptions,\n type MuxCommand,\n MuxError,\n type MuxHealth,\n type NewSessionOptions,\n type NewSessionWithPaneOptions,\n type NewWindowOptions,\n PaneNotFoundError,\n parseAgentNameFromTitle,\n type SendOptions,\n type SendWarning,\n type SplitWindowOptions,\n};\n\n/** Back-compat aliases: tmux-specific names predating the MuxBackend\n * extraction. Same shapes, now owned by ./types.js. */\nexport type TmuxSession = MuxSession;\nexport type TmuxWindow = MuxWindow;\nexport type TmuxPane = MuxPane;\n\n// ─── Error type ────────────────────────────────────────────────────────\n\nexport class TmuxError extends MuxError {\n constructor(\n public readonly args: readonly string[],\n public readonly stderr: string,\n public readonly stdout: string,\n public readonly exitCode: number | null,\n ) {\n const detail = stderr.trim() || stdout.trim() || \"no output\";\n super(`tmux ${args.join(\" \")} failed (exit ${exitCode}): ${detail}`);\n this.name = \"TmuxError\";\n }\n override errorNextSteps(): NextStep[] {\n return [\n { intent: \"Run health check\", command: \"mu doctor\" },\n {\n intent: \"Verify tmux is running and reachable\",\n command: \"tmux info | head\",\n },\n {\n intent: \"Check the failing tmux command in isolation\",\n command: `tmux ${this.args.join(\" \")}`,\n },\n ];\n }\n}\n\n// ─── Pane ID validation ────────────────────────────────────────────────\n\n/**\n * Stable tmux pane IDs are of the form `%N` (e.g. \"%15\"). They never change\n * for the lifetime of the pane. **Pane indexes** (0, 1, 2…) are volatile and\n * shift when other panes close — never store or pass them.\n */\nexport const PANE_ID_RE = /^%\\d+$/;\n\nexport function isValidPaneId(s: string): boolean {\n return PANE_ID_RE.test(s);\n}\n\nexport function assertValidPaneId(s: string): void {\n if (!isValidPaneId(s)) {\n throw new TypeError(`invalid tmux pane id: ${JSON.stringify(s)} (expected /^%\\\\d+$/)`);\n }\n}\n\n// ─── Configurable delay ────────────────────────────────────────────────\n\n/**\n * Delay between bracketed-paste and Enter, in milliseconds. Claude/Codex/pi\n * process pasted text asynchronously; without this delay, Enter can arrive\n * before the agent has ingested the text. Defaults to 500; lower for tests,\n * raise for slow remotes via `MU_SEND_DELAY_MS`.\n */\nexport function defaultSendDelayMs(): number {\n const raw = process.env.MU_SEND_DELAY_MS;\n if (raw === undefined) return 500;\n const parsed = Number.parseInt(raw, 10);\n if (Number.isNaN(parsed) || parsed < 0) return 500;\n return parsed;\n}\n\n// ─── Executor (swappable for tests) ────────────────────────────────────\n\nexport interface TmuxExecResult {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n}\n\nexport type TmuxExecutor = (args: readonly string[]) => Promise<TmuxExecResult>;\n\n/**\n * Optional global-flag prefix to splice in front of every tmux args\n * vector. When `MU_TMUX_SOCKET=<name>` is set, this returns\n * `[\"-L\", name, \"-f\", \"/dev/null\"]`:\n *\n * `-L <name>` routes every call through a private tmux server with\n * the given socket name (Linux: `/tmp/tmux-<uid>/<name>`,\n * macOS: `$TMPDIR/tmux-<uid>/<name>`) instead of the user's default\n * `/tmp/tmux-<uid>/default`. Set by the test harness in Layer 3 of\n * bug_test_suite_flake_leaks_isolation so the integration suite\n * can never observe — or contaminate — the user's interactive\n * tmux server.\n *\n * `-f /dev/null` skips the user's `~/.tmux.conf`. This matters\n * because tmux auto-starts the server on the first client call if\n * none is running (e.g. after a test’s last `kill-session`\n * shuts the server down). A typical user config uses `run-shell`\n * for status-bar plugins (TPM, hostname/network probes), and each\n * such hook adds ~1–4s to that auto-start. Without `-f /dev/null`\n * a single integration test grows from 3s to 48s on a configured\n * dev box. The suite drives tmux through the documented protocol,\n * not through bound keys, so the user's config is irrelevant.\n *\n * Read fresh on every call so a setupFiles hook that mutates\n * `process.env.MU_TMUX_SOCKET` mid-run takes effect immediately.\n *\n * Production code never sets this; it's a test-isolation seam.\n */\nfunction tmuxGlobalFlags(): readonly string[] {\n const socket = process.env.MU_TMUX_SOCKET;\n if (socket === undefined || socket.length === 0) return [];\n return [\"-L\", socket, \"-f\", \"/dev/null\"];\n}\n\nconst realExecutor: TmuxExecutor = async (args) => {\n const result = await execa(\"tmux\", [...tmuxGlobalFlags(), ...args], { reject: false });\n return {\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? \"\",\n exitCode: result.exitCode ?? null,\n };\n};\n\nlet currentExecutor: TmuxExecutor = realExecutor;\n\n/**\n * Install a custom executor (for tests). Returns the previous executor so\n * tests can restore it cleanly. Production code should never call this.\n */\nexport function setTmuxExecutor(executor: TmuxExecutor): TmuxExecutor {\n const previous = currentExecutor;\n currentExecutor = executor;\n return previous;\n}\n\n/** Restore the real (execa-backed) executor. */\nexport function resetTmuxExecutor(): void {\n currentExecutor = realExecutor;\n}\n\n/**\n * Run an arbitrary tmux command. The single point of contact with the\n * tmux binary; every higher-level operation in this module goes through it.\n *\n * Throws `TmuxError` on non-zero exit. Returns stdout on success.\n */\nexport async function tmux(args: readonly string[]): Promise<string> {\n const result = await currentExecutor(args);\n if (result.exitCode !== 0) {\n throw new TmuxError([...args], result.stderr, result.stdout, result.exitCode);\n }\n return result.stdout;\n}\n\n// ─── Sleep helper (testable) ──────────────────────────────────────────\n\nlet currentSleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nexport function setSleepForTests(\n impl: (ms: number) => Promise<void>,\n): (ms: number) => Promise<void> {\n const previous = currentSleep;\n currentSleep = impl;\n return previous;\n}\n\nexport function resetSleep(): void {\n currentSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Test-aware sleep — honours `setSleepForTests`. Public so other modules\n * (notably `agents.ts` for spawn liveness polling) get free no-op-ing in\n * tests without re-implementing the swap. */\nexport function sleep(ms: number): Promise<void> {\n return currentSleep(ms);\n}\n\n// ─── Domain types ──────────────────────────────────────────────────────\n\n// ─── Sessions ──────────────────────────────────────────────────────────\n\n/**\n * The `-t` form that names session `name` EXACTLY. A bare `-t mu-auth`\n * falls back to prefix matching, so it resolves to `mu-auth-refactor`\n * when `mu-auth` does not exist, and a teardown of workstream `auth`\n * kills another workstream's session. `=` disables the prefix and\n * pattern fallback. The trailing `:` makes it a session target for\n * pane- and window-scoped verbs too: `list-panes -s -t =mu-auth`\n * still prefix-matches through the pane lookup, but\n * `-t =mu-auth:` does not (verified on tmux 3.7c).\n */\nfunction exactSessionTarget(name: string): string {\n return `=${name}:`;\n}\n\n/**\n * stderr shapes that mean \"no tmux server is running\", so there is no\n * session, window or pane to find. tmux prints \"no server running on\n * <path>\" when the socket file exists but nothing listens, and \"error\n * connecting to <path> (No such file or directory)\" when the socket file\n * does not exist at all (fresh boot, cleared /tmp, new TMUX_TMPDIR).\n * Other \"error connecting\" causes, such as permission denied, stay\n * real errors.\n */\nconst NO_SERVER_RE = /no server running|error connecting to .*\\(No such file or directory\\)/i;\n\n/** True when a tmux call failed because the pane (or the whole server)\n * is gone, rather than for a transient reason. \"no current target\" is\n * what a server with zero sessions (`exit-empty off`) says about a dead\n * pane, as in killSession and sendToPane's copy-mode check. */\nfunction isPaneGoneError(err: unknown): boolean {\n return (\n err instanceof TmuxError &&\n (/can't find pane|pane not found|no current target/i.test(err.stderr) ||\n NO_SERVER_RE.test(err.stderr))\n );\n}\n\nexport async function listSessions(): Promise<TmuxSession[]> {\n // `list-sessions` exits 1 when no sessions exist; treat as empty.\n try {\n const out = await tmux([\"list-sessions\", \"-F\", \"#{session_name}\"]);\n return out\n .split(\"\\n\")\n .filter((line) => line.length > 0)\n .map((name) => ({ name }));\n } catch (err) {\n if (\n err instanceof TmuxError &&\n (NO_SERVER_RE.test(err.stderr) || /no sessions/i.test(err.stderr))\n ) {\n return [];\n }\n throw err;\n }\n}\n\nexport async function sessionExists(name: string): Promise<boolean> {\n const result = await currentExecutor([\"has-session\", \"-t\", exactSessionTarget(name)]);\n return result.exitCode === 0;\n}\n\nexport async function newSession(name: string, opts: NewSessionOptions = {}): Promise<void> {\n const args = [\"new-session\"];\n if (opts.detached !== false) args.push(\"-d\");\n args.push(\"-s\", name);\n if (opts.windowName) args.push(\"-n\", opts.windowName);\n if (opts.cwd) args.push(\"-c\", opts.cwd);\n appendEnvFlags(args, opts.env);\n if (opts.command) args.push(opts.command);\n await tmux(args);\n}\n\n/**\n * Create a tmux session AND its first window+pane in one atomic call.\n * Returns the new pane's stable id. Used by mu when spawning the first\n * agent in a workstream so we never end up with an empty `mu-<workstream>`\n * session left behind by a failed spawn.\n */\nexport async function newSessionWithPane(\n name: string,\n opts: NewSessionWithPaneOptions,\n): Promise<string> {\n const args = [\"new-session\"];\n if (opts.detached !== false) args.push(\"-d\");\n args.push(\"-s\", name, \"-n\", opts.windowName);\n if (opts.cwd) args.push(\"-c\", opts.cwd);\n appendEnvFlags(args, opts.env);\n args.push(\"-P\", \"-F\", \"#{pane_id}\", opts.command);\n const out = (await tmux(args)).trim();\n assertValidPaneId(out);\n return out;\n}\n\n/**\n * Idempotent: succeeds even if the session is already gone.\n *\n * Four swallowed shapes:\n * - \"can't find session: <name>\" — session never existed.\n * - \"session not found\" — alternate phrasing on some tmux builds.\n * - \"no server running on <path>\" (or \"error connecting to <path> (No\n * such file or directory)\", see NO_SERVER_RE) — the tmux server itself has exited\n * (typical when the test suite runs against a private `tmux -L\n * <socket>` server and the just-killed session was its last; tmux\n * quietly shuts the server down). Without this, killSession would\n * throw on the very next idempotent call — only visible under\n * Layer 3 of bug_test_suite_flake_leaks_isolation.\n * - \"no current target\" — the server is UP but has zero\n * sessions, so `-t <name>` has no session list to resolve against\n * and tmux never gets as far as \"can't find session\". Reachable\n * whenever the server outlives its last session: the suite's\n * private server (`exit-empty off`, see test/_global-teardown.ts)\n * and any user whose ~/.tmux.conf sets `exit-empty off`. Same\n * meaning as \"can't find session\" for our purposes — the session\n * we were asked to kill is gone.\n */\nexport async function killSession(name: string): Promise<void> {\n const args = [\"kill-session\", \"-t\", exactSessionTarget(name)];\n const result = await currentExecutor(args);\n if (\n result.exitCode !== 0 &&\n !/can't find session|session not found|no current target/i.test(result.stderr) &&\n !NO_SERVER_RE.test(result.stderr)\n ) {\n throw new TmuxError(args, result.stderr, result.stdout, result.exitCode);\n }\n}\n\n// ─── Windows ───────────────────────────────────────────────────────────\n\nexport async function listWindows(session?: string): Promise<TmuxWindow[]> {\n if (session) {\n const out = await tmux([\n \"list-windows\",\n \"-t\",\n exactSessionTarget(session),\n \"-F\",\n \"#{window_id}\\t#{window_name}\",\n ]);\n return parseWindows(out);\n }\n // Cross-session: include the session name.\n const out = await tmux([\n \"list-windows\",\n \"-a\",\n \"-F\",\n \"#{session_name}\\t#{window_id}\\t#{window_name}\",\n ]);\n const windows: TmuxWindow[] = [];\n for (const line of out.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [sessionName, id, name] = line.split(\"\\t\");\n if (!sessionName || !id || name === undefined) continue;\n windows.push({ id, name, sessionName });\n }\n return windows;\n}\n\nfunction parseWindows(output: string): TmuxWindow[] {\n const windows: TmuxWindow[] = [];\n for (const line of output.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [id, name] = line.split(\"\\t\");\n if (!id || name === undefined) continue;\n windows.push({ id, name });\n }\n return windows;\n}\n\n/**\n * Create a new tmux window with one pane. Returns the new pane's stable\n * pane id (e.g. `%15`).\n */\nexport async function newWindow(opts: NewWindowOptions): Promise<string> {\n const args = [\"new-window\"];\n if (opts.detached !== false) args.push(\"-d\");\n if (opts.session) args.push(\"-t\", exactSessionTarget(opts.session));\n args.push(\"-n\", opts.name);\n if (opts.cwd) args.push(\"-c\", opts.cwd);\n appendEnvFlags(args, opts.env);\n args.push(\"-P\", \"-F\", \"#{pane_id}\", opts.command);\n const out = (await tmux(args)).trim();\n assertValidPaneId(out);\n return out;\n}\n\n// ─── Panes ─────────────────────────────────────────────────────────────\n\n/**\n * List ALL panes in a tmux session (across every window). Used by\n * reconciliation to find every pane in the workstream's session.\n *\n * Note `list-panes -t <session>` (no -s) lists panes in the current\n * *window* of that session, not the whole session — a common gotcha.\n * `-s` is the flag that says \"all panes in this session.\"\n *\n * Returns `[]` (not throws) when the session doesn't exist or has no\n * panes. tmux destroys a session as soon as its last pane closes, so the\n * \"session was just here a moment ago\" case is normal during reconcile.\n * tmux's error wording in this case varies (\"can't find session\",\n * \"can't find window\", or \"no current target\" when the server is up\n * with zero sessions), so we match any of them.\n */\nexport async function listPanesInSession(session: string): Promise<TmuxPane[]> {\n const args = [\n \"list-panes\",\n \"-s\",\n \"-t\",\n exactSessionTarget(session),\n \"-F\",\n \"#{window_id}\\t#{pane_id}\\t#{pane_title}\\t#{pane_current_command}\",\n ];\n const result = await currentExecutor(args);\n if (result.exitCode !== 0) {\n if (\n /can't find (session|window)|no sessions|no current target/i.test(result.stderr) ||\n NO_SERVER_RE.test(result.stderr)\n ) {\n return [];\n }\n throw new TmuxError(args, result.stderr, result.stdout, result.exitCode);\n }\n const panes: TmuxPane[] = [];\n for (const line of result.stdout.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [windowId, paneId, title, command] = line.split(\"\\t\");\n if (!windowId || !paneId || command === undefined) continue;\n panes.push({ paneId, title: title ?? \"\", command, windowId });\n }\n return panes;\n}\n\n/**\n * List panes in the current session, a specific window/session target, or\n * all panes across all sessions when `target` is the literal \"*\".\n */\nexport async function listPanes(target?: string): Promise<TmuxPane[]> {\n if (target === \"*\") {\n const out = await tmux([\n \"list-panes\",\n \"-a\",\n \"-F\",\n \"#{session_name}\\t#{window_id}\\t#{pane_id}\\t#{pane_title}\\t#{pane_current_command}\",\n ]);\n const panes: TmuxPane[] = [];\n for (const line of out.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [sessionName, windowId, paneId, title, command] = line.split(\"\\t\");\n if (!sessionName || !windowId || !paneId || command === undefined) continue;\n panes.push({ paneId, title: title ?? \"\", command, windowId, sessionName });\n }\n return panes;\n }\n\n const args = [\"list-panes\"];\n if (target !== undefined) args.push(\"-t\", target);\n args.push(\"-F\", \"#{pane_id}\\t#{pane_title}\\t#{pane_current_command}\");\n const out = await tmux(args);\n const panes: TmuxPane[] = [];\n for (const line of out.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [paneId, title, command] = line.split(\"\\t\");\n if (!paneId || command === undefined) continue;\n panes.push({ paneId, title: title ?? \"\", command });\n }\n return panes;\n}\n\n/**\n * Split a window and run a command in the new pane. Returns the new pane's\n * stable pane id.\n */\nexport async function splitWindow(opts: SplitWindowOptions): Promise<string> {\n const args = [\"split-window\"];\n if (opts.horizontal !== false) args.push(\"-h\");\n if (opts.detached !== false) args.push(\"-d\");\n args.push(\"-t\", opts.target);\n if (opts.cwd) args.push(\"-c\", opts.cwd);\n appendEnvFlags(args, opts.env);\n args.push(\"-P\", \"-F\", \"#{pane_id}\", opts.command);\n const out = (await tmux(args)).trim();\n assertValidPaneId(out);\n return out;\n}\n\n/**\n * Push one `-e KEY=VALUE` flag per entry into `args`, validating that\n * keys are non-empty and contain no `=` (tmux would error obscurely\n * otherwise; throwing TypeError keeps the failure at the call site).\n * No-op when `env` is undefined or empty.\n *\n * Iteration order follows Object.entries (insertion order); tests\n * shouldn't depend on a specific ordering, only on the presence of\n * each `-e KEY=VALUE` pair in the captured args.\n */\nfunction appendEnvFlags(args: string[], env: Record<string, string> | undefined): void {\n if (!env) return;\n for (const [k, v] of Object.entries(env)) {\n if (k.length === 0) {\n throw new TypeError(\"tmux env key must be non-empty\");\n }\n if (k.includes(\"=\")) {\n throw new TypeError(`tmux env key must not contain '=': ${JSON.stringify(k)}`);\n }\n args.push(\"-e\", `${k}=${v}`);\n }\n}\n\n/** Idempotent: succeeds even if the pane is already gone. */\nexport async function killPane(paneId: string): Promise<void> {\n assertValidPaneId(paneId);\n const result = await currentExecutor([\"kill-pane\", \"-t\", paneId]);\n if (\n result.exitCode !== 0 &&\n !/can't find pane/i.test(result.stderr) &&\n !NO_SERVER_RE.test(result.stderr)\n ) {\n throw new TmuxError([\"kill-pane\", \"-t\", paneId], result.stderr, result.stdout, result.exitCode);\n }\n}\n\nexport async function paneExists(paneId: string): Promise<boolean> {\n if (!isValidPaneId(paneId)) return false;\n // tmux's `display-message -t <bogus>` exits 0 but emits empty output; we\n // must check that the echoed pane id matches what we asked for.\n const result = await currentExecutor([\"display-message\", \"-t\", paneId, \"-p\", \"#{pane_id}\"]);\n if (result.exitCode !== 0) return false;\n return result.stdout.trim() === paneId;\n}\n\nexport async function setPaneTitle(paneId: string, title: string): Promise<void> {\n assertValidPaneId(paneId);\n await tmux([\"select-pane\", \"-t\", paneId, \"-T\", title]);\n}\n\n/**\n * Look up the window id (e.g. `@42`) that contains a given pane id\n * (e.g. `%15`). Used by spawn so we can apply window-scoped options\n * (`pane-border-status`) to the freshly created window.\n *\n * Returns undefined if the pane no longer exists.\n */\nexport async function getWindowIdForPane(paneId: string): Promise<string | undefined> {\n if (!isValidPaneId(paneId)) return undefined;\n const result = await currentExecutor([\"display-message\", \"-t\", paneId, \"-p\", \"#{window_id}\"]);\n if (result.exitCode !== 0) return undefined;\n const id = result.stdout.trim();\n return id.length > 0 ? id : undefined;\n}\n\n/**\n * Single source of truth for the operator opt-out from the mu pane\n * banner / border decorations. Set `MU_BANNER_QUIET=1` to disable.\n * All `enableMuPaneBorders*` helpers self-check this so callers\n * don't have to wrap them in env guards (a footgun: forget the\n * guard and you set the border even when the operator wanted\n * quiet).\n */\nfunction muBannersDisabled(): boolean {\n return process.env.MU_BANNER_QUIET === \"1\";\n}\n\n/**\n * Enable the mu pane border on EVERY window currently in `session`\n * without overriding the user's pane-border format. Idempotent. Best-effort:\n * windows that have vanished mid-iteration are silently skipped. Used\n * by `mu workstream init` (covers the placeholder `_mu` window plus\n * any windows that already exist, e.g. on re-init of an upgraded\n * mu-pre-border session) and by `mu agent spawn` (covers the\n * just-created window so the border shows immediately on attach).\n *\n * No-op (returns 0) when `MU_BANNER_QUIET=1`.\n *\n * Returns the number of windows that received the option.\n */\nexport async function enableMuPaneBordersForSession(session: string): Promise<number> {\n if (muBannersDisabled()) return 0;\n const windows = await listWindows(session).catch(() => []);\n let n = 0;\n for (const w of windows) {\n try {\n await enableMuPaneBorders(w.id);\n n += 1;\n } catch {\n // Window vanished; skip silently. Border is decorative.\n }\n }\n return n;\n}\n\n/**\n * Apply the mu pane border to the window containing `paneId`. This is\n * the spawn/adopt shape: callers have a pane id (from `new-window` or\n * from an adopt target), and need to resolve the enclosing window\n * before calling `enableMuPaneBorders` (a window-scoped option).\n *\n * Self-checks `MU_BANNER_QUIET` and swallows tmux errors — the border\n * is decorative; failing to set it is never load-bearing.\n */\nexport async function enableMuPaneBordersForPane(paneId: string): Promise<void> {\n if (muBannersDisabled()) return;\n const wid = await getWindowIdForPane(paneId).catch(() => undefined);\n if (wid) await enableMuPaneBorders(wid).catch(() => {});\n}\n\n/**\n * Enable a one-line top pane border on a specific window/session target.\n * The user's inherited `pane-border-format` is left intact; mu owns the\n * title's durable context, not border presentation. Idempotent\n * (`set-option` is a write, not a toggle).\n *\n * IMPORTANT: tmux's `pane-border-status` is a **window** option, not a\n * session option. `set-option -t <session>`\n * only updates the active window at call time — windows created later\n * inherit from the GLOBAL value (which is `off` by default and which\n * we deliberately do NOT touch, since changing the global would\n * affect every other tmux session on the user's machine, including\n * dotfile-curated ones).\n *\n * Therefore mu must call this twice:\n * 1. At `mu workstream init` time on the placeholder `_mu` window\n * (so an attached operator sees a border immediately).\n * 2. On every `mu agent spawn` (which calls `tmux new-window`),\n * against the new window's id.\n *\n * The border is tmux chrome, not pane content: it doesn't scroll, it\n * survives copy-mode, and the inner CLI never sees it.\n *\n * Designed as the pane-border visual cue for mu-managed panes.\n */\nexport async function enableMuPaneBorders(target: string): Promise<void> {\n if (muBannersDisabled()) return;\n await tmux([\"set-option\", \"-w\", \"-t\", target, \"pane-border-status\", \"top\"]);\n // Bottom + sides: heavy box-drawing lines so a mu-managed pane is\n // visually distinct even when not the active pane (top carries the\n // labeled status text; the rest of the frame carries the visual\n // \"this is mu\" cue). Cyan-bold for the active pane, dim brightblack\n // for inactive ones, so the operator's eye lands on the pane that\n // currently has focus.\n await tmux([\"set-option\", \"-w\", \"-t\", target, \"pane-border-lines\", \"heavy\"]);\n await tmux([\"set-option\", \"-w\", \"-t\", target, \"pane-active-border-style\", \"fg=cyan,bold\"]);\n await tmux([\"set-option\", \"-w\", \"-t\", target, \"pane-border-style\", \"fg=brightblack\"]);\n}\n\n/**\n * Look up the TTY device path for a pane (e.g. `/dev/ttys012` on macOS,\n * `/dev/pts/3` on Linux). Used by `mu agent kick` to find the\n * foreground process group on the pane's TTY so it can be signalled\n * directly — `tmux send-keys C-c` does NOT propagate to wrapped\n * subprocesses inside a CLI like pi/claude/codex (the CLI catches it\n * itself and treats it as a UI input). The escape hatch is signalling\n * the foreground pgid of the underlying TTY from outside the pane.\n *\n * Throws `PaneNotFoundError` when the pane id is invalid or the pane\n * has vanished. Throws `TmuxError` on any other tmux failure.\n */\nexport async function paneTTY(paneId: string): Promise<string> {\n assertValidPaneId(paneId);\n const result = await currentExecutor([\"display-message\", \"-t\", paneId, \"-p\", \"#{pane_tty}\"]);\n if (result.exitCode !== 0) {\n if (/can't find pane|pane not found/i.test(result.stderr) || NO_SERVER_RE.test(result.stderr)) {\n throw new PaneNotFoundError(paneId, tmuxBackend);\n }\n throw new TmuxError(\n [\"display-message\", \"-t\", paneId, \"-p\", \"#{pane_tty}\"],\n result.stderr,\n result.stdout,\n result.exitCode,\n );\n }\n const tty = result.stdout.trim();\n if (tty === \"\") throw new PaneNotFoundError(paneId, tmuxBackend);\n return tty;\n}\n\nexport async function getPaneTitle(paneId: string): Promise<string | undefined> {\n if (!isValidPaneId(paneId)) return undefined;\n const result = await currentExecutor([\"display-message\", \"-t\", paneId, \"-p\", \"#{pane_title}\"]);\n if (result.exitCode !== 0) return undefined;\n return result.stdout.trimEnd();\n}\n\n/**\n * Read the title of the *current* pane (the one whose shell is running this\n * process), via $TMUX_PANE. Returns undefined when not inside tmux. Used by\n * `mu claim` to derive the agent identity from the pane title — the claim\n * protocol's zero-config identity step.\n */\nexport async function currentPaneTitle(): Promise<string | undefined> {\n const paneId = process.env.TMUX_PANE;\n if (!paneId || !isValidPaneId(paneId)) return undefined;\n return getPaneTitle(paneId);\n}\n\n/**\n * Convenience: read the current pane's title and extract the agent name.\n */\nexport async function currentAgentName(): Promise<string | undefined> {\n const title = await currentPaneTitle();\n if (title === undefined) return undefined;\n return parseAgentNameFromTitle(title);\n}\n\n/**\n * Name of the tmux session this process is running inside, or\n * undefined outside tmux. Gated on `$TMUX` so a call from a plain\n * shell doesn't get whatever session the server happens to consider\n * current.\n */\nexport async function currentSessionName(): Promise<string | undefined> {\n if (!process.env.TMUX) return undefined;\n const result = await currentExecutor([\"display-message\", \"-p\", \"#S\"]);\n if (result.exitCode !== 0) return undefined;\n const name = result.stdout.trim();\n return name === \"\" ? undefined : name;\n}\n\nexport async function selectLayout(window: string, layout: string): Promise<void> {\n await tmux([\"select-layout\", \"-t\", window, layout]);\n}\n\n// ─── Send protocol (the canonical bracketed-paste sequence) ────────────\n\n/**\n * Default pre-send readiness budget in ms. Override with\n * MU_SEND_READINESS_MS; 0 disables the wait entirely.\n *\n * 15s (vs spawn's 10s) because the blocking operation here is an LLM\n * round-trip inside the target TUI, not a process cold-start. pi's\n * post-`/new` \"Naming session before closing…\" step measured ~1.5s on a\n * warm session but is a model call, so it has no useful upper bound.\n */\nexport function defaultSendReadinessMs(): number {\n const raw = process.env.MU_SEND_READINESS_MS;\n if (raw === undefined) return 15_000;\n const parsed = Number.parseInt(raw, 10);\n if (Number.isNaN(parsed) || parsed < 0) return 15_000;\n return parsed;\n}\n\n/** Interval between quiescence polls (ms). */\nconst SEND_READINESS_POLL_MS = 250;\n\n/**\n * How many CONSECUTIVE non-busy polls count as quiescent.\n *\n * One is not enough, and that is the subtle half of this bug. A TUI\n * does not render its modal synchronously: `mu agent send W \"/new\"`\n * returns as soon as tmux delivers Enter, and pi needs ~200-400ms more\n * before the \"Naming session…\" spinner appears. A single poll in that\n * window sees the PREVIOUS frame — still `needs_input` — declares the\n * pane ready, and pastes straight into the modal that is about to\n * appear. Measured directly: with a single poll, quiescence returned\n * true while naming was still running on 2 of 3 attempts.\n *\n * Three polls at 250ms means the pane must look idle for ~500ms of\n * wall-clock before we believe it, which covers the render gap without\n * adding meaningful latency to the common already-idle case.\n */\nconst QUIESCENCE_CONFIRMATIONS = 3;\n\n/** How many times to re-send Enter for text stranded in the input box.\n * One swallowed Enter is the observed failure; a second retry costs\n * nothing when it is not needed. */\nconst SUBMIT_RETRIES = 2;\n\n/** Pause after a retry Enter so the TUI can accept it and repaint\n * before we re-inspect. */\nconst SUBMIT_SETTLE_MS = 600;\n\n/** Samples that must ALL agree before calling a send stranded, and the\n * gap between them. 4 x 700ms ≈ 2.1s, which covers the observed\n * repaint lag during which a delivered prompt still reads as count==1. */\nconst SUBMIT_VERIFY_SAMPLES = 4;\nconst SUBMIT_VERIFY_INTERVAL_MS = 700;\n\n/**\n * Markers that mean \"the agent is mid-turn\", as opposed to \"a modal is\n * up\". Both render a spinner, so the spinner alone cannot tell them\n * apart. `to interrupt)` is the same literal the status detector keys\n * on for busy; `Working` covers pi's plain progress line.\n */\nconst WORK_MARKERS: readonly string[] = [\"to interrupt)\", \"Working\"];\n\n/** True when the pane tail shows the agent working on a turn (rather\n * than a modal / re-init spinner). Only the last 40 lines count, so a\n * work marker scrolled far above cannot make a live modal look like an\n * in-flight turn. See awaitPaneQuiescence. */\nexport function hasWorkMarker(scrollback: string): boolean {\n const tail = scrollback.split(\"\\n\").slice(-40).join(\"\\n\");\n return WORK_MARKERS.some((m) => tail.includes(m));\n}\n\n/**\n * Block until the pane is ready to ACCEPT input, or the budget expires.\n * Returns true if the pane quiesced, false on timeout. Throws the\n * capture's TmuxError at once when the pane or the server is gone:\n * a dead pane never quiesces, so polling it would only burn the whole\n * budget before the send fails anyway.\n *\n * WHY THIS EXISTS (dogfood_send_after_new_dropped, reproduced 3/6 at\n * sleep=0.3s and 1/5 at sleep=2s against a real pi pane):\n *\n * `mu agent send W \"/new\"` makes pi run an ASYNC \"Naming session before\n * closing…\" step — an LLM call — before the new session exists. A\n * bracketed paste that arrives while that modal is up is ACCEPTED, but\n * the Enter that follows it is SWALLOWED. The text is therefore left\n * sitting in the new session's input box, typed but never submitted —\n * exactly the reported symptom: pane at needs_input, context 0.0%, no\n * error anywhere, and an orchestrator waiting on a task the agent never\n * started.\n *\n * (Verified rather than assumed: pasting during the modal and capturing\n * afterwards shows the probe string exactly ONCE, in the input box, on\n * 3 of 3 attempts. Re-sending Enter afterwards took it to 2 and moved\n * context 0.0% -> 2.4%, which is what makes recovery possible.)\n *\n * Because the blocker is a model call, no fixed sleep can be correct —\n * `sleep 2` is a coin flip, not a fix. The signal already existed: pi's\n * spinner is a Braille glyph, so the private input-timing helper can wait\n * for it to clear before sending.\n *\n * `needs_permission` counts as ready: a pane sitting on a confirm\n * dialog is waiting for a keystroke, and refusing to send would break\n * the documented \"answer the prompt with `mu agent send`\" flow.\n *\n * IMPORTANT — what this does NOT wait for: an agent that is BUSY doing\n * the work you gave it. Queuing a follow-up into a working agent is a\n * legitimate, documented pattern, and pi accepts it. Waiting on that\n * made every send into a working agent pay the full budget — measured\n * at 14.5s of 15s, versus milliseconds before. The two cases are told\n * apart by WHAT is busy, not by when:\n *\n * agent working — tail carries a work marker (`to interrupt)`,\n * `Working`). Send immediately; pi queues it.\n * modal / re-init — spinner with NO work marker, which is what pi's\n * \"Naming session before closing…\" looks like.\n * Wait it out; this is the one that eats the Enter.\n */\nexport async function awaitPaneQuiescence(paneId: string, budgetMs: number): Promise<boolean> {\n if (budgetMs <= 0) return true;\n const deadline = Date.now() + budgetMs;\n let calm = 0;\n for (;;) {\n // A transient capture failure counts as busy; a vanished pane is fatal.\n const scrollback = await capturePane(paneId, { lines: 50 }).catch((err: unknown) => {\n if (isPaneGoneError(err)) throw err;\n return undefined;\n });\n const busy = scrollback === undefined || paneLooksBusy(scrollback);\n // Busy BECAUSE the agent is working a turn: not a re-init modal.\n // Send now and let the TUI queue it behind the current turn.\n if (busy && scrollback !== undefined && hasWorkMarker(scrollback)) return true;\n if (!busy) {\n calm++;\n if (calm >= QUIESCENCE_CONFIRMATIONS) return true;\n } else {\n // Went busy after looking idle: a modal rendered late. Reset the\n // streak so it cannot sneak in behind that early idle reading.\n calm = 0;\n }\n const remaining = deadline - Date.now();\n if (remaining <= 0) return false;\n await currentSleep(Math.min(SEND_READINESS_POLL_MS, remaining));\n }\n}\n\n/**\n * Longest prefix of `text` that is safe to look for in a pane capture.\n * The TUI soft-wraps its input box, so only the first line survives\n * intact. 24 chars is comfortably under any sane terminal width while\n * still being specific enough not to false-positive on chrome.\n */\nfunction pasteProbe(text: string): string {\n const firstLine = (text.split(\"\\n\")[0] ?? \"\").trim();\n return firstLine.slice(0, 24);\n}\n\n/**\n * True when `probe` looks SUSTAINED-unsubmitted: visible exactly once,\n * on a pane that is not working, across every sample in a short window.\n *\n * Why all three conditions, each learned from a false positive on a\n * real pane:\n *\n * - EXACTLY ONCE. Text sitting in the input box appears once. A\n * submitted prompt ends up appearing twice (the TUI echoes it into\n * the transcript above its response) or zero times (it scrolled\n * past). Zero therefore counts as delivered — treating \"cannot see\n * it\" as failure would warn on every long send.\n * - NOT BUSY. A busy pane is working on something, which is what a\n * delivered prompt looks like.\n * - SUSTAINED. This is the one that matters most. A successful submit\n * legitimately passes THROUGH count==1 for up to ~2s while the TUI\n * repaints, before the echo makes it 2. Sampling once inside that\n * window reports a strand that has already gone through, which was\n * a real false warning during development. Requiring every sample\n * to agree removes it.\n *\n * Cost when all is well: the first sample usually shows 0 or 2 and the\n * function returns immediately.\n */\nasync function isTextStranded(paneId: string, probe: string): Promise<boolean> {\n for (let i = 0; i < SUBMIT_VERIFY_SAMPLES; i++) {\n if (i > 0) await currentSleep(SUBMIT_VERIFY_INTERVAL_MS);\n const capture = await capturePane(paneId, { lines: 50 }).catch(() => undefined);\n if (capture === undefined) return false;\n if (paneLooksBusy(capture)) return false;\n let count = 0;\n let idx = capture.indexOf(probe);\n while (idx !== -1 && count < 2) {\n count++;\n idx = capture.indexOf(probe, idx + probe.length);\n }\n if (count !== 1) return false;\n }\n return true;\n}\n\n/** Default handler for an unconfirmed send: warn on stderr. Loud by\n * design — the whole bug was that this case was silent. */\nfunction defaultUndeliveredWarning(warning: SendWarning): void {\n console.warn(`warning: ${warning.message}`);\n}\n\n/**\n * Send a single line of text to a pane and submit it.\n *\n * Sequence:\n * 0. wait until the pane is not mid-modal (see awaitPaneQuiescence)\n * 1. exit copy mode (silent if not in copy mode)\n * 2. load text into a uniquely-named tmux buffer\n * 3. paste with bracketed-paste mode (-p) so apps treat as literal text;\n * delete buffer after paste (-d); preserve LF (-r)\n * 4. wait MU_SEND_DELAY_MS (default 500) so the agent ingests the text\n * 5. send Enter as a real key event\n * 6. confirm the Enter took; re-send it if the text is stranded\n *\n * DELIVERY CONTRACT (dogfood_send_after_new_dropped): exit 0 must not\n * be able to mean \"silently dropped\". After Enter, the pane is checked\n * for text left STRANDED in the input box — typed but unsubmitted,\n * which is what a swallowed Enter looks like. If found, Enter is\n * re-sent (up to SUBMIT_RETRIES times), which recovers the prompt.\n * `onUndelivered` fires only if it is STILL stranded afterwards.\n *\n * Not a throw, deliberately: by then the text is in the agent's input\n * box, so the operator's next `mu agent read` shows it and a bare Enter\n * finishes the job. Failing the command would also break every existing\n * caller for a usually-recoverable condition. The warning is loud and\n * names the recovery step.\n *\n * Naive `send-keys \"<text>\"` would let characters like /, ?, f, : be\n * interpreted by the agent's TUI or by tmux's copy mode. Always use this.\n */\nexport async function sendToPane(\n paneId: string,\n text: string,\n opts: SendOptions = {},\n): Promise<void> {\n assertValidPaneId(paneId);\n\n // 0. Wait out an in-flight modal / re-init. A TUI rendering one\n // swallows the Enter, stranding the paste in the input box.\n const readinessMs = opts.readinessMs ?? defaultSendReadinessMs();\n const quiesced = await awaitPaneQuiescence(paneId, readinessMs);\n\n // 1. Exit copy mode silently. -q suppresses errors when not in copy mode.\n const copyResult = await currentExecutor([\"copy-mode\", \"-q\", \"-t\", paneId]);\n // Even with -q, some tmux versions report errors. Swallow non-fatal.\n if (copyResult.exitCode !== 0 && /can't find pane|no current target/i.test(copyResult.stderr)) {\n throw new TmuxError(\n [\"copy-mode\", \"-q\", \"-t\", paneId],\n copyResult.stderr,\n copyResult.stdout,\n copyResult.exitCode,\n );\n }\n\n // 2. Load text into a uniquely-named buffer.\n const bufferName = `mu-send-${process.pid}-${Date.now()}-${Math.floor(Math.random() * 1e6)}`;\n await tmux([\"set-buffer\", \"-b\", bufferName, text]);\n\n // 3. Bracketed paste: -p wraps in \\e[200~...\\e[201~ so apps see literal\n // text; -d deletes buffer after paste; -r preserves LF (no CR conversion).\n try {\n await tmux([\"paste-buffer\", \"-p\", \"-d\", \"-r\", \"-b\", bufferName, \"-t\", paneId]);\n } catch (err) {\n // Best-effort buffer cleanup if paste failed before -d took effect.\n await currentExecutor([\"delete-buffer\", \"-b\", bufferName]).catch(() => {});\n throw err;\n }\n\n // 4. Wait for the agent CLI to ingest the pasted text.\n const delay = opts.delayMs ?? defaultSendDelayMs();\n if (delay > 0) await currentSleep(delay);\n\n // 5. Submit. Enter must be a real key event, not part of the paste.\n await tmux([\"send-keys\", \"-t\", paneId, \"Enter\"]);\n\n // 6. Confirm the Enter took. readinessMs: 0 opts out of the whole\n // wrapper (bare 4-command protocol; unit tests mock captures away).\n if (readinessMs <= 0) return;\n\n const probe = pasteProbe(text);\n if (probe.length === 0) return; // nothing observable to verify\n\n for (let attempt = 0; attempt <= SUBMIT_RETRIES; attempt++) {\n if (!(await isTextStranded(paneId, probe))) return;\n if (attempt === SUBMIT_RETRIES) break;\n // Sustained strand on a calm pane: the Enter was swallowed. Re-send.\n await tmux([\"send-keys\", \"-t\", paneId, \"Enter\"]);\n await currentSleep(SUBMIT_SETTLE_MS);\n }\n\n const warn = opts.onUndelivered ?? defaultUndeliveredWarning;\n warn({\n paneId,\n reason: quiesced ? \"paste-vanished\" : \"busy-at-deadline\",\n message: `send to ${paneId} was NOT submitted: the text is still sitting unsubmitted in the pane's input box after ${SUBMIT_RETRIES + 1} Enter attempts. The agent has NOT seen it. Press Enter in the pane, or re-send.`,\n });\n}\n\n// ─── Capture ───────────────────────────────────────────────────────────\n\n/**\n * Read pane scrollback as plain text (no ANSI escapes).\n *\n * - No options: full scrollback (`-S - -E -`)\n * - `lines: 0`: visible pane only\n * - `lines: N`: last N lines, ending at the last non-blank row\n *\n * `-S -N` counts from the TOP of the visible screen, so tmux returns the\n * N rows above the screen plus every visible row, blank rows below the\n * cursor included. That range always holds the last N non-blank-ended\n * lines, so drop the trailing blank rows and keep the last N.\n */\nexport async function capturePane(paneId: string, opts: CaptureOptions = {}): Promise<string> {\n assertValidPaneId(paneId);\n const args = [\"capture-pane\", \"-t\", paneId, \"-p\"];\n if (opts.lines === undefined) {\n args.push(\"-S\", \"-\", \"-E\", \"-\");\n } else if (opts.lines > 0) {\n args.push(\"-S\", `-${opts.lines}`);\n return lastLines(await tmux(args), opts.lines);\n }\n return tmux(args);\n}\n\n/** The last `n` lines of `text` after dropping trailing blank lines.\n * Keeps a trailing newline when `text` had one (tmux prints one). */\nfunction lastLines(text: string, n: number): string {\n const lines = text.split(\"\\n\");\n let end = lines.length;\n while (end > 0 && (lines[end - 1] ?? \"\").trim() === \"\") end--;\n if (end === 0) return \"\";\n const kept = lines.slice(Math.max(0, end - n), end).join(\"\\n\");\n return text.endsWith(\"\\n\") ? `${kept}\\n` : kept;\n}\n\n// ─── Backend record ────────────────────────────────────────────────────\n\n/**\n * True iff tmux can be reached right now. `tmux -V` rather than a PATH\n * probe: a tmux binary that cannot execute (broken install, missing\n * libtinfo) is not an available backend, and the version call is the\n * cheapest thing that proves the binary actually runs.\n *\n * Deliberately does NOT require `$TMUX` — mu creates its own detached\n * sessions, so a usable tmux server is enough. `$TMUX` only tells us\n * whether the CALLER is inside a pane, which is a detection-precedence\n * signal (see ./detect.ts), not an availability one.\n */\nasync function tmuxAvailable(): Promise<boolean> {\n try {\n await tmux([\"-V\"]);\n return true;\n } catch {\n return false;\n }\n}\n\n// ─── Attach ────────────────────────────────────────────────────\n//\n// From INSIDE a tmux client the right verb is `switch-client`, which\n// repoints the existing client and returns immediately. From outside\n// it is `attach-session` followed by a separate `select-window`: `tmux\n// attach -t session:window` does not reliably select the window across\n// tmux versions, and the select must run after the attach detaches.\n\nfunction attachTargetSpec(target: AttachTarget): string {\n return target.window === undefined ? target.session : `${target.session}:${target.window}`;\n}\n\nexport function attachHint(target: AttachTarget): string {\n const spec = attachTargetSpec(target);\n if (target.inside === true) return `tmux switch-client -t ${spec}`;\n if (target.window === undefined) return `tmux attach -t ${target.session}`;\n return `tmux attach -t ${target.session} && tmux select-window -t ${spec}`;\n}\n\nexport function attachCommands(target: AttachTarget): readonly MuxCommand[] {\n const spec = attachTargetSpec(target);\n if (target.inside === true) {\n return [{ command: \"tmux\", args: [\"switch-client\", \"-t\", spec] }];\n }\n const steps: MuxCommand[] = [{ command: \"tmux\", args: [\"attach-session\", \"-t\", target.session] }];\n if (target.window !== undefined) {\n // Best-effort: if it fails the user is at least in the right session.\n steps.push({ command: \"tmux\", args: [\"select-window\", \"-t\", spec], optional: true });\n }\n return steps;\n}\n\n// ─── Diagnostics ────────────────────────────────────────────────\n\nexport function paneNotFoundNextSteps(paneId: string): NextStep[] {\n return [\n {\n intent: `Verify the pane id ${paneId} actually exists`,\n command: `tmux display-message -t ${paneId} -p '#{pane_id} #{pane_title}'`,\n },\n {\n intent: \"List all live panes across all sessions\",\n command:\n \"tmux list-panes -a -F '#{session_name}:#{window_id}.#{pane_id}\\\\t#{pane_title}\\\\t#{pane_current_command}'\",\n },\n ];\n}\n\n/**\n * Version probe + the two ambient env facts tmux exposes. Returns DATA;\n * `mu doctor` owns every string the user sees, so a second backend\n * reporting different env vars needs no doctor change.\n */\nexport async function healthCheck(): Promise<MuxHealth> {\n let version: string | null = null;\n try {\n version = (await tmux([\"-V\"])).trim();\n } catch {\n version = null;\n }\n return {\n name: \"tmux\",\n ok: version !== null,\n version,\n env: [\n { name: \"$TMUX\", value: process.env.TMUX ?? null },\n { name: \"$TMUX_PANE\", value: process.env.TMUX_PANE ?? null },\n ],\n remediation: \"install tmux ≥ 3.0\",\n };\n}\n\n/** `MuxBackend` with tmux's synchronous attach methods: SDK callers of\n * `tmuxBackend.attachHint()` keep getting a string, not a Promise. */\nexport interface TmuxBackend extends MuxBackend {\n attachHint(target: AttachTarget): string;\n attachCommands(target: AttachTarget): readonly MuxCommand[];\n}\n\n/**\n * The tmux implementation of `MuxBackend`. A frozen record of the\n * module's functions — no state of its own, so swapping backends is a\n * pointer assignment (see ./detect.ts).\n */\nexport const tmuxBackend: TmuxBackend = Object.freeze({\n name: \"tmux\" as const,\n available: tmuxAvailable,\n\n isValidPaneId,\n assertValidPaneId,\n\n listSessions,\n sessionExists,\n newSession,\n newSessionWithPane,\n killSession,\n\n listWindows,\n newWindow,\n selectLayout,\n\n listPanes,\n listPanesInSession,\n splitWindow,\n killPane,\n paneExists,\n paneTTY,\n\n setPaneTitle,\n getPaneTitle,\n currentAgentName,\n currentSessionName,\n\n sendToPane,\n capturePane,\n\n enableMuPaneBordersForSession,\n enableMuPaneBordersForPane,\n\n attachHint,\n attachCommands,\n\n paneNotFoundNextSteps,\n healthCheck,\n});\n","// tmux paste path for non-pi agents; pi agents use the control socket.\n//\n// Private pane-text checks used by tmux's send protocol. These answer whether\n// input can be delivered, not what state an agent is in.\n\nconst TAIL_WINDOW_LINES = 100;\nconst TAIL_LINES = 20;\nconst BUSY_MARKERS: readonly string[] = [\"to interrupt)\"];\nconst BRAILLE_SPINNER_RE = /[\\u2800-\\u28ff]/;\n\n/** Take the recent non-blank pane tail used by the input-timing checks. */\nexport function extractTail(scrollback: string): string {\n const lines = scrollback.split(\"\\n\");\n const window = lines.slice(-TAIL_WINDOW_LINES);\n let end = window.length;\n while (end > 0 && (window[end - 1] ?? \"\").trim() === \"\") end--;\n return window.slice(Math.max(0, end - TAIL_LINES), end).join(\"\\n\");\n}\n\n/** True when recent pane text shows input is temporarily unsafe to submit. */\nexport function paneLooksBusy(scrollback: string): boolean {\n const tail = extractTail(scrollback);\n return BUSY_MARKERS.some((marker) => tail.includes(marker)) || BRAILLE_SPINNER_RE.test(tail);\n}\n","// mu — shared multiplexer backend contracts and typed errors.\n//\n// A \"mux\" is the terminal multiplexer that owns panes. mu supports one\n// active backend per invocation, chosen by `detectMux()` in ./detect.js.\n// Everything backend-specific lives behind `MuxBackend`:\n//\n// - session / window / pane topology\n// - the send protocol (bracketed paste on tmux)\n// - scrollback capture\n// - PANE ID VALIDATION — tmux `%15` vs herdr `w1:p1`, which is why\n// there is no global pane-id regex\n// - ACTOR IDENTITY fallback — how to ask \"which agent is this pane?\"\n//\n// Same shape as src/vcs/: a types module, one file per impl, and an\n// index that dispatches. See docs/VOCABULARY.md § \"mux backend\".\n\nimport type { HasNextSteps, NextStep } from \"../output.js\";\n\n/** The multiplexers mu can drive. */\nexport type MuxBackendName = \"herdr\" | \"tmux\";\n\n/** Runtime states a mux backend can report directly for one pane. */\nexport type MuxPaneStatus = \"busy\" | \"needs_input\" | \"needs_permission\";\n\n// ─── Domain types ──────────────────────────────────────────────────────\n//\n// Deliberately narrow: these carry only the fields mu actually reads.\n// A backend that knows more about a pane keeps that to itself.\n\nexport interface MuxSession {\n name: string;\n}\n\nexport interface MuxWindow {\n /** Backend-specific window handle (tmux `@1`). */\n id: string;\n name: string;\n /** Session this window belongs to (only set by cross-session listings). */\n sessionName?: string;\n}\n\nexport interface MuxPane {\n /** Backend-specific stable pane id (tmux `%15`). Never a volatile index. */\n paneId: string;\n /** The agent's name, in mu's convention. */\n title: string;\n /** Current foreground command (e.g. \"claude\", \"node\", \"bash\"). */\n command: string;\n /** Window this pane lives in. Only set by cross-window listings. */\n windowId?: string;\n /** Session this pane lives in. Only set by cross-session listings. */\n sessionName?: string;\n}\n\nexport interface NewSessionOptions {\n detached?: boolean;\n windowName?: string;\n command?: string;\n /** Initial working directory for the first pane (`-c <path>`). */\n cwd?: string;\n /** Extra env vars to set in the new pane. On tmux this is `-e KEY=VALUE`\n * (tmux 3.0+), which sets the variable in the new pane's environment\n * without polluting the server's global env. */\n env?: Record<string, string>;\n}\n\nexport interface NewSessionWithPaneOptions {\n windowName: string;\n command: string;\n cwd?: string;\n detached?: boolean;\n /** Extra env vars to set in the new pane. */\n env?: Record<string, string>;\n}\n\nexport interface NewWindowOptions {\n /** Target session. Required if invoking outside an existing client. */\n session?: string;\n /** Window name. Maps to the agent's `tab:` value (or its name if no tab). */\n name: string;\n /** Command to run in the first pane. */\n command: string;\n /** If true, do not switch focus. Defaults to true. */\n detached?: boolean;\n /** Initial working directory (`-c <path>`). */\n cwd?: string;\n /** Extra env vars to set in the new pane. */\n env?: Record<string, string>;\n}\n\nexport interface SplitWindowOptions {\n /** Target window or pane (e.g. \":Backend\" or \"%15\"). */\n target: string;\n command: string;\n /** Horizontal split (side-by-side). Default true. */\n horizontal?: boolean;\n detached?: boolean;\n /** Initial working directory for the new pane (`-c <path>`). */\n cwd?: string;\n /** Extra env vars to set in the new pane. */\n env?: Record<string, string>;\n}\n\n/** Where an attach should land the user. */\nexport interface AttachTarget {\n /** Mux session name, e.g. `mu-auth`. */\n session: string;\n /** Window inside that session (the agent's `tab`, or its name). */\n window?: string;\n /** True when the caller is already inside a client of this mux, which\n * on tmux means `switch-client` rather than `attach-session`. */\n inside?: boolean;\n}\n\n/** One argv the caller may execute to hand the terminal to the mux. */\nexport interface MuxCommand {\n command: string;\n args: readonly string[];\n /** Best-effort step: a non-zero exit is not a failure of the attach\n * as a whole (tmux's post-attach `select-window`, for instance). */\n optional?: boolean;\n}\n\n/** What a backend can say about its own health. Deliberately DATA, not\n * prose: `mu doctor` owns all rendering (human and --json). */\nexport interface MuxHealth {\n /** Backend name, echoed so doctor can label the row. */\n name: MuxBackendName;\n /** True iff the backend answered a version probe. */\n ok: boolean;\n /** Version string as the backend reports it, or null when unreachable. */\n version: string | null;\n /** Ambient env facts this backend cares about, in display order\n * (tmux: $TMUX / $TMUX_PANE). Values are null when unset. */\n env: readonly { name: string; value: string | null }[];\n /** Remediation line shown when `ok` is false. */\n remediation: string;\n}\n\nexport interface SendOptions {\n /** Override the default delay between paste and Enter, in ms. */\n delayMs?: number;\n /**\n * Override the pre-send readiness budget, in ms. 0 disables the wait\n * AND the post-send submit verification.\n */\n readinessMs?: number;\n /** Called when the send could not be confirmed as submitted. */\n onUndelivered?: (warning: SendWarning) => void;\n}\n\n/** Why a send could not be confirmed as submitted. */\nexport interface SendWarning {\n paneId: string;\n /** 'paste-vanished' — pane looked calm, but the text stayed stranded\n * in the input box. 'busy-at-deadline' — pane never quiesced within\n * the budget and the text stayed stranded. */\n reason: \"busy-at-deadline\" | \"paste-vanished\";\n message: string;\n}\n\n/**\n * What `startAgentInPane` needs to know. mu has already resolved the\n * command it WOULD have run in a create-and-run backend; a backend that\n * starts agents itself needs both that string and the `--cli` key it came\n * from, because those select different routes (see the herdr impl).\n */\nexport interface StartAgentInPaneOptions {\n /** An existing pane, sitting at its interactive shell prompt. */\n paneId: string;\n /** mu's agent name. Backends with their own agent registry use it as\n * the mux-level handle too. */\n name: string;\n /** mu's `--cli` key, e.g. \"pi\". A backend that classifies agent kinds\n * natively maps this onto its own kind vocabulary. */\n cli: string;\n /** Fully resolved command string mu would otherwise have spawned. */\n command: string;\n /**\n * Where `command` came from. A backend that resolves the executable\n * ITSELF (rather than running the string mu hands it) must not silently\n * discard an operator's explicit choice, and needs to know which one it\n * is so the diagnostic can name the right thing to change:\n *\n * \"cli-key\" — just the `--cli` value; nothing to honour, proceed.\n * \"env\" — a `MU_<UPPER_CLI>_COMMAND` override.\n * \"explicit\" — an explicit `--command \"…\"`.\n */\n commandSource: \"cli-key\" | \"env\" | \"explicit\";\n}\n\nexport interface CaptureOptions {\n /**\n * Number of trailing lines to capture. Omitted = full scrollback.\n * 0 = visible pane only.\n */\n lines?: number;\n}\n\n// ─── Title parsing ───────────────────────────────────────────────\n\n/**\n * Extract the agent-name token from a (possibly composed) pane title.\n * mu's `composeAgentTitle` renders titles as `name · task_id`;\n * the agent name is always the first ' · '-separated token. Adopted\n * panes mu never re-titled have just the name — still parses.\n *\n * Backend-INDEPENDENT: the format is mu's, not any multiplexer's, so\n * every backend that can read a pane title parses it the same way.\n * Lives here rather than in an impl so migrated call sites can use it\n * without importing tmux.\n */\nexport function parseAgentNameFromTitle(title: string): string {\n const idx = title.indexOf(\" · \");\n return idx === -1 ? title.trim() : title.slice(0, idx).trim();\n}\n\n// ─── Errors ────────────────────────────────────────────────────────────\n\n/**\n * Base class for \"the multiplexer itself failed\". Each backend\n * subclasses it (tmux → `TmuxError`) so `handle()` can map the whole\n * family to exit code 5 (substrate unavailable) with one `instanceof`.\n */\nexport class MuxError extends Error implements HasNextSteps {\n override name: string = \"MuxError\";\n errorNextSteps(): NextStep[] {\n return [{ intent: \"Run health check\", command: \"mu doctor\" }];\n }\n}\n\n/**\n * Thrown when a verb references a pane id that doesn't exist on the\n * running multiplexer. Distinct from `MuxError` (which wraps any mux\n * command failure) so callers can map it to a specific exit code\n * (`mu` maps it to 5 alongside other mux issues, but the message is\n * more actionable than raw backend stderr).\n *\n * The backend-specific half of the remediation comes from the backend\n * that raised it: showing a herdr user `tmux info | head` would be\n * worse than showing no hint at all. Callers that construct this error\n * already hold the active backend, so passing it is free; the\n * no-backend form degrades to mu's own verbs only.\n */\nexport class PaneNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"PaneNotFoundError\";\n constructor(\n public readonly paneId: string,\n private readonly backend?: MuxDiagnostics,\n ) {\n super(`${backend?.name ?? \"mux\"} pane not found: ${paneId}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n ...(this.backend?.paneNotFoundNextSteps(this.paneId) ?? []),\n {\n intent: \"List workstreams to choose the right scope\",\n command: \"mu workstream list\",\n },\n {\n intent: \"List registered agents and orphan panes in that scope\",\n command: \"mu agent list -w <workstream>\",\n },\n ];\n }\n}\n\n/** The slice of a backend `PaneNotFoundError` needs. Structural so the\n * error type doesn't depend on the whole backend contract. */\nexport interface MuxDiagnostics {\n readonly name: MuxBackendName;\n paneNotFoundNextSteps(paneId: string): NextStep[];\n}\n\n/**\n * Thrown by `detectMux()` when no supported multiplexer is available.\n * Maps to exit 5 (substrate unavailable) — the same bucket as \"tmux is\n * installed but the server is unreachable\", because from the caller's\n * point of view both mean \"mu cannot reach a pane right now\".\n */\nexport class NoMultiplexerError extends Error implements HasNextSteps {\n override readonly name = \"NoMultiplexerError\";\n constructor(public readonly tried: readonly string[]) {\n super(\n `no supported multiplexer found (tried: ${tried.join(\", \")}). mu drives agents through tmux panes.`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"Run health check\", command: \"mu doctor\" },\n { intent: \"Verify tmux is installed\", command: \"tmux -V\" },\n { intent: \"Force a specific backend\", command: \"MU_MUX=tmux mu <verb>\" },\n ];\n }\n}\n\n// ─── The backend contract ──────────────────────────────────────────────\n\n/**\n * One multiplexer implementation. Methods mirror the operations mu\n * actually performs; nothing is here speculatively.\n *\n * Implementations must be stateless value objects (a frozen record of\n * functions), so swapping the active backend is a pointer assignment\n * and tests can install a fake without teardown ordering hazards.\n */\nexport interface MuxBackend {\n readonly name: MuxBackendName;\n\n /** True iff this backend can drive panes on this machine right now.\n * Consulted by `detectMux()` in precedence order. */\n available(): Promise<boolean>;\n\n // — pane id validation (shape is backend-specific) —\n isValidPaneId(s: string): boolean;\n assertValidPaneId(s: string): void;\n\n // — sessions —\n listSessions(): Promise<MuxSession[]>;\n sessionExists(name: string): Promise<boolean>;\n newSession(name: string, opts?: NewSessionOptions): Promise<void>;\n newSessionWithPane(name: string, opts?: NewSessionWithPaneOptions): Promise<string>;\n killSession(name: string): Promise<void>;\n\n // — windows —\n listWindows(session?: string): Promise<MuxWindow[]>;\n newWindow(opts: NewWindowOptions): Promise<string>;\n selectLayout(window: string, layout: string): Promise<void>;\n\n // — panes —\n listPanes(target?: string): Promise<MuxPane[]>;\n listPanesInSession(session: string): Promise<MuxPane[]>;\n splitWindow(opts: SplitWindowOptions): Promise<string>;\n killPane(paneId: string): Promise<void>;\n paneExists(paneId: string): Promise<boolean>;\n paneTTY(paneId: string): Promise<string>;\n\n // — identity —\n //\n // The FALLBACK rung of actor resolution. `$MU_AGENT_NAME` is checked\n // first by the caller (src/tasks/claim.ts) and is backend-independent;\n // this exists because adopted panes predate that env injection.\n setPaneTitle(paneId: string, title: string): Promise<void>;\n getPaneTitle(paneId: string): Promise<string | undefined>;\n currentAgentName(): Promise<string | undefined>;\n /** Name of the session the CALLER is running inside, or undefined\n * when outside one. Backs the `mu-<name>` rung of workstream\n * auto-detection, which is why it is identity and not topology. */\n currentSessionName(): Promise<string | undefined>;\n\n // — spawn —\n //\n // OPTIONAL, and its absence is the DEFAULT shape: tmux creates a pane\n // and runs a command in one atomic call, so `NewWindowOptions.command`\n // et al. carry the command and there is nothing left to do.\n //\n // A backend that has no create-and-run form implements this instead.\n // mu then creates the pane bare, calls `startAgentInPane`, and — because\n // such a backend only returns once IT considers the agent ready for\n // input — SKIPS its own liveness/readiness polling. This is a capability\n // flag, not a backend name check: `src/agents/spawn.ts` branches on the\n // method being present, never on `mux.name`.\n startAgentInPane?(opts: StartAgentInPaneOptions): Promise<void>;\n\n // — io —\n sendToPane(paneId: string, text: string, opts?: SendOptions): Promise<void>;\n capturePane(paneId: string, opts?: CaptureOptions): Promise<string>;\n\n /**\n * The pane's runtime state as the mux itself reports it, or undefined\n * when the pane is gone or unclassified. tmux omits this capability;\n * herdr reports it directly.\n */\n paneStatus?(paneId: string): Promise<MuxPaneStatus | undefined>;\n\n // — chrome (decorative; a backend with no equivalent no-ops) —\n enableMuPaneBordersForSession(session: string): Promise<number>;\n enableMuPaneBordersForPane(paneId: string): Promise<void>;\n\n // — attach —\n //\n // Two shapes for the same intent because mu prints attach hints after\n // workstream creation while the TUI's `a` key executes the steps.\n // Neither may hardcode a tmux string. Sync or async: tmux builds the\n // line from the target alone, herdr must first resolve the session\n // name to an opaque workspace id. Callers always `await` the result.\n\n /** Copy-pasteable shell line that lands the user on `target`. */\n attachHint(target: AttachTarget): string | Promise<string>;\n /** The same attach, as argv steps to spawn in order. */\n attachCommands(target: AttachTarget): readonly MuxCommand[] | Promise<readonly MuxCommand[]>;\n\n // — diagnostics —\n\n /** Backend-specific `PaneNotFoundError` remediation. */\n paneNotFoundNextSteps(paneId: string): NextStep[];\n /** Version + ambient facts for `mu doctor`. Never throws: an\n * unreachable backend reports `ok: false`. */\n healthCheck(): Promise<MuxHealth>;\n}\n","// mu — multiplexer backend detection.\n//\n// Picks the ONE backend a given mu invocation drives. Mirrors\n// detectBackend() in src/vcs/index.ts, with one difference: the VCS\n// backend is a property of a directory, so it is re-detected per call;\n// the mux backend is a property of the PROCESS, so it is resolved once\n// and cached (see `activeMux`).\n\nimport { herdrBackend } from \"./herdr.js\";\nimport { tmuxBackend } from \"./tmux.js\";\nimport { type MuxBackend, type MuxBackendName, NoMultiplexerError } from \"./types.js\";\n\n/** Every backend mu knows about, in detection-precedence order. tmux is\n * the incumbent and wins pure availability ties; herdr is selected by\n * the narrower `$HERDR_ENV` ambient signal (see `detectMux`). */\nconst BACKENDS: readonly MuxBackend[] = [tmuxBackend, herdrBackend];\n\n/** Look up a backend by name. Throws on unknown name. Backs `MU_MUX`. */\nexport function muxByName(name: MuxBackendName): MuxBackend {\n for (const backend of BACKENDS) {\n if (backend.name === name) return backend;\n }\n throw new Error(`unknown mux backend: ${name}`);\n}\n\n/** Valid `MU_MUX` values, for error messages. */\nconst KNOWN_NAMES: readonly string[] = BACKENDS.map((b) => b.name);\n\n/**\n * Resolve the active multiplexer backend.\n *\n * The ladder, in order:\n *\n * 1. `MU_MUX=<name>` — explicit override. Also the test seam. An\n * unknown value throws rather than silently falling through: a\n * typo'd backend name should fail loud, not quietly run on tmux.\n * 2. Ambient signal — an env var proving the CALLER is already\n * inside a managed pane of that mux ($HERDR_ENV for herdr, $TMUX\n * or $TMUX_PANE for tmux). The most specific signal wins, since a\n * mux can run nested inside another: herdr panes routinely host a\n * tmux server, so BOTH sets of vars can be present at once and\n * $HERDR_ENV — the narrower claim — is checked first.\n * 3. Availability — whichever backend's binary actually runs.\n * Ties break in BACKENDS order (tmux is the incumbent).\n * 4. Throw `NoMultiplexerError`.\n *\n * Note rungs 2 and 3 are separate on purpose. `$TMUX` says \"the caller\n * is in a tmux pane\"; `tmux -V` says \"tmux works here\". mu can spawn a\n * detached session from a plain shell, so rung 3 alone is sufficient to\n * operate — rung 2 exists only to pick the RIGHT mux when several are\n * installed.\n */\nexport async function detectMux(): Promise<MuxBackend> {\n const override = process.env.MU_MUX;\n if (override !== undefined && override.length > 0) {\n return muxByName(override as MuxBackendName);\n }\n\n // herdr FIRST. herdr can run inside tmux and vice versa, so both\n // signals can be live simultaneously; `$HERDR_ENV=1` is the narrower\n // one (herdr sets it only in panes IT manages) and must win. The\n // exact-\"1\" comparison is what `herdr --skill` mandates: any other\n // value, including \"0\" or \"true\", is not the documented contract.\n if (process.env.HERDR_ENV === \"1\") return herdrBackend;\n\n // Both vars, not just $TMUX: tmux sets $TMUX_PANE in every pane too,\n // and some setups (sudo -E, direnv, ssh with a restrictive SendEnv)\n // pass one through but not the other. Either one proves we are in a\n // tmux pane, which is what this rung is asking.\n if (process.env.TMUX || process.env.TMUX_PANE) return tmuxBackend;\n\n for (const backend of BACKENDS) {\n if (await backend.available()) return backend;\n }\n\n throw new NoMultiplexerError(KNOWN_NAMES);\n}\n\n// ─── Process-wide active backend ───────────────────────────────────────\n\nlet cached: MuxBackend | undefined;\n\n/**\n * The active backend, resolved once per process and memoized.\n *\n * Memoized because detection can shell out (`tmux -V`) and mu is a\n * short-lived CLI that touches the mux many times per invocation; the\n * answer cannot change mid-process in any way that mu should react to.\n */\nexport async function activeMux(): Promise<MuxBackend> {\n if (cached === undefined) cached = await detectMux();\n return cached;\n}\n\n/**\n * Install a backend directly, bypassing detection. Returns the previous\n * value so tests can restore it. Mirrors `setTmuxExecutor`.\n *\n * Production code never calls this — use `activeMux()`.\n */\nexport function setMuxForTests(backend: MuxBackend | undefined): MuxBackend | undefined {\n const previous = cached;\n cached = backend;\n return previous;\n}\n\n/** Drop the memoized backend so the next `activeMux()` re-detects. */\nexport function resetMux(): void {\n cached = undefined;\n}\n","// mu — spawnAgent + supporting helpers (resolveCliCommand,\n// awaitSpawnLiveness, createOrReusePane, defaultSpawnLivenessMs,\n// prestageWorkspace, finalizeAgentRow, rollbackSpawn).\n//\n// spawnAgent's flow is documented inline. The interesting bit is the\n// `--workspace` cycle (workspace row FKs agent.name; agent row needs\n// pane_id which needs workspace path as cwd) — see prestageWorkspace.\n//\n// Extracted from src/agents.ts as part of refactor_split_large_src_files.\n\nimport { execFile } from \"node:child_process\";\nimport { mkdirSync, rmSync } from \"node:fs\";\nimport { basename, dirname } from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { promisify } from \"node:util\";\nimport {\n type AgentRow,\n deleteAgent,\n getAgent,\n insertAgent,\n isValidAgentName,\n pendingPaneIdFor,\n} from \"../agents.js\";\nimport { type CtlProbe, ctlProbe } from \"../ctl/client.js\";\nimport { CTL_SOCK_ENV, ctlSocketPath } from \"../ctl/path.js\";\nimport type { Db } from \"../db.js\";\nimport { emitEvent } from \"../logs.js\";\nimport { activeMux, type MuxBackend } from \"../mux.js\";\nimport { isJsonMode, type NextStep } from \"../output.js\";\n// `sleep` is the shared poll-sleep TEST SEAM (setSleepForTests), not a\n// multiplexer operation, so it stays on the tmux module rather than\n// joining the MuxBackend contract.\nimport { sleep } from \"../tmux.js\";\nimport type { VcsBackendName } from \"../vcs.js\";\nimport { createWorkspace, freeWorkspace } from \"../workspace.js\";\nimport { ensureWorkstreamSession, isScratchWorkstream } from \"../workstream.js\";\nimport {\n AgentDiedOnSpawnError,\n AgentExistsError,\n AgentSpawnCliNotFoundError,\n AgentSpawnStartupError,\n} from \"./errors.js\";\nimport { withSpawnLock } from \"./spawn-lock.js\";\n\n/**\n * Smallest-unused-suffix naming convention for agent names: a role\n * (lowercase alpha + digits) followed by `-<n>` where `<n>` is one or\n * more digits. Examples that pass: `worker-1`, `reviewer-2`, `scout-12`.\n * Examples that warn: `worker-tests`, `alice`, `db-leader`, `x-y-1`.\n *\n * Source: feedback ws task fb_agent_naming_convention. The dogfood report\n * was operators (LLMs included) drifting to descriptive names like\n * `worker-tests` after a few spawns, breaking the convention silently.\n * The hint surfaced by `maybeWarnNonConventionalAgentName` is a lint, not\n * a rule — `isValidAgentName` still accepts the broader\n * `^[a-z][a-z0-9_-]{0,31}$` shape.\n */\nconst AGENT_NAME_CONVENTION_RE = /^[a-z][a-z0-9]*(?:-[0-9]+)$/;\n\n/**\n * Stderr lint when an agent name doesn't match the smallest-unused-suffix\n * convention. Mirrors the slugify-truncation stderr hint in cmdTaskAdd\n * (commit 28a13af): stderr-only, exit 0, suppressed under --json so\n * machine consumers stay clean. The spawn already succeeded by the time\n * this runs — we're just nudging the operator toward `<role>-<n>`.\n */\nfunction maybeWarnNonConventionalAgentName(name: string): void {\n if (AGENT_NAME_CONVENTION_RE.test(name)) return;\n if (isJsonMode()) return;\n process.stderr.write(\n `hint: agent name \"${name}\" does not match the smallest-unused-suffix convention (<role>-<n>; e.g. worker-1, reviewer-2). Accepted; consider renaming if you spawn additional workers.\\n`,\n );\n}\n\n/**\n * Resolve the actual executable to launch in an agent's pane for a given\n * `cli`. Honours the env var `MU_<UPPER_CLI>_COMMAND` (e.g. `MU_PI_COMMAND=\n * pi-alt` makes `--cli pi` actually exec `pi-alt`). Falls back to the cli\n * name itself, which is what users expect when their `pi` binary is on\n * `$PATH` under that exact name.\n *\n * Used by `spawnAgent` to pick the spawned command, and by reconcile's\n * orphan detector so externally-spawned panes running the resolved binary\n * are still recognised as agents.\n */\nexport function resolveCliCommand(cli: string): string {\n const envName = envVarNameForCli(cli);\n const override = process.env[envName];\n return override && override.trim() !== \"\" ? override : cli;\n}\n\n/**\n * Compute the `MU_<UPPER_CLI>_COMMAND` env var name mu consults when\n * resolving `--cli <key>`. Hyphens in the cli key become underscores\n * (env var names can't contain `-`); this matches the operator-aliases\n * convention documented in the mu skill (e.g. `--cli pi-meta` →\n * `MU_PI_META_COMMAND`).\n */\nexport function envVarNameForCli(cli: string): string {\n return `MU_${cli.toUpperCase().replace(/-/g, \"_\")}_COMMAND`;\n}\n\n/**\n * Resolve `--cli <key>` to its actual command string AND tell the\n * caller whether the resolution came from a `MU_<UPPER_CLI>_COMMAND`\n * env var or fell through to the bare cli name. The CLI uses this to\n * surface env-var attribution in the spawn-success line so config\n * issues are visible without `mu agent show`\n * (fb_agent_spawn_no_validation, part C).\n */\nexport function resolveCliCommandWithSource(cli: string): {\n command: string;\n envVar: string;\n resolvedFromEnv: boolean;\n} {\n const envVar = envVarNameForCli(cli);\n const override = process.env[envVar];\n if (override !== undefined && override.trim() !== \"\") {\n return { command: override, envVar, resolvedFromEnv: true };\n }\n return { command: cli, envVar, resolvedFromEnv: false };\n}\n\n// ─── Pre-flight PATH check (fb_agent_spawn_no_validation, part A) ──\n//\n// Operator typo'd `mu agent spawn worker-1 --cli pi-meta` on a host\n// where `pi-meta` wasn't on PATH; the spawn appeared to succeed but\n// the pane died with `command not found` and the existing 1.5s\n// liveness check sometimes missed it (shells stay alive past a failed\n// exec). Pre-flight the PATH resolution so a typo never:\n// 1. creates an orphan workspace dir\n// 2. creates an orphan tmux pane\n// 3. inserts a half-spawned DB row\n// The check fires AFTER `resolveCliCommand` so `MU_<UPPER>_COMMAND`\n// overrides are honoured AND the diagnostic mentions the env var by\n// name. Hookable via `setCommandResolverForTests` so the test suite\n// can simulate \"binary present\" / \"binary absent\" without polluting\n// the real PATH.\n\nexport interface CommandResolutionResult {\n ok: boolean;\n /** First whitespace-separated token of the command — the binary\n * whose presence on PATH we checked. */\n binary: string;\n /** Absolute path of the resolved binary on PATH, when ok=true. */\n resolvedPath?: string;\n}\n\nexport type CommandResolver = (command: string) => Promise<CommandResolutionResult>;\n\nconst execFileP = promisify(execFile);\n\n/** Default resolver: shell-out to `command -v <binary>` (POSIX-portable\n * equivalent of `which`). Returns ok=false when the lookup exits\n * non-zero. Always returns the parsed `binary` field so the typed\n * error can quote what we actually searched for. */\nasync function defaultCommandResolver(command: string): Promise<CommandResolutionResult> {\n const binary = parseFirstToken(command);\n if (binary === \"\") return { ok: false, binary };\n try {\n // `command -v` is the POSIX builtin lookup. Spawned via /bin/sh\n // -c so the builtin is available (it's not an external program).\n const { stdout } = await execFileP(\"/bin/sh\", [\"-c\", `command -v -- ${shellQuote(binary)}`], {\n env: process.env,\n });\n const resolvedPath = stdout.trim();\n if (resolvedPath === \"\") return { ok: false, binary };\n return { ok: true, binary, resolvedPath };\n } catch {\n return { ok: false, binary };\n }\n}\n\n/** Single-quote a token for /bin/sh -c. Only used on the binary name,\n * which `parseFirstToken` already restricted to a non-whitespace run\n * — no embedded single-quotes expected. Escape defensively anyway. */\nfunction shellQuote(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n}\n\n/** First whitespace-separated token of a command string (\"pi-meta\n * --no-solo\" → \"pi-meta\"). Returns \"\" on an empty / whitespace-only\n * input. */\nfunction parseFirstToken(command: string): string {\n const trimmed = command.trim();\n if (trimmed === \"\") return \"\";\n const match = trimmed.match(/^\\S+/);\n return match ? match[0] : \"\";\n}\n\nlet activeCommandResolver: CommandResolver = defaultCommandResolver;\n\n/** Override the PATH resolver. Tests use this to simulate \"binary\n * absent\" / \"binary present\" without depending on what's actually\n * installed. Production callers should never touch this. */\nexport function setCommandResolverForTests(resolver: CommandResolver): void {\n activeCommandResolver = resolver;\n}\n\n/** Restore the default PATH resolver. */\nexport function resetCommandResolverForTests(): void {\n activeCommandResolver = defaultCommandResolver;\n}\n\n/**\n * Verify the first token of `command` resolves to a binary on PATH.\n * Public so tests can call it directly; spawnAgent calls it before\n * prestageWorkspace so a bad --cli never creates an orphan workspace.\n */\nexport async function checkCommandResolvable(command: string): Promise<CommandResolutionResult> {\n return activeCommandResolver(command);\n}\n\nexport interface SpawnAgentOptions {\n name: string;\n workstream: string;\n /** Defaults to \"pi\". Any CLI is accepted: pi is driven over its control\n * socket; others run `MU_<CLI>_COMMAND` (or the cli name) and get\n * text by mux paste. */\n cli?: string;\n /** The actual command to run in the pane. Defaults to the cli value. */\n command?: string;\n /** Window name to group this agent under. Defaults to the agent's name\n * (so each agent gets its own window). Multiple agents sharing a `tab`\n * share a window with multiple panes. */\n tab?: string;\n /** \"full-access\" (default) or \"read-only\". The schema stores it; today\n * the role isn't enforced (deferred to a future capabilities pass). */\n role?: string;\n /** Initial working directory for the spawned pane (`tmux -c <path>`).\n * When `workspace: true` is passed, this is ignored — the workspace\n * path is used instead. */\n cwd?: string;\n /** Override the tmux session name. Defaults to `mu-<workstream>`. */\n tmuxSession?: string;\n /** Auto-create a VCS workspace for this agent before spawning the\n * pane and use the workspace path as cwd. Backend defaults to\n * detection (jj > sl > git > none). */\n workspace?: boolean;\n /** Force a specific VCS backend (only meaningful with `workspace: true`). */\n workspaceBackend?: VcsBackendName;\n /** Optional ref to base the workspace on (only meaningful with\n * `workspace: true`). Backend-specific. */\n workspaceFrom?: string;\n /** Project root the workspace branches from (only meaningful with\n * `workspace: true`). Defaults to `process.cwd()`. */\n workspaceProjectRoot?: string;\n /** Run the control-socket handshake for pi agents (default true).\n * `false` skips it and reports `ctl: \"skipped\"`. */\n ctl?: boolean;\n}\n\n/** Outcome of the spawn-time control-socket handshake. */\nexport type SpawnCtl = \"ok\" | \"missing\" | \"refused\" | \"skipped\";\n\n/** A ctl probe's outcome as spawn and adopt report it. */\nexport function probeToSpawnCtl(kind: CtlProbe[\"kind\"]): Exclude<SpawnCtl, \"skipped\"> {\n return kind === \"ok\" ? \"ok\" : kind === \"missing\" ? \"missing\" : \"refused\";\n}\n\n/** What spawnAgent returns: the registered row plus the handshake outcome. */\nexport type SpawnedAgent = AgentRow & { ctl: SpawnCtl; ctlSocket: string };\n\n/**\n * True when the agent runs pi, so the mu pi extension should answer on\n * its control socket: `cli` is \"pi\", or the command's argv0 basename is\n * `pi` / `pi-meta`.\n */\nexport function speaksMuCtl(cli: string, command: string): boolean {\n if (cli === \"pi\") return true;\n const argv0 = basename(parseFirstToken(command));\n return argv0 === \"pi\" || argv0 === \"pi-meta\";\n}\n\n/**\n * Handshake budget in ms after the agent is live. 0 disables. Override\n * via `MU_SPAWN_CTL_MS`.\n */\nexport function defaultSpawnCtlMs(): number {\n const raw = process.env.MU_SPAWN_CTL_MS;\n if (raw === undefined) return 30_000;\n const parsed = Number.parseInt(raw, 10);\n if (Number.isNaN(parsed) || parsed < 0) return 30_000;\n return parsed;\n}\n\nconst CTL_POLL_INTERVAL_MS = 250;\n\n/**\n * Poll the control socket until the extension answers or the budget\n * runs out. Returns the last probe outcome. `onMiss` runs after each\n * unanswered probe and may throw to abort (the pane died); otherwise\n * this never throws. Uses real timers, not the `sleep` seam: tests stub\n * that to a no-op, which would turn this into a busy loop.\n */\nasync function awaitCtlHandshake(\n sock: string,\n onMiss?: () => Promise<void>,\n): Promise<Exclude<SpawnCtl, \"skipped\">> {\n const budgetMs = defaultSpawnCtlMs();\n const deadline = Date.now() + budgetMs;\n for (;;) {\n const remaining = deadline - Date.now();\n const probe = await ctlProbe(sock, Math.max(1, Math.min(remaining, CTL_POLL_INTERVAL_MS * 4)));\n if (probe.kind === \"ok\") return \"ok\";\n const last = probeToSpawnCtl(probe.kind);\n if (onMiss) await onMiss();\n const left = deadline - Date.now();\n if (left <= 0) return last;\n await delay(Math.min(CTL_POLL_INTERVAL_MS, left));\n }\n}\n\n/**\n * Spawn a new agent in its tmux pane and register it in the DB.\n *\n * Phases:\n * 1. Validate name + uniqueness.\n * 2. If --workspace: prestageWorkspace() (placeholder agent row +\n * workspace dir + workspace row).\n * 3. createOrReusePane() in the workspace path (or opts.cwd).\n * 4. setPaneTitle + enableMuPaneBordersForPane.\n * 5. finalizeAgentRow() — patch placeholder pane_id to real (workspace\n * path), or insert a fresh agent row (no-workspace path).\n * 6. Liveness: awaitSpawnLiveness() (fixed sleep, then pane + scan\n * check), or for a tmux pi agent the ctl handshake with a pane\n * check per tick and the scan at the end. herdr: startAgentInPane.\n * 7. ctl handshake for herdr pi agents (outside the rollback).\n *\n * Failure between any of (3)–(6) calls rollbackSpawn() to undo the\n * pane + row + workspace. The caller-visible error is preserved.\n */\nexport async function spawnAgent(db: Db, opts: SpawnAgentOptions): Promise<SpawnedAgent> {\n if (!isValidAgentName(opts.name)) {\n throw new TypeError(\n `invalid agent name: ${JSON.stringify(opts.name)} (expected /^[a-z][a-z0-9_-]{0,31}$/)`,\n );\n }\n // Per-workstream uniqueness check: v5 allows the same agent name in\n // different workstreams. Scope the existence check to the spawn's\n // workstream so two operators spawning 'worker-1' in wsA and wsB\n // both succeed (bug_v5_name_clash_silent_misroute). A fast path only:\n // a parallel spawn of the same name can pass it too, so the check is\n // repeated inside the spawn lock, and insertAgent maps the UNIQUE\n // violation to AgentExistsError as the last line.\n if (getAgent(db, opts.name, opts.workstream) !== undefined) {\n throw new AgentExistsError(opts.name);\n }\n\n const session = opts.tmuxSession ?? `mu-${opts.workstream}`;\n const windowName = opts.tab ?? opts.name;\n const cli = opts.cli ?? \"pi\";\n const resolved = resolveCliCommandWithSource(cli);\n const command = opts.command ?? resolved.command;\n // Where `command` came from. Backends that resolve the executable\n // themselves may not silently ignore an operator's choice, and need to\n // know WHICH knob to name (see `StartAgentInPaneOptions.commandSource`).\n const commandSource =\n opts.command !== undefined ? \"explicit\" : resolved.resolvedFromEnv ? \"env\" : \"cli-key\";\n\n // Pre-flight PATH resolution. Catches `--cli does-not-exist` (and\n // typos in `MU_<UPPER>_COMMAND` overrides) before any side effect.\n // See `checkCommandResolvable` / `AgentSpawnCliNotFoundError` for\n // the rationale; in short: prevents orphan workspace dirs + orphan\n // panes + half-spawned DB rows on a typo.\n //\n // We do NOT pre-flight an explicit `--command \"...\"` value: the\n // operator who passed --command gave us the literal string they\n // want spawned (often a wrapper like `pi-meta --no-solo` whose\n // first token IS on PATH but with arg-quoting that the resolver\n // would mis-parse; or a shell snippet like `bash -lc '...'`). For\n // those, the existing post-spawn liveness check is the safety net.\n if (opts.command === undefined) {\n const check = await checkCommandResolvable(command);\n if (!check.ok) {\n throw new AgentSpawnCliNotFoundError(cli, check.binary, envVarNameForCli(cli));\n }\n }\n\n // Workspace pre-stage. See `prestageWorkspace` for the FK-ordering\n // rationale. Returns the workspace path (used as the pane's cwd) or\n // undefined when --workspace wasn't requested.\n const workspacePathStr = opts.workspace ? await prestageWorkspace(db, opts, cli) : undefined;\n\n // Inject identity env vars into the pane so anything running inside\n // (pi extensions, claim-protocol scripts, status segments) can branch\n // on 'I am a mu-managed worker' without scraping pane titles or DB\n // lookups. Set via tmux `-e KEY=VALUE` (per-pane; doesn't pollute the\n // tmux server's global env).\n //\n // These are NOT exposed via SpawnAgentOptions — mu identity is not\n // user-tunable. Adding more keys here means every spawned pane sees\n // them automatically.\n //\n // MU_CTL_SOCK goes to every CLI (harmless for non-pi). Its directory\n // must exist before the pane starts: for a remote agent, ssh's\n // `-L <local>:<remote>` binds the LOCAL path and does not mkdir. A\n // socket file left by a previous agent of this name is removed inside\n // the spawn lock below, once this spawn owns the name, since ssh\n // refuses to bind over it.\n //\n // The base is the DB's directory: in production that is exactly\n // ctlSocketPath's own default (MU_DB_PATH dir or the state dir), and\n // a test DB in a temp dir keeps its sockets there too.\n const ctlSocket = ctlSocketPath(opts.workstream, opts.name, dirname(db.name));\n mkdirSync(dirname(ctlSocket), { recursive: true, mode: 0o700 });\n const paneEnv: Record<string, string> = {\n MU_MANAGED_AGENT: \"1\",\n MU_AGENT_NAME: opts.name,\n MU_WORKSTREAM: opts.workstream,\n [CTL_SOCK_ENV]: ctlSocket,\n };\n\n const hasWorkspace = workspacePathStr !== undefined;\n\n // Single outer try wrapping every step that can throw AFTER the\n // workspace has been prestaged: pane create/reuse, pane title +\n // borders, agent-row finalize, liveness check. Earlier shape had\n // createOrReusePane outside the try, so a tmux failure (e.g. no\n // workstream session and tmux refusing to create one) left the\n // prestaged workspace dir + placeholder agent row behind as an\n // orphan — task agent_spawn_abort_leaves_orphan_workspace.\n //\n // paneId is undefined until createOrReusePane returns; rollbackSpawn\n // skips killPane in that case. The inner try/catches that previously\n // wrapped finalize and liveness were folded into this single catch\n // (clarity > belt-and-suspenders; rollbackSpawn is idempotent and\n // best-effort regardless).\n let paneId: string | undefined;\n let agent: AgentRow;\n // Whether the agent row is this spawn's to delete on rollback. A\n // spawn that lost a same-name race must not delete the winner's row.\n let ownsRow = hasWorkspace;\n const wantsCtl = opts.ctl !== false && speaksMuCtl(cli, command) && defaultSpawnCtlMs() !== 0;\n let ctl: SpawnCtl | undefined;\n try {\n // Cross-process critical section: the tmux topology check-then-act\n // (`sessionExists` → `new-session` / `new-window` / `split-window`)\n // plus the agent-row finalize. Serialised per tmux SESSION so a\n // parallel fan-out (`for n in …; do mu agent spawn … & done`) can't\n // race six `new-session -d -s mu-scratch` calls into\n // five-rolled-back-losers. Keyed on the session name: spawns into\n // different sessions never contend. See spawn-lock.ts.\n //\n // The liveness wait is deliberately OUTSIDE the lock: it is the slow\n // part (1.5s+), and holding the lock across it would serialise the\n // very parallelism the operator asked for. Once the row is inserted\n // and the lock released, the next spawn can build its own window\n // while this one waits.\n const created = await withSpawnLock(session, async () => {\n // Re-check under the lock: a parallel spawn of the same name may\n // have inserted its row since the fast-path check. (With a\n // workspace, the placeholder row already claimed the name.)\n if (!hasWorkspace && getAgent(db, opts.name, opts.workstream) !== undefined) {\n throw new AgentExistsError(opts.name);\n }\n // The name is ours: no live agent owns a socket at this path.\n rmSync(ctlSocket, { force: true });\n const pid = await createOrReusePane({\n session,\n keepSessionAlive: isScratchWorkstream(opts.workstream),\n windowName,\n command,\n cwd: workspacePathStr ?? opts.cwd,\n env: paneEnv,\n });\n // Capture the pane id on the OUTER scope as soon as it exists so a\n // throw later in the lock (setPaneTitle / finalizeAgentRow INSERT\n // failure) still routes through rollbackSpawn -> killPane. Assigning\n // only via the returned object meant a finalize failure left paneId\n // undefined and rollbackSpawn skipped killPane, leaking the pane\n // (finding_verbs_spawn_rolls_back_pane).\n paneId = pid;\n const mux = await activeMux();\n await mux.setPaneTitle(pid, opts.name);\n // Apply the mu pane border to the new window. Window-scoped option;\n // see enableMuPaneBorders docstring for why this is required per\n // window (and not just per session). Self-checks MU_BANNER_QUIET\n // and is best-effort — the border is decorative.\n await mux.enableMuPaneBordersForPane(pid);\n const row = finalizeAgentRow(db, { opts, cli, paneId: pid, hasWorkspace });\n ownsRow = true;\n return { pid, row };\n });\n paneId = created.pid;\n agent = created.row;\n // Liveness check: wait briefly, then verify the pane is still alive.\n // Catches the silent-spawn-failure class of bugs where the CLI dies\n // immediately (lock conflict, bad credentials, etc.). On failure,\n // surface a typed error with whatever scrollback tmux still has.\n //\n // On a backend with no create-and-run form the pane above is a BARE\n // SHELL and this is where the agent actually starts. Both branches\n // live outside the spawn lock on purpose — they are the slow part\n // (1.5s+ here, up to herdr's 30s there) and holding the lock across\n // either would serialise the very fan-out the operator asked for.\n // Either branch throwing routes through the same `rollbackSpawn`,\n // so a pane that never became an agent never keeps its agent row.\n //\n // A tmux pi agent skips the fixed sleep: the ctl handshake is the\n // readiness signal, so the pane check rides on each handshake tick\n // and the startup-error scan runs once the handshake ends. A\n // handshake timeout with a live, clean pane is still NOT a\n // rollback (see below). MU_SPAWN_LIVENESS_MS=0 disables the checks\n // here too.\n const startAgent = (await activeMux()).startAgentInPane;\n if (startAgent === undefined) {\n if (wantsCtl) {\n const pid = paneId;\n const check = defaultSpawnLivenessMs() !== 0;\n const when = `during the ctl handshake (up to ${defaultSpawnCtlMs()}ms after spawn)`;\n // Capture every tick and keep the last good buffer: on real tmux\n // capture-pane fails once the pane is gone, so a capture taken\n // after the death check would always come back empty.\n let lastScrollback: string | undefined;\n const assertPaneAlive = async (): Promise<void> => {\n const mux = await activeMux();\n const sb = await mux.capturePane(pid, { lines: 50 }).catch(() => undefined);\n lastScrollback = sb ?? lastScrollback;\n if (await mux.paneExists(pid)) return;\n throw new AgentDiedOnSpawnError(opts.name, pid, lastScrollback, command, when);\n };\n ctl = await awaitCtlHandshake(ctlSocket, check ? assertPaneAlive : undefined);\n // Known gap: pi binds the socket in session_start (init), before\n // it renders startup diagnostics, so an ok on the first tick can\n // scan a pane that has not printed its error yet.\n //\n // An ok handshake proves the CLI started, so only provider/auth\n // patterns apply: a shell exec-failure line in the tail is then\n // transcript text (a resumed `--session` re-renders old tool\n // output such as `grep: x: No such file or directory`).\n if (check) await checkSpawnHealth(pid, opts.name, command, when, ctl === \"ok\");\n } else {\n await awaitSpawnLiveness(paneId, opts.name, command);\n }\n } else {\n // `startAgentInPane` returns only once the MUX has detected the\n // agent in that pane and considers it ready for input — strictly\n // stronger than what awaitSpawnLiveness's scrollback poll can\n // prove, so MU_SPAWN_LIVENESS_MS is\n // subsumed and deliberately not consulted. Branching on the\n // CAPABILITY, never on `mux.name`.\n await startAgent({ paneId, name: opts.name, cli, command, commandSource });\n }\n } catch (err) {\n await rollbackSpawn(db, opts.name, paneId, hasWorkspace, ownsRow, opts.workstream);\n if (hasWorkspace) attachOrphanCleanupHint(err, opts.name, opts.workstream);\n throw err;\n }\n // Control-socket handshake when the tmux path above did not run it.\n // Outside the rollback: a timeout means only control is missing; the\n // pane and pi are fine. The caller reports it (see\n // AgentCtlUnreachableError).\n ctl ??= wantsCtl ? await awaitCtlHandshake(ctlSocket) : \"skipped\";\n // `agents` is machine-local (it holds pane_id), so no capture trigger\n // covers it and this emit is the ONLY record. Typed intent so it\n // renders through the same formatter as captured ops.\n emitEvent(\n db,\n opts.workstream,\n \"agent.spawn\",\n `agent spawn ${opts.name} (cli=${cli}, role=${opts.role ?? \"full-access\"}, pane=${paneId})`,\n );\n // Lint-grade stderr nudge when the operator picked a name that\n // doesn't fit the <role>-<n> smallest-unused-suffix convention. The\n // spawn already succeeded; this is advice, not an error. Done LAST\n // so the hint trails the spawn's own stdout output and so a partial\n // failure (rolled back above) never emits it.\n maybeWarnNonConventionalAgentName(opts.name);\n return { ...agent, ctl, ctlSocket };\n}\n\n/**\n * Stage 1 of `--workspace` spawn: insert the agent row with a placeholder\n * pane id, then create the VCS workspace (whose row FKs the agent name).\n *\n * Why placeholder-first? `vcs_workspaces.agent` FK + ON DELETE CASCADE\n * means the workspace row needs an agents row to exist before we insert\n * it; but we can't insert agents without a pane_id (NOT NULL), and we\n * can't create the pane until we know the workspace's on-disk path\n * (used as cwd). The placeholder unblocks the cycle.\n *\n * The placeholder is a publicly-visible quirk — see `PENDING_PANE_PREFIX`\n * and the consumers it documents (refreshAgentTitle and reconcile's\n * pending-pane prune skip). The deeper fix (eliminate the placeholder\n * by reordering ws-dir → pane → atomic dual-insert) is filed as a\n * separate refactor; this shape is the established equilibrium.\n *\n * Throws after best-effort rollback (deleteAgent) if workspace creation\n * fails. Returns the workspace's on-disk path.\n */\nasync function prestageWorkspace(db: Db, opts: SpawnAgentOptions, cli: string): Promise<string> {\n insertAgent(db, {\n name: opts.name,\n workstream: opts.workstream,\n cli,\n paneId: pendingPaneIdFor(opts.name),\n role: opts.role,\n tab: opts.tab ?? null,\n });\n try {\n const wsOpts: Parameters<typeof createWorkspace>[1] = {\n agent: opts.name,\n workstream: opts.workstream,\n };\n if (opts.workspaceBackend !== undefined) wsOpts.backend = opts.workspaceBackend;\n if (opts.workspaceFrom !== undefined) wsOpts.parentRef = opts.workspaceFrom;\n if (opts.workspaceProjectRoot !== undefined) wsOpts.projectRoot = opts.workspaceProjectRoot;\n const ws = await createWorkspace(db, wsOpts);\n return ws.path;\n } catch (err) {\n deleteAgent(db, opts.name, opts.workstream);\n throw err;\n }\n}\n\n/**\n * Stage 2 of spawn (after the real pane exists): either patch the\n * placeholder row to the real pane id (workspace path), or insert a\n * fresh agent row (no-workspace path). Throws if the patch UPDATE\n * silently affects no row (the placeholder row vanished mid-spawn —\n * historically the bug_agent_spawn_workspace_fk_failure class, now\n * patched by reconcile's pending-pane prune skip).\n */\nfunction finalizeAgentRow(\n db: Db,\n args: { opts: SpawnAgentOptions; cli: string; paneId: string; hasWorkspace: boolean },\n): AgentRow {\n const { opts, cli, paneId, hasWorkspace } = args;\n if (!hasWorkspace) {\n return insertAgent(db, {\n name: opts.name,\n workstream: opts.workstream,\n cli,\n paneId,\n role: opts.role,\n tab: opts.tab ?? null,\n });\n }\n // Scope the patch to the spawn's workstream so a same-named worker\n // in another workstream isn't repointed at this pane\n // (bug_v5_name_clash_silent_misroute).\n db.prepare(\n `UPDATE agents SET pane_id = ?, updated_at = ?\n WHERE name = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)`,\n ).run(paneId, new Date().toISOString(), opts.name, opts.workstream);\n const row = getAgent(db, opts.name, opts.workstream);\n if (!row) throw new Error(`spawnAgent: agent vanished after workspace stage: ${opts.name}`);\n return row;\n}\n\n/**\n * Roll back a failed spawn. Idempotent and best-effort: every step\n * swallows its own errors so a partial-cleanup substrate (already-killed\n * pane, no agent row) never masks the original failure.\n *\n * `paneId` is `undefined` when createOrReusePane itself threw before\n * returning a pane id — there's nothing to kill. `hasWorkspace` is\n * `true` when prestageWorkspace succeeded; freeWorkspace is idempotent\n * on a missing workspace so calling it after a same-cycle failure is\n * safe. deleteAgent is also idempotent (no-op on a missing row).\n *\n * `ownsRow` is false when the spawn failed before inserting its own row\n * (e.g. it lost a same-name race); the row then belongs to another\n * agent and is left alone.\n */\nasync function rollbackSpawn(\n db: Db,\n name: string,\n paneId: string | undefined,\n hasWorkspace: boolean,\n ownsRow: boolean,\n workstream: string,\n): Promise<void> {\n if (paneId !== undefined) {\n // Best-effort: rollback runs on an already-failing path and must not\n // mask the original error, even if the mux itself went away.\n await activeMux()\n .then((mux) => mux.killPane(paneId))\n .catch(() => {});\n }\n if (hasWorkspace) {\n // Scope cleanup to the spawn's workstream so a same-named worker\n // elsewhere isn't torn down by accident\n // (bug_v5_name_clash_silent_misroute).\n await freeWorkspace(db, name, { workstream }).catch(() => {});\n }\n if (ownsRow) deleteAgent(db, name, workstream);\n}\n\n/**\n * After a failed spawn that prestaged a workspace, augment the thrown\n * error with orphan-cleanup hints (`mu workspace orphans` +\n * `mu workspace free`). rollbackSpawn is best-effort — if the\n * freeWorkspace call inside it failed (rmdir blocked, vcs backend\n * refusing to remove a workspace with uncommitted state, etc.), the\n * dir survives and the operator needs to know how to clean it up.\n *\n * Uses the duck-typed `errorNextSteps()` convention (see\n * `hasNextSteps()` in src/output.ts) so this works whether the inner\n * error is a typed mu error already exposing nextSteps\n * (AgentDiedOnSpawnError) or a bare TmuxError / Error from\n * createOrReusePane that has none. Existing hints (if any) are\n * preserved and listed first; the orphan-cleanup hints are appended\n * so the diagnostic-first ordering of typed errors is kept.\n */\nfunction attachOrphanCleanupHint(err: unknown, agent: string, workstream: string): void {\n if (typeof err !== \"object\" || err === null) return;\n const target = err as { errorNextSteps?: () => NextStep[] };\n const existing =\n typeof target.errorNextSteps === \"function\" ? target.errorNextSteps.bind(target) : null;\n const orphanHints: NextStep[] = [\n {\n intent: \"Check for an orphan workspace dir (rollback is best-effort; may have failed)\",\n command: `mu workspace orphans -w ${workstream}`,\n },\n {\n intent: \"Free the workspace if it survived the rollback (idempotent on missing)\",\n command: `mu workspace free ${agent} -w ${workstream}`,\n },\n ];\n target.errorNextSteps = () => [...(existing ? existing() : []), ...orphanHints];\n}\n\n/**\n * Default liveness window in milliseconds. 0 disables the check (useful\n * for fast tests that don't want to wait). Override via env var\n * `MU_SPAWN_LIVENESS_MS`.\n */\nexport function defaultSpawnLivenessMs(): number {\n const raw = process.env.MU_SPAWN_LIVENESS_MS;\n if (raw === undefined) return 1500;\n const parsed = Number.parseInt(raw, 10);\n if (Number.isNaN(parsed) || parsed < 0) return 1500;\n return parsed;\n}\n\n/**\n * Curated list of patterns that indicate the spawned CLI hit a fatal\n * provider/auth error during startup and is now parked at an error\n * prompt instead of becoming a working agent. Source:\n * `agent_spawn_model_auth_failure_counts_as_live` in the feedback ws.\n *\n * Kept short and case-insensitive on purpose: every entry must match a\n * real, observed dogfood failure mode. Don't add patterns speculatively\n * — the false-positive cost is to roll back a healthy spawn, which the\n * operator then has to re-attempt without the scan (`MU_SPAWN_LIVENESS_MS=0`).\n *\n * Mitigation against scrollback noise from harmless prior-session text:\n * `checkSpawnHealth` only scans the LAST `STARTUP_ERROR_TAIL_LINES`\n * lines of the capture, and after an ok ctl handshake it skips\n * `EXEC_FAILURE_PATTERN` (a resumed pi session re-renders old tool\n * output, which often ends in `No such file or directory`).\n */\nconst AUTH_ERROR_PATTERNS: readonly RegExp[] = [\n /No API key found for [\\w-]+/i,\n /Error: invalid API key/i,\n /Authentication failed/i,\n /401 Unauthorized/i,\n /Could not authenticate/i,\n];\n\n/**\n * The spawned command failed to exec (fb_agent_spawn_no_validation\n * part B): post-spawn detection of the\n * \"binary not found at exec time\" failure mode. The pre-flight check\n * above catches the common typo BEFORE any side effect, but a few\n * edge cases still slip through and only surface in the pane:\n * - `--command \"...\"` skips the pre-flight (operator opt-out).\n * - PATH inside the spawned shell differs from PATH in mu's\n * process (login shell rc files, /etc/paths.d, etc.).\n * - Race: binary on PATH at spawn time, gone 1.5s later.\n *\n * Anchored to lines that LOOK like a shell error rather than prose\n * mentioning the marker (review_substrate_startup_err_patterns_too_broad):\n * require the marker at end-of-line, optionally followed by a single\n * `: <name>` token (the zsh form: `zsh: command not found: pi-meta`).\n * This still trips the canonical cases —\n * `bash: pi-meta: command not found`\n * `zsh: command not found: pi-meta`\n * `sh: pi-meta: No such file or directory`\n * — but rejects banner prose like\n * `I noticed earlier you saw 'command not found'`\n * `type \\`mu\\` — command not found? install with …`\n * that previously triggered a full spawn rollback. The pre-flight\n * PATH check remains the deterministic safety net for typo'd `--cli`.\n */\nconst EXEC_FAILURE_PATTERN =\n /^(?:.*: )?(?:command not found|No such file or directory)(?::\\s*\\S+)?$/i;\n\n/** True when `line` reads as a shell exec failure, not a provider/auth error. */\nexport function isExecFailureLine(line: string): boolean {\n return EXEC_FAILURE_PATTERN.test(line);\n}\n\n/**\n * Number of trailing lines of the post-liveness scrollback to scan for\n * the startup-error patterns. The capture is `lines: 50`; tailing the last\n * 30 keeps the scan focused on the spawned CLI's own startup output\n * (the pane is brand-new, so anything earlier than that is the shell\n * banner / our own command-line, both safe).\n */\nconst STARTUP_ERROR_TAIL_LINES = 30;\n\n/**\n * Scan the tail of a freshly-captured pane buffer for known startup-\n * failure patterns. Returns the matched line on first hit, or undefined\n * if the buffer is clean. `cliStarted` (the CLI answered the ctl\n * handshake) drops the exec-failure pattern: the CLI demonstrably ran.\n *\n * Exported for the test suite; not part of the SDK surface.\n */\nexport function detectSpawnStartupError(\n scrollback: string,\n cliStarted = false,\n): string | undefined {\n const patterns = cliStarted\n ? AUTH_ERROR_PATTERNS\n : [...AUTH_ERROR_PATTERNS, EXEC_FAILURE_PATTERN];\n const lines = scrollback.split(/\\r?\\n/);\n const tail = lines.slice(Math.max(0, lines.length - STARTUP_ERROR_TAIL_LINES));\n for (const line of tail) {\n for (const pattern of patterns) {\n if (pattern.test(line)) return line;\n }\n }\n return undefined;\n}\n\nasync function awaitSpawnLiveness(\n paneId: string,\n agentName: string,\n command: string,\n): Promise<void> {\n const ms = defaultSpawnLivenessMs();\n if (ms === 0) return;\n await sleep(ms);\n await checkSpawnHealth(paneId, agentName, command);\n}\n\n/**\n * One liveness verdict: throw AgentDiedOnSpawnError when the pane is\n * gone, AgentSpawnStartupError when its scrollback tail shows a known\n * startup failure.\n */\nasync function checkSpawnHealth(\n paneId: string,\n agentName: string,\n command: string,\n /** When the failure happened, for the error text; default: the liveness window. */\n when?: string,\n /** The CLI answered the ctl handshake; see detectSpawnStartupError. */\n cliStarted = false,\n): Promise<void> {\n // Capture-pane first so we have something to attach to the error if the\n // pane is in the process of being torn down (the buffer survives a beat\n // longer than the pane's existence in some tmux builds).\n const mux = await activeMux();\n const scrollback = await mux.capturePane(paneId, { lines: 50 }).catch(() => undefined);\n if (!(await mux.paneExists(paneId))) {\n throw new AgentDiedOnSpawnError(agentName, paneId, scrollback, command, when);\n }\n // Pane is alive — but \"alive\" doesn't mean \"working\". Scan the tail of\n // the capture for known provider/auth startup errors\n // (agent_spawn_model_auth_failure_counts_as_live). The pane stays at\n // an error prompt forever otherwise, and the orchestrator only\n // discovers the dud minutes later when `mu task wait` stalls.\n if (scrollback !== undefined) {\n const matchedLine = detectSpawnStartupError(scrollback, cliStarted);\n if (matchedLine !== undefined) {\n throw new AgentSpawnStartupError(agentName, paneId, matchedLine, scrollback, when);\n }\n }\n}\n\n/**\n * Three cases, all returning a stable pane id:\n * - session doesn't exist → create session+window+pane in one shot\n * (with `keepSessionAlive`: create the\n * session with its placeholder `_mu`\n * window first, then a window here)\n * - session exists, no such window → create a new window for this agent\n * - session and window both exist → split the window to add a pane\n *\n * BARE-PANE MODE. A backend implementing `startAgentInPane` has no\n * create-and-run form, so passing a command to the creation verbs is a\n * hard error there (deliberately, so it can never be dropped silently).\n * For those backends this creates the pane EMPTY and the caller starts\n * the agent in it as a second step. Keyed on the capability, never on\n * the backend name; on tmux the command still rides along exactly as\n * before and this function is byte-for-byte equivalent to its previous\n * behaviour.\n */\nasync function createOrReusePane(opts: {\n session: string;\n /** Ensure the placeholder `_mu` window (creating or repairing it) so\n * the session outlives its last agent. Set for `scratch`, which is\n * never `workstream init`-ed; other workstreams get `_mu` at init. */\n keepSessionAlive: boolean;\n windowName: string;\n command: string;\n cwd?: string;\n env?: Record<string, string>;\n}): Promise<string> {\n // Load-bearing from top to bottom: this IS the pane creation.\n const mux = await activeMux();\n const command = mux.startAgentInPane === undefined ? opts.command : \"\";\n if (opts.keepSessionAlive) await ensureWorkstreamSession(opts.session);\n else if (!(await mux.sessionExists(opts.session))) {\n return mux.newSessionWithPane(opts.session, {\n windowName: opts.windowName,\n command,\n cwd: opts.cwd,\n env: opts.env,\n });\n }\n\n const windows = await mux.listWindows(opts.session);\n const matching = windows.find((w) => w.name === opts.windowName);\n\n if (matching) {\n return mux.splitWindow({\n // tmux splits a WINDOW: target it as `=<session>:<window_id>`.\n // A bare `<session>:<name>` falls back to tmux's session prefix\n // match, so if `mu-auth` vanishes between listWindows and here,\n // `mu-auth:Backend` splits `mu-auth-refactor`'s Backend window.\n // `=` makes the session exact and the stable window id (from\n // the listing above) must live in that session, so a vanished\n // session or window fails instead of landing elsewhere.\n // herdr splits a PANE and has no window-target form at all. Ask\n // the backend for a concrete pane in that window when it needs\n // one — cheaper than a `target` union type for two backends.\n target:\n mux.startAgentInPane === undefined\n ? `=${opts.session}:${matching.id}`\n : await firstPaneInWindow(mux, opts.session, matching.id),\n command,\n cwd: opts.cwd,\n env: opts.env,\n });\n }\n\n return mux.newWindow({\n session: opts.session,\n name: opts.windowName,\n command,\n cwd: opts.cwd,\n env: opts.env,\n });\n}\n\n/**\n * A concrete pane id inside `windowId`, for backends that split panes\n * rather than windows. Any pane in the window is a correct split target\n * — the new pane lands in the same window either way, which is all mu\n * cares about; geometry is the user's business.\n *\n * Throws rather than guessing when the window is empty: an empty window\n * cannot exist on either backend (a window IS its panes), so seeing one\n * means the window vanished between `listWindows` and here, and the\n * caller must roll back rather than create a pane somewhere else.\n */\nasync function firstPaneInWindow(\n mux: MuxBackend,\n session: string,\n windowId: string,\n): Promise<string> {\n const panes = await mux.listPanesInSession(session);\n const pane = panes.find((p) => p.windowId === windowId);\n if (pane === undefined) {\n throw new Error(\n `spawnAgent: window ${windowId} in session ${session} has no panes to split (it vanished mid-spawn?)`,\n );\n }\n return pane.paneId;\n}\n","// mu — agent registry CRUD primitives + the five high-level verbs\n// (spawn, send, read, list, close) that the CLI in step 7 will wrap.\n//\n// Layering inside this file:\n//\n// - Types & raw-row mapping (RawAgentRow / rowFromDb)\n// - CRUD primitives (insertAgent, getAgent, listAgents,\n// deleteAgent)\n// - Verbs (spawnAgent, sendToAgent, readAgent,\n// closeAgent, listLiveAgents)\n//\n// The verbs compose the CRUD primitives with the mux and reconciliation\n// layers. They are deliberately thin.\n\nimport { dirname } from \"node:path\";\nimport {\n agentKey,\n type CtlLink,\n type RuntimeState,\n readAgentStates,\n type StateSource,\n} from \"./agent-state.js\";\nimport { type Db, resolveWorkstreamId, tryResolveWorkstreamId } from \"./db.js\";\nimport { GLYPH } from \"./glyphs.js\";\nimport { emitEvent } from \"./logs.js\";\nimport { withOpContext } from \"./op-context.js\";\nimport { type ReconcileMode, type ReconcileReport, reconcile } from \"./reconcile.js\";\nimport { addNote, listTasksByOwner } from \"./tasks.js\";\n\n// Re-export the cluster modules so external callers continue to\n// `import { AgentNotFoundError, spawnAgent, ... } from \"./agents.js\"`.\nexport {\n type AbortAgentOptions,\n type AbortResult,\n abortAgent,\n DEFAULT_ABORT_TIMEOUT_MS,\n type InterruptResult,\n interruptAgent,\n} from \"./agents/abort.js\";\nexport {\n type AdoptAgentOptions,\n type AdoptAgentResult,\n adoptAgent,\n} from \"./agents/adopt.js\";\nexport { type DelegateOutcome, delegateOutcome } from \"./agents/delegate.js\";\nexport {\n AgentAbortNeedsCtlError,\n AgentAbortTimeoutError,\n AgentBusyError,\n AgentCtlUnreachableError,\n AgentDiedOnSpawnError,\n AgentExistsError,\n AgentExtensionOutdatedError,\n AgentFreshNeedsCtlError,\n AgentNotFoundError,\n AgentNotInWorkstreamError,\n AgentSlashCommandUnsupportedError,\n AgentSpawnCliNotFoundError,\n AgentSpawnStartupError,\n PaneNotInSessionError,\n WorkspacePreservedError,\n} from \"./agents/errors.js\";\nexport {\n foregroundPgid,\n isKickSignal,\n type KickAgentOptions,\n type KickAgentResult,\n type KickProcessExecutor,\n type KickSignal,\n kickAgent,\n NoForegroundProcessError,\n parsePsTtyOutput,\n resetKickProcessExecutor,\n setKickProcessExecutor,\n} from \"./agents/kick.js\";\nexport {\n type CommandResolutionResult,\n type CommandResolver,\n checkCommandResolvable,\n defaultSpawnCtlMs,\n defaultSpawnLivenessMs,\n envVarNameForCli,\n resetCommandResolverForTests,\n resolveCliCommand,\n resolveCliCommandWithSource,\n type SpawnAgentOptions,\n type SpawnCtl,\n type SpawnedAgent,\n setCommandResolverForTests,\n spawnAgent,\n speaksMuCtl,\n} from \"./agents/spawn.js\";\nexport {\n agentCtlSocket,\n CTL_SLASH_COMMANDS,\n chooseTransport,\n expectsCtl,\n isSlashCommand,\n parseSessionCommand,\n type SendResult,\n sendViaTransport,\n type Transport,\n type TransportSendOptions,\n} from \"./agents/transport.js\";\nexport {\n type AgentStatusSnapshot,\n type AgentWaitAgentState,\n type AgentWaitOptions,\n type AgentWaitRef,\n type AgentWaitResult,\n type AgentWatch,\n setAgentWaitSleepForTests,\n waitForAgents,\n} from \"./agents/wait.js\";\n\nimport { AgentExistsError, AgentNotFoundError, WorkspacePreservedError } from \"./agents/errors.js\";\nimport {\n agentCtlSocket,\n recordSend,\n type SendResult,\n sendViaTransport,\n type TransportSendOptions,\n} from \"./agents/transport.js\";\nimport { unlinkCtlSocket } from \"./ctl/path.js\";\nimport { activeMux, type CaptureOptions, type MuxPane } from \"./mux.js\";\nimport { freeWorkspace, getWorkspaceForAgent, isWorkspaceClean } from \"./workspace.js\";\n// (freeWorkspace is used by the spawn rollback paths below, not by closeAgent.\n// Closing an agent is intentionally a separate concern from freeing its workspace;\n// see the closeAgent docstring.)\nimport { ensureWorkstream, isScratchWorkstream } from \"./workstream.js\";\n\nexport interface AgentRow {\n name: string;\n /** Foreign-name reference to the owning workstream. */\n workstreamName: string;\n cli: string;\n paneId: string;\n role: string;\n /** Window name; null when the agent has its own window named after itself. */\n tab: string | null;\n /** ISO 8601 timestamp. */\n createdAt: string;\n /** ISO 8601 timestamp. */\n updatedAt: string;\n}\n\nexport interface LiveAgent extends AgentRow {\n state: RuntimeState;\n source: StateSource;\n /** Control-socket link: ok / missing / refused for pi agents, n/a otherwise. */\n ctl?: CtlLink;\n /** ISO 8601 time when the source entered this state. */\n since: string | null;\n reason?: string;\n idle?: boolean;\n}\n\n/** Default idle threshold for the derived `idle` flag (`mu state`, TUI).\n * Deliberately looser than `mu task wait --stuck-after` (2 min), which\n * acts on the stall rather than just displaying it. */\nconst DEFAULT_IDLE_THRESHOLD_MS = 300_000;\n\n/**\n * Read the operator-tunable idle threshold (`MU_IDLE_THRESHOLD_MS`).\n * Returns the default on any unparsable / negative value rather than\n * throwing — env-var typos shouldn't crash `mu state`.\n */\nexport function idleThresholdMs(): number {\n const env = process.env.MU_IDLE_THRESHOLD_MS;\n if (env === undefined || env === \"\") return DEFAULT_IDLE_THRESHOLD_MS;\n const n = Number.parseInt(env, 10);\n if (!Number.isFinite(n) || n < 0) return DEFAULT_IDLE_THRESHOLD_MS;\n return n;\n}\n\n/**\n * Decide whether an agent is in the 'idle but assigned' state. Pure\n * read on (agents, tasks); no side effects. Exported so `listLiveAgents`,\n * the renderers, and tests can share one source of truth.\n *\n * Scratch agents own no task, so for them idle past the threshold is\n * enough: a leftover delegate pane is then flagged in `mu state -w\n * scratch` / `mu agent list` instead of piling up unseen.\n */\nexport function computeAgentIdle(db: Db, agent: LiveAgent, now: number = Date.now()): boolean {\n if (agent.state !== \"needs_input\" || agent.since === null) return false;\n const threshold = idleThresholdMs();\n if (threshold <= 0) return false;\n const since = Date.parse(agent.since);\n if (!Number.isFinite(since)) return false;\n if (now - since < threshold) return false;\n if (isScratchWorkstream(agent.workstreamName)) return true;\n const wsId = tryResolveWorkstreamId(db, agent.workstreamName);\n if (wsId === null) return false;\n const row = db\n .prepare(\n `SELECT COUNT(*) AS n\n FROM tasks t\n JOIN agents a ON a.id = t.owner_id\n WHERE a.name = ? AND a.workstream_id = ? AND t.status = 'IN_PROGRESS'`,\n )\n .get(agent.name, wsId) as { n: number };\n return row.n > 0;\n}\n\nexport interface InsertAgentInput {\n name: string;\n workstream: string;\n paneId: string;\n /** Defaults to \"pi\" via schema DEFAULT. */\n cli?: string;\n /** Defaults to \"full-access\" via schema DEFAULT. */\n role?: string;\n tab?: string | null;\n}\n\ninterface RawAgentRow {\n name: string;\n /** Joined from workstreams.name. */\n workstream: string;\n cli: string;\n pane_id: string;\n role: string;\n tab: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/** SELECT clause that joins agents to workstreams, exposing the\n * operator-facing workstream name as `workstream`. Used by every\n * read path. */\nconst SELECT_AGENT_COLS = `\n a.name AS name,\n ws.name AS workstream,\n a.cli AS cli,\n a.pane_id AS pane_id,\n a.role AS role,\n a.tab AS tab,\n a.created_at AS created_at,\n a.updated_at AS updated_at\n`;\n\nconst AGENT_FROM_JOIN = \"FROM agents a JOIN workstreams ws ON ws.id = a.workstream_id\";\n\nfunction rowFromDb(row: RawAgentRow): AgentRow {\n return {\n name: row.name,\n workstreamName: row.workstream,\n cli: row.cli,\n paneId: row.pane_id,\n role: row.role,\n tab: row.tab,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\n/** Resolve an agent's surrogate id by (workstream, name). Returns\n * null on miss. */\nfunction agentIdByName(db: Db, name: string, workstream: string): number | null {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return null;\n const row = db\n .prepare(\"SELECT id FROM agents WHERE name = ? AND workstream_id = ?\")\n .get(name, wsId) as { id: number } | undefined;\n return row ? row.id : null;\n}\n\nexport function insertAgent(db: Db, input: InsertAgentInput): AgentRow {\n // Auto-create the workstreams row if missing so the FK on\n // agents.workstream_id is always satisfied. Preserves the ergonomics\n // where you could spawn without explicit `mu init`.\n ensureWorkstream(db, input.workstream);\n const workstreamId = resolveWorkstreamId(db, input.workstream);\n const now = new Date().toISOString();\n try {\n db.prepare(\n `INSERT INTO agents (name, workstream_id, cli, pane_id, status, role, tab, created_at, updated_at)\n VALUES (@name, @workstreamId, COALESCE(@cli, 'pi'), @paneId, 'spawning',\n COALESCE(@role, 'full-access'), @tab, @now, @now)`,\n ).run({\n name: input.name,\n workstreamId,\n cli: input.cli ?? null,\n paneId: input.paneId,\n role: input.role ?? null,\n tab: input.tab ?? null,\n now,\n });\n } catch (err) {\n // UNIQUE (workstream_id, name): a concurrent spawn/adopt took the\n // name between the caller's existence check and this insert.\n if ((err as { code?: unknown }).code === \"SQLITE_CONSTRAINT_UNIQUE\") {\n throw new AgentExistsError(input.name);\n }\n throw err;\n }\n const row = getAgent(db, input.name, input.workstream);\n if (!row) throw new Error(`agents.insertAgent: row not found after insert: ${input.name}`);\n return row;\n}\n\n/**\n * Look up an agent by its tmux pane id (e.g. `%4`). Returns undefined if\n * no agent currently owns that pane. Used by `mu me` and friends to\n * answer \"which agent am I?\" from `$TMUX_PANE` without the LLM having to\n * remember its own name.\n *\n * Note: `pane_id` is not declared UNIQUE in the schema (a managed agent\n * could in theory be re-spawned into the same recycled pane id) but in\n * practice tmux pane ids are unique within a server's lifetime, and\n * reconcile prunes ghosts. We return the first match.\n */\nexport function getAgentByPane(db: Db, paneId: string): AgentRow | undefined {\n const row = db\n .prepare(`SELECT ${SELECT_AGENT_COLS} ${AGENT_FROM_JOIN} WHERE a.pane_id = ? LIMIT 1`)\n .get(paneId) as RawAgentRow | undefined;\n return row ? rowFromDb(row) : undefined;\n}\n\nexport function getAgent(db: Db, name: string, workstream: string): AgentRow | undefined {\n // v5: agents.name is per-workstream unique, not globally unique.\n // Workstream is required so the same name in two workstreams\n // resolves unambiguously.\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return undefined;\n const row = db\n .prepare(\n `SELECT ${SELECT_AGENT_COLS} ${AGENT_FROM_JOIN} WHERE a.name = ? AND a.workstream_id = ?`,\n )\n .get(name, wsId) as RawAgentRow | undefined;\n return row ? rowFromDb(row) : undefined;\n}\n\nexport function listAgents(db: Db, opts: { workstream?: string } = {}): AgentRow[] {\n if (opts.workstream === undefined) {\n const rows = db\n .prepare(`SELECT ${SELECT_AGENT_COLS} ${AGENT_FROM_JOIN} ORDER BY ws.name, a.name`)\n .all() as RawAgentRow[];\n return rows.map(rowFromDb);\n }\n const wsId = tryResolveWorkstreamId(db, opts.workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_AGENT_COLS} ${AGENT_FROM_JOIN} WHERE a.workstream_id = ? ORDER BY a.name`,\n )\n .all(wsId) as RawAgentRow[];\n return rows.map(rowFromDb);\n}\n\n// ─── Pane title composition (mu's durable context) ───────────────────\n//\n// mu owns the pane title as identity plus task context. Runtime status is\n// sampled only when a mu process reconciles, so putting it here leaves a stale\n// glyph after the agent stops. A continuously updated observer may append live\n// status in tmux chrome without competing with mu for the title.\n//\n// worker-a\n// worker-a · build_x\n// worker-a · <glyph multi>2 tasks\n//\n// The agent name MUST remain the first ' · '-separated token so the\n// claim protocol's pane-title-as-identity fallback (currentPaneTitle\n// in src/tmux.ts) keeps working. Adopted panes that haven't been\n// re-titled by mu just have the name (one token) — still parses.\n\n/** Maximum total length for a composed pane title. tmux truncates\n * silently in some chrome positions; we truncate the task id\n * ourselves so the suffix is predictable. */\nconst MAX_TITLE_LEN = 64;\n\n/**\n * Placeholder pane-id prefix used during the `--workspace` pre-stage in\n * spawnAgent (src/agents/spawn.ts).\n *\n * The placeholder unblocks the FK-ordering cycle:\n * - vcs_workspaces.agent FK requires an agents row\n * - agents.pane_id is NOT NULL\n * - pane creation needs the workspace path as cwd\n * So we insert the agent with a placeholder pane_id, then create the\n * workspace, then the real pane, then patch pane_id.\n *\n * Because no real tmux pane has this prefix, a naive mutating reconcile\n * pass would treat the placeholder row as a ghost and prune it\n * (→ FK-failure on the workspace insert mid-spawn). Reconcile now guards\n * against it explicitly via isPendingPaneId(), and refreshAgentTitle skips\n * placeholders for the same reason.\n *\n * Bug surfaced as bug_agent_spawn_workspace_fk_failure.\n */\nexport const PENDING_PANE_PREFIX = \"%pending-\";\n\n/** Build the placeholder pane id for an agent during workspace pre-stage. */\nexport function pendingPaneIdFor(agentName: string): string {\n return `${PENDING_PANE_PREFIX}${agentName}`;\n}\n\n/** True iff `paneId` is a `--workspace` pre-stage placeholder (not yet patched\n * to the real tmux pane id). */\nexport function isPendingPaneId(paneId: string): boolean {\n return paneId.startsWith(PENDING_PANE_PREFIX);\n}\n\n/** Build the pane title for `agent` based on current DB state.\n * Pure (no tmux side effect; no DB write). Read-only on the DB. */\nexport function composeAgentTitle(db: Db, agent: AgentRow): string {\n // Scope by the agent's workstream so a same-named worker in another\n // workstream can't pollute this title's task list.\n const tasks = listTasksByOwner(db, agent.workstreamName, agent.name);\n let title = agent.name;\n if (tasks.length === 1) {\n title += ` · ${tasks[0]?.name}`;\n } else if (tasks.length > 1) {\n title += ` · ${GLYPH.multi}${tasks.length} tasks`;\n }\n if (title.length > MAX_TITLE_LEN) title = `${title.slice(0, MAX_TITLE_LEN - 1)}…`;\n return title;\n}\n\n/** Push a fresh pane title for `agentName`. Best-effort — a missing\n * agent, a placeholder pane id, or a tmux failure are all swallowed\n * silently (titles are decorative; never block the calling verb). */\nexport async function refreshAgentTitle(\n db: Db,\n agentName: string,\n workstream: string,\n): Promise<void> {\n const agent = getAgent(db, agentName, workstream);\n if (!agent) return;\n if (isPendingPaneId(agent.paneId)) return; // workspace pre-stage placeholder; see PENDING_PANE_PREFIX\n const title = composeAgentTitle(db, agent);\n // Best-effort all the way down, including \"no mux reachable at all\":\n // pane titles are decorative and must never fail the calling verb.\n await activeMux()\n .then((mux) => mux.setPaneTitle(agent.paneId, title))\n .catch(() => {});\n}\n\n/**\n * Delete an agent row. Returns true if a row was matched. Idempotent;\n * deleting an agent that doesn't exist returns false without throwing.\n *\n * Reaper side-effect: any task that was IN_PROGRESS owned by this\n * agent gets flipped back to OPEN with a `[reaper]` task_note and a\n * `task reap` event in `agent_logs`. The FK on `tasks.owner` is\n * `ON DELETE SET NULL` so the owner column resets automatically; the\n * extra step here is the status revert. Without this an agent that\n * crashed (or was explicitly closed mid-task) leaves the task graph\n * in a wrong state — IN_PROGRESS forever, with no owner to release.\n */\nexport function deleteAgent(db: Db, name: string, workstream: string): boolean {\n const deleted = deleteAgentRow(db, name, workstream);\n if (deleted) unlinkCtlSocket(db, name, workstream);\n return deleted;\n}\n\nfunction deleteAgentRow(db: Db, name: string, workstream: string): boolean {\n // Wrap the whole reaper sequence (snapshot stuck tasks → DELETE\n // agent → per-task UPDATE + addNote + emitEvent) in a single\n // synchronous better-sqlite3 transaction. Without this, a throw\n // mid-loop (FK race after workstream teardown, addNote/emitEvent\n // regression, OOM, …) would leave the agent row deleted (FK\n // CASCADE already SET NULL on tasks.owner_id) but only PART of\n // the reaper trail written: leftover IN_PROGRESS tasks with no\n // owner and no `[reaper]` note explaining how they got there.\n // Reconcile / `mu task wait --stuck-after` would then surface\n // them as ownerless zombies with no breadcrumb.\n // `task.reap` intent so the tasks-trigger ops this produces are\n // attributable. Without it the reaper's status revert landed as\n // intent=NULL typed ops (measured), unrenderable by the one formatter\n // v2-log-verb builds — the same defect as the prose events, in the\n // other direction.\n return withOpContext(db, { intent: \"task.reap\", actor: \"reaper\", group: \"new\" }, () =>\n db\n .transaction(() => {\n // Snapshot the stuck tasks BEFORE the DELETE; the FK CASCADE\n // (SET NULL on owner_id) makes the post-delete query\n // indistinguishable from \"never owned by this agent.\"\n const agentId = agentIdByName(db, name, workstream);\n if (agentId === null) {\n // Already gone — idempotent return. (Could happen if reconcile\n // pruned a ghost concurrently.) The DELETE is a no-op.\n return false;\n }\n const stuck = db\n .prepare(\n `SELECT t.id AS taskId, t.local_id AS localId, ws.name AS workstream\n FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE t.owner_id = ? AND t.status = 'IN_PROGRESS'`,\n )\n .all(agentId) as Array<{ taskId: number; localId: string; workstream: string }>;\n\n const result = db.prepare(\"DELETE FROM agents WHERE id = ?\").run(agentId);\n if (result.changes === 0) return false;\n\n for (const t of stuck) {\n db.prepare(\n \"UPDATE tasks SET status = 'OPEN', substate = 'todo', updated_at = ? WHERE id = ?\",\n ).run(new Date().toISOString(), t.taskId);\n addNote(\n db,\n t.localId,\n `[reaper] previous owner ${name} gone (agent removed); status reverted IN_PROGRESS → OPEN, owner cleared`,\n { author: \"reaper\", workstream: t.workstream },\n );\n // No emitEvent: the UPDATE above fired the tasks capture\n // trigger. Reap is the one site the orchestrator's split did not\n // predict — it DOES mutate a portable table, so a trigger sees\n // it, but it ran outside any withOpContext and so produced\n // intent=NULL typed ops. The fix is the intent above, not a\n // second prose row. The `[reaper]` task_note is the\n // human-readable breadcrumb, and it is itself a captured op.\n }\n return true;\n })\n .immediate(),\n );\n}\n\n// ────────────────────────────────────────────────────────────────────────\n// High-level verbs (spawn, send, read, list, close)\n// ────────────────────────────────────────────────────────────────────────\n\n/** Allowed agent name shape: lowercase alpha first, then alnum/underscore/\n * hyphen. Mirrors docs/reference/naming.md. */\nconst AGENT_NAME_RE = /^[a-z][a-z0-9_-]{0,31}$/;\n\nexport function isValidAgentName(name: string): boolean {\n return AGENT_NAME_RE.test(name);\n}\n\n/**\n * Send text to an agent and submit it. A pi agent gets it through its\n * control socket (AgentCtlUnreachableError when that does not answer —\n * never a silent paste), `/new` / `/reload` / `/compact` included; a\n * non-pi CLI or `via: \"mux\"` goes through the mux paste path. See\n * src/agents/transport.ts.\n */\nexport async function sendToAgent(\n db: Db,\n name: string,\n text: string,\n opts: TransportSendOptions & { workstream: string },\n): Promise<SendResult> {\n const agent = getAgent(db, name, opts.workstream);\n if (!agent) throw new AgentNotFoundError(name);\n const sent = await sendViaTransport(agent, text, {\n ...opts,\n socket: opts.socket ?? agentCtlSocket(db, agent),\n });\n recordSend(\n db,\n agent,\n opts.fresh ? \"fresh\" : opts.mode === \"steer\" ? \"steer\" : \"plain\",\n Buffer.byteLength(text),\n {\n transport: sent.transport,\n command: sent.command,\n state: sent.state,\n since: sent.since,\n },\n );\n return sent;\n}\n\n/**\n * Read scrollback from an agent's pane. With no options, returns the full\n * scrollback (`-S - -E -`); with `lines: N`, returns only the last N lines.\n */\nexport async function readAgent(\n db: Db,\n name: string,\n opts: CaptureOptions & { workstream: string },\n): Promise<string> {\n const agent = getAgent(db, name, opts.workstream);\n if (!agent) throw new AgentNotFoundError(name);\n // Load-bearing: the scrollback IS the output of this verb.\n return (await activeMux()).capturePane(agent.paneId, opts);\n}\n\nexport interface CloseAgentOptions {\n /**\n * Lossy override: when true, free the agent's workspace BEFORE\n * deleting the agent regardless of whether it's clean. (We control\n * the order rather than relying on FK cascade, which leaves the\n * on-disk dir orphaned.) Any pending changes / commits since fork\n * are gone unless the caller frees with `--commit` separately first.\n *\n * When false (default), behaviour depends on workspace state:\n * - clean (no uncommitted changes AND no commits since fork):\n * silently auto-free. allow_mu_agent_close_without_discard.\n * - dirty (uncommitted changes OR commits since fork): throw\n * WorkspacePreservedError so the caller decides explicitly.\n * Surfaced as a real bug in the multi-agent dogfood teardown.\n */\n discardWorkspace?: boolean;\n}\n\nexport interface CloseAgentResult {\n killedPane: boolean;\n deletedRow: boolean;\n /** True iff the agent had an associated workspace AND we proactively\n * freed it — either because the caller passed `discardWorkspace:\n * true` (lossy) or because the workspace was clean and we\n * auto-freed (allow_mu_agent_close_without_discard). False on the\n * no-workspace path (nothing to free) and on the refused path (we\n * threw before doing anything). */\n workspaceFreed: boolean;\n /** True iff `workspaceFreed` was triggered by the clean-workspace\n * auto-free path (no uncommitted changes AND no commits since\n * fork) rather than the explicit `discardWorkspace: true` override.\n * Lets the CLI render an accurate message (\"auto-freed (clean)\"\n * vs \"workspace discarded\") and gives JSON consumers a stable\n * signal. False on every other path. */\n workspaceAutoFreedClean: boolean;\n}\n\n/**\n * Close an agent: kill its tmux pane and remove its DB row. Idempotent:\n * - if the agent doesn't exist in the DB, returns a no-op result\n * - if the tmux pane is already gone, killPane swallows the error\n *\n * Workspace handling: closing an agent and freeing its workspace are\n * separate concerns (agent lifecycle vs disk artifacts). Three cases:\n *\n * - No workspace: close proceeds normally.\n * - Workspace exists AND is CLEAN (no uncommitted changes, no\n * commits since fork): silently auto-free (so a workspace that\n * contains nothing worth preserving doesn't make the operator\n * type --discard-workspace just to clean it up). Surfaced by\n * allow_mu_agent_close_without_discard — a misconfigured-spawn\n * teardown was needlessly forced through the lossy flag.\n * - Workspace exists AND has either uncommitted changes OR commits\n * since fork: REFUSE with WorkspacePreservedError so the operator\n * decides explicitly. Two resolutions:\n * 1. `freeWorkspace(db, name)` first, then `closeAgent(db, name)`.\n * Preserves the option to `--commit` pending changes.\n * 2. `closeAgent(db, name, { discardWorkspace: true })`.\n * One-shot; lossy.\n *\n * The CLI surfaces these as the two actionable nextSteps on the\n * `WorkspacePreservedError` thrown by the refuse path.\n */\nexport async function closeAgent(\n db: Db,\n name: string,\n opts: CloseAgentOptions & { workstream: string },\n): Promise<CloseAgentResult> {\n const agent = getAgent(db, name, opts.workstream);\n if (!agent) {\n return {\n killedPane: false,\n deletedRow: false,\n workspaceFreed: false,\n workspaceAutoFreedClean: false,\n };\n }\n const ws = getWorkspaceForAgent(db, name, agent.workstreamName);\n // allow_mu_agent_close_without_discard: silently auto-free a clean\n // workspace (no uncommitted changes AND no commits since fork) so\n // the user doesn't have to type --discard-workspace for a workspace\n // that contains nothing worth preserving. Only refuse when there's\n // actually something to lose. The flag stays as the lossy override\n // for non-clean workspaces.\n let autoFreeClean = false;\n if (ws !== undefined && opts.discardWorkspace !== true) {\n autoFreeClean = await isWorkspaceClean(ws);\n if (!autoFreeClean) {\n throw new WorkspacePreservedError(name, ws.path);\n }\n }\n // No pre-mutation snapshot: v9 dropped the `snapshots` table and\n // rollback is inverse ops over the ops log (`mu undo`).\n // Free the workspace BEFORE the agent (so the on-disk dir is\n // removed cleanly, not orphaned by FK cascade). freeWorkspace is\n // idempotent on missing rows.\n let workspaceFreed = false;\n if (ws !== undefined && (opts.discardWorkspace === true || autoFreeClean)) {\n await freeWorkspace(db, name, { commit: false, workstream: agent.workstreamName });\n workspaceFreed = true;\n }\n await activeMux()\n .then((mux) => mux.killPane(agent.paneId))\n .catch(() => {\n /* idempotent — pane may already be gone, or the mux with it */\n });\n const deletedRow = deleteAgent(db, name, agent.workstreamName);\n // Machine-local table (`agents`): no trigger, so this emit is the\n // only record.\n emitEvent(\n db,\n agent.workstreamName,\n \"agent.close\",\n `agent close ${name} (pane=${agent.paneId}${\n workspaceFreed\n ? autoFreeClean\n ? \", workspace auto-freed (clean)\"\n : \", workspace discarded\"\n : \"\"\n })`,\n );\n return {\n killedPane: true,\n deletedRow,\n workspaceFreed,\n workspaceAutoFreedClean: workspaceFreed && autoFreeClean,\n };\n}\n\nexport interface ListLiveAgentsOptions {\n workstream: string;\n tmuxSession?: string;\n /**\n * Which kind of reconciliation pass to run. Forwarded to\n * `reconcile()`'s same-name option. Default `\"full\"` (the\n * documented mutating behaviour `mu agent list` has always had,\n * now also used by `mu state`).\n *\n * `mu doctor` passes `\"report-only\"`: count drift, mutate nothing.\n *\n * Mid-spawn placeholders (pane id `%pending-<name>`) are protected\n * directly in reconcile's prune loop, independent of mode\n * (bug_agent_spawn_workspace_fk_failure).\n *\n * BREAKING: this replaces the previous `dryRun?: boolean`\n * option. Migration: `dryRun: true` → `mode: \"report-only\"`;\n * default (`dryRun: false` / unset) → `mode: \"full\"`.\n */\n mode?: ReconcileMode;\n}\n\nexport interface LiveAgentsView {\n /** All registered agents in the workstream, post-reconcile. */\n agents: LiveAgent[];\n /** Panes in the tmux session that look like agents but aren't registered. */\n orphans: MuxPane[];\n /** Diagnostic numbers from the reconcile pass; useful for `mu doctor`. */\n report: ReconcileReport;\n}\n\n/**\n * Return the live, reality-reconciled view of agents in a workstream.\n * `mu state` and `mu agent list` call this with the default `mode: \"full\"`\n * (mutating); the read-only diagnostic (`mu doctor`) calls it with\n * `mode: \"report-only\"` to mutate nothing at all.\n */\nexport async function listLiveAgents(db: Db, opts: ListLiveAgentsOptions): Promise<LiveAgentsView> {\n const report = await reconcile(db, {\n workstream: opts.workstream,\n ...(opts.tmuxSession !== undefined ? { tmuxSession: opts.tmuxSession } : {}),\n ...(opts.mode !== undefined ? { mode: opts.mode } : {}),\n });\n const baseAgents = listAgents(db, { workstream: opts.workstream });\n const readings = await readAgentStates(baseAgents, { stateDir: dirname(db.name) });\n const now = Date.now();\n const agents: LiveAgent[] = baseAgents.map((agent) => {\n const reading = readings.get(agentKey(agent)) ?? {\n state: \"unknown\" as const,\n source: \"none\" as const,\n since: null,\n alive: true,\n reason: \"state unavailable\",\n };\n const live: LiveAgent = {\n ...agent,\n state: reading.state,\n source: reading.source,\n ctl: reading.ctl ?? \"n/a\",\n since: reading.since === null ? null : new Date(reading.since).toISOString(),\n ...(reading.reason !== undefined ? { reason: reading.reason } : {}),\n };\n return computeAgentIdle(db, live, now) ? { ...live, idle: true } : live;\n });\n return { agents, orphans: report.orphans, report };\n}\n","// The one glyph vocabulary. Every symbol mu paints as *state* is\n// defined here and nowhere else.\n//\n// WHY ONE FILE\n// Glyphs were previously inlined at each render site: \"✓\" in the\n// Recent card, \"✓\" again in the Workspaces card, \"✓\" a third time in\n// the doctor card, \"⛓\" in Blocked, \"⚠\" in two CLI formatters. Three\n// spellings of \"ok\" drifted apart once already (the busy gear vs\n// murmur's play glyph), and each inline literal is a place the next\n// drift can start. One meaning, one constant.\n//\n// WHY ONE FAMILY\n// All state glyphs are classic Nerd Font `nf-fa-*` (Font Awesome 4)\n// codepoints. Two properties matter:\n//\n// 1. SINGLE CODEPOINT, SINGLE CELL. Unicode emoji like ⚙️ are two\n// codepoints (base + variation selector) that terminals draw one\n// cell wide. cli-table3 sizes columns by string length, so a\n// table mixing 1- and 2-codepoint glyphs misaligns. nf-fa slots\n// are private-use, all length-1, all one cell.\n// 2. STABLE SLOTS. Avoid `nf-md-*`: those PUA codepoints move\n// between Nerd Font releases and silently render as the wrong\n// icon (mu's `idle` once looked like a red shield under md-sleep).\n//\n// This matches murmur's dash (`src/dash-paint.ts` in ../murmur), which\n// drives the same tmux panes from the other side. `busy` here and\n// `running` there are both nf-fa-play on purpose: one symbol, one\n// meaning, across both surfaces.\n//\n// Requires a Nerd Font on the operator's terminal. mu's substrate is\n// pi, which assumes one. Without it every glyph renders as a\n// placeholder box — and the columns still line up, which is the bug\n// the family rule exists to prevent.\n//\n// NOT IN HERE\n// Typographic punctuation that carries no state: the `•` list bullet,\n// the `—` empty-cell dash, `…` truncation, box-drawing borders. Those\n// are text, not vocabulary, and stay at their call sites.\n\nimport type { RuntimeState } from \"./agent-state.js\";\n\nexport const AGENT_STATE_GLYPH: Record<RuntimeState, string> = {\n busy: \"\\uf04b\", // nf-fa-play\n needs_input: \"\\uf186\", // nf-fa-moon_o\n needs_permission: \"\\uf023\", // nf-fa-lock\n unknown: \"\\uf059\", // nf-fa-question_circle\n};\n\nexport function agentStateGlyph(state: RuntimeState): string {\n return AGENT_STATE_GLYPH[state] ?? GLYPH.unknown;\n}\n\n/**\n * Non-agent state glyphs, keyed by what they MEAN rather than what\n * they look like. A call site asks for `GLYPH.stale`, never for a\n * clock — so re-pointing the clock is a one-line edit here.\n *\n * `ok` / `fail` / `warn` deliberately reuse the same codepoints as the\n * agent statuses they rhyme with (`free`, `terminated`): a check-circle\n * means \"fine\" whether the row is an agent, a workspace or a doctor\n * check.\n */\nexport const GLYPH = {\n /** Healthy, clean, passing, closed-successfully. */\n ok: \"\\uf058\", // nf-fa-check_circle\n /** Failed check. */\n fail: \"\\uf057\", // nf-fa-times_circle\n /** Needs attention but not broken (idle-but-assigned, stale warning). */\n warn: \"\\uf071\", // nf-fa-warning\n /** Workspace has uncommitted edits. */\n dirty: \"\\uf005\", // nf-fa-star\n /** Workspace is ≥ WORKSPACE_STALE_THRESHOLD commits behind main. */\n stale: \"\\uf017\", // nf-fa-clock_o\n /** Task is blocked by an incoming edge. */\n blocked: \"\\uf0c1\", // nf-fa-link (chain)\n /** Task is parked (OPEN/parked): open, but set aside on purpose. */\n parked: \"\\uf04c\", // nf-fa-pause\n /** Track whose roots merged (diamond dependency). */\n merge: \"\\uf074\", // nf-fa-random\n /** Agent owns more than one task (pane-title multi-count slot). */\n multi: \"\\uf055\", // nf-fa-plus_circle\n /** Filter toggle: enabled / disabled. */\n on: \"\\uf111\", // nf-fa-circle\n off: \"\\uf10c\", // nf-fa-circle_o\n /** Status outside the known enum. */\n unknown: \"\\uf059\", // nf-fa-question_circle\n} as const;\n\n// ─── Card-header digits ────────────────────────────────────────────\n//\n// Not state, but the same question (\"which codepoint?\") asked of the\n// same file. Superscript digits prefix a TUI card's title with its\n// toggle key (`╭─ ¹ Agents · 3 free ─...─╮`, the btop convention);\n// the help overlay renders the same glyphs so the dashboard and the\n// keymap speak one visual language.\n\nconst SUPERSCRIPT = [\"⁰\", \"¹\", \"²\", \"³\", \"⁴\", \"⁵\", \"⁶\", \"⁷\", \"⁸\", \"⁹\"] as const;\n\n/**\n * Map a single decimal digit (0..9) to its Unicode superscript form.\n * Throws on out-of-range inputs so callers can't silently drop a key.\n */\nexport function superscriptDigit(n: number): string {\n if (!Number.isInteger(n) || n < 0 || n > 9) {\n throw new RangeError(`superscriptDigit: expected integer 0..9, got ${n}`);\n }\n const g = SUPERSCRIPT[n];\n // Defensive narrow for noUncheckedIndexedAccess; bounds checked above.\n if (g === undefined) {\n throw new RangeError(`superscriptDigit: unreachable for n=${n}`);\n }\n return g;\n}\n","// mu — the **op context** SDK seam (docs/VOCABULARY.md § op context).\n//\n// Triggers capture reliably but blindly: they see a row change, not why\n// it happened. Intent, grouping and actor are recovered by having the\n// SDK write them into the per-connection `_op_ctx` temp table, which the\n// triggers read as they stamp each op.\n//\n// ONE ASSIGNMENT PER PUBLIC SDK FUNCTION, not an emit per mutation.\n// That is the whole reason this is cheap enough to apply everywhere: a\n// cascade close touching 12 tasks sets the context once and gets 12\n// correctly-labelled, correctly-grouped ops for free.\n//\n// WHY A SCOPED WRAPPER RATHER THAN A SETTER\n// -----------------------------------------\n// The obvious alternative is `setOpContext(db, {...})` called at the top\n// of each SDK function. It was rejected: it leaks. Whatever intent was\n// set last stays set for the rest of the process, so the NEXT mutation\n// — possibly in a caller that never set a context, possibly in a\n// finally-block cleanup after a throw — gets mislabelled with a stale\n// intent. A wrong intent is worse than a null one, because `mu log`\n// renders it as confident prose and undo groups by it.\n//\n// `withOpContext(db, {...}, fn)` restores the previous context in a\n// `finally`, so:\n// * a throw inside fn cannot leak the intent (tested),\n// * nesting is predictable — an inner context wins while it runs and\n// the outer one is exactly restored after,\n// * the context's lifetime is visible in the source as a block.\n//\n// Nested calls inherit the OUTER group by default, which is what makes\n// grouping work without any caller threading a group id around:\n// `closeTask` opening a group and calling `setTaskStatus` per cascaded\n// child yields one group covering all of them. An inner call that wants\n// its own group passes `group: \"new\"`.\n\nimport { randomUUID } from \"node:crypto\";\nimport type { Db } from \"./db.js\";\n\n/** What an op context carries. All fields optional — a partial context\n * is fine and a null intent is captured as null (fail safe). */\nexport interface OpContext {\n /** Semantic label, e.g. `task.close`. Human-grade: `mu log` renders\n * prose from it. Use `<entity>.<verb>` with entities from\n * docs/VOCABULARY.md. */\n intent?: string | undefined;\n /**\n * Intent to use ONLY when no enclosing context already set one.\n *\n * For shared internals that several public verbs funnel through.\n * `setTaskStatus` is the motivating case: called directly it is the\n * operator's action and should label itself, but called from\n * `closeTask` the OUTER verb is the operator-meaningful label and\n * must win. Without this, the inner call would report the mechanism\n * instead of the intent.\n *\n * Ignored when `intent` is also provided.\n */\n intentIfUnset?: string | undefined;\n /** Who caused it. Free text, same semantics as the old\n * `agent_logs.source`: an agent name, \"user\", \"system\". */\n actor?: string | undefined;\n /**\n * Grouping for `mu undo`:\n * - omitted inherit the enclosing group, or start one if none.\n * - \"new\" force a fresh group even when nested.\n * - <string> use this exact group id.\n */\n group?: string | \"new\" | undefined;\n}\n\ninterface CtxRow {\n group_id: string | null;\n actor: string | null;\n intent: string | null;\n applying: number;\n}\n\n/** Read the current context. Exported for tests and for `mu doctor`. */\nexport function currentOpContext(db: Db): {\n groupId: string | null;\n actor: string | null;\n intent: string | null;\n applying: boolean;\n} {\n const row = db.prepare(\"SELECT group_id, actor, intent, applying FROM _op_ctx\").get() as\n | CtxRow\n | undefined;\n // No row => this connection never ran installCapture (a readonly\n // open). Report the inert default rather than throwing: callers use\n // this for diagnostics, and a missing context is not an error.\n if (!row) return { groupId: null, actor: null, intent: null, applying: false };\n return {\n groupId: row.group_id,\n actor: row.actor,\n intent: row.intent,\n applying: row.applying !== 0,\n };\n}\n\nfunction writeCtx(db: Db, row: CtxRow): void {\n db.prepare(\n `UPDATE _op_ctx SET group_id = @group_id, actor = @actor,\n intent = @intent, applying = @applying`,\n ).run(row);\n}\n\nfunction readCtx(db: Db): CtxRow | undefined {\n return db.prepare(\"SELECT group_id, actor, intent, applying FROM _op_ctx\").get() as\n | CtxRow\n | undefined;\n}\n\n/**\n * Run `fn` with the given op context applied, restoring the previous\n * context afterwards even if `fn` throws.\n *\n * Synchronous by design. Every mutating mu SDK function is synchronous\n * (better-sqlite3 is), so an async variant would only invite\n * interleaving two contexts on one connection — the temp table is\n * shared per-connection, so two concurrent async scopes would clobber\n * each other with no way to tell whose intent won. Keeping this sync\n * makes that unrepresentable.\n */\nexport function withOpContext<T>(db: Db, ctx: OpContext, fn: () => T): T {\n const previous = readCtx(db);\n if (!previous) {\n // No _op_ctx table on this connection (readonly open, or a DB\n // opened before capture existed). Nothing to stamp and nothing to\n // restore; run the body so behaviour degrades to \"captured with no\n // context\" rather than throwing.\n return fn();\n }\n\n const groupId =\n ctx.group === \"new\"\n ? randomUUID()\n : ctx.group !== undefined\n ? ctx.group\n : (previous.group_id ?? randomUUID());\n\n const intent =\n ctx.intent !== undefined ? ctx.intent : (previous.intent ?? ctx.intentIfUnset ?? null);\n\n writeCtx(db, {\n group_id: groupId,\n // An explicit undefined inherits; only a provided value overrides.\n actor: ctx.actor !== undefined ? ctx.actor : previous.actor,\n intent,\n applying: previous.applying,\n });\n try {\n return fn();\n } finally {\n writeCtx(db, previous);\n }\n}\n\n/**\n * Run `fn` with capture SUPPRESSED — the echo guard.\n *\n * Applying a peer's op writes to `tasks`, which fires the capture\n * trigger, which mints a NEW local op, which flushes to our segment and\n * propagates back to the peer, which applies it and echoes again. This\n * sets `applying = 1` so every trigger's `WHEN` clause short-circuits\n * and the ingest writes rows WITHOUT writing ops.\n *\n * v2-sync wraps its ingest loop in this. It lives here rather than in\n * the sync module because the flag is part of the op-context contract\n * the triggers read, and having exactly one writer of it is the point.\n *\n * Restores the previous value in a `finally`, so a throw mid-ingest\n * cannot leave capture permanently disabled on this connection — which\n * would be the worst possible failure mode, silently dropping every\n * subsequent local change.\n */\nexport function withCaptureSuppressed<T>(db: Db, fn: () => T): T {\n const previous = readCtx(db);\n if (!previous) return fn();\n writeCtx(db, { ...previous, applying: 1 });\n try {\n return fn();\n } finally {\n writeCtx(db, previous);\n }\n}\n","// mu — the canonical \"reality wins\" reconciliation routine.\n//\n// Three steps, in order:\n//\n// 1. Prune ghost rows whose pane no longer exists in the mux.\n// 2. Refresh pane titles for surviving agents.\n// 3. Surface orphan panes that look like agents but have no DB row.\n// Do NOT auto-adopt — `mu agent list` shows orphans under a separate\n// section and the user runs `mu agent adopt` to formally claim.\n//\n// `mu state`, `mu agent list`, and `mu doctor` all call this. It's the only\n// place where the registry's view of the world is reconciled against tmux's\n// view.\n\nimport * as agentSdk from \"./agents.js\";\nimport type { Db } from \"./db.js\";\nimport { activeMux, type MuxPane } from \"./mux.js\";\n\n/**\n * What kind of reconciliation pass to run.\n *\n * \"full\" Default for `mu state` and `mu agent list`. Prunes\n * ghosts (deleting the registry row, which fires the\n * deleteAgent reaper that flips IN_PROGRESS tasks back\n * to OPEN with [reaper] notes), refreshes pane titles\n * of surviving agents, surfaces orphans.\n *\n * \"report-only\" Pure observation. Counts would-be-pruned ghosts\n * without deleting; skips the title refresh (no DB\n * writes, no tmux title writes); surfaces orphans\n * (pure read). Used by `mu doctor` (read-only\n * diagnostic).\n *\n * Mid-spawn placeholders are protected directly in the prune loop, so read\n * paths no longer need a separate mode just to avoid racing spawn's workspace\n * pre-stage.\n */\nexport type ReconcileMode = \"full\" | \"report-only\";\n\nexport interface ReconcileOptions {\n /** The workstream whose registry rows we're reconciling. */\n workstream: string;\n /**\n * Override the tmux session name. Defaults to `mu-<workstream>`. Useful\n * for tests and for the rare case where a workstream's tmux session was\n * created with a non-default name.\n */\n tmuxSession?: string;\n /**\n * Which kind of pass to run. Default is `\"full\"` (the documented\n * mutating behaviour `mu agent list` has always had). See\n * `ReconcileMode` for the full per-mode contract.\n *\n * BREAKING: this replaces the previous `dryRun?: boolean` flag.\n * Migration: `dryRun: true` → `mode: \"report-only\"`; default\n * (`dryRun: false` / unset) → `mode: \"full\"`.\n */\n mode?: ReconcileMode;\n}\n\nexport interface ReconcileReport {\n /** Number of registry rows whose pane was gone. In `report-only` mode\n * this is the count of rows that WOULD have been pruned; in `full`\n * mode it's the count actually deleted. */\n prunedGhosts: number;\n /** Panes in the workstream's tmux session that look like agents but\n * aren't in the registry. NOT auto-adopted. */\n orphans: MuxPane[];\n /** Which mode this report was generated in. Lets callers switch their\n * output text (\"agents pruned\" vs \"would-be-pruned (suppressed)\")\n * without re-deriving from options. */\n mode: ReconcileMode;\n}\n\n/**\n * Pane commands that suggest \"this is an agent, surface it as an orphan.\"\n * 0.1.0 scope: pi only is detected, but the orphan list will surface\n * claude/codex panes too so the user can adopt them later.\n *\n * Also includes any env-overridden binary names (e.g. `MU_PI_COMMAND=pi-alt`\n * makes \"pi-alt\" agent-worthy) so externally-spawned panes running the\n * user's actual pi binary are still surfaced as orphans.\n */\nconst BASE_AGENT_CLIS: readonly string[] = [\"pi\", \"claude\", \"codex\"];\n\nfunction knownAgentCommands(): ReadonlySet<string> {\n const names = new Set<string>(BASE_AGENT_CLIS);\n for (const cli of BASE_AGENT_CLIS) {\n names.add(agentSdk.resolveCliCommand(cli));\n }\n return names;\n}\n\n/**\n * Build a recogniser closure that captures one snapshot of\n * knownAgentCommands() (which itself reads MU_<CLI>_COMMAND env vars).\n * Hoisted out of the orphan-surface loop so reconcile() reads each env\n * var at most once per pass instead of once per pane: dozens of panes\n * × three env vars per call = needless syscalls in a `mu state` poll\n * tick. Also pins the env-var snapshot for the loop, so test suites\n * that twiddle MU_PI_COMMAND in afterEach can't race the inner check.\n */\nfunction buildAgentPaneRecogniser(): (pane: MuxPane) => boolean {\n const known = knownAgentCommands();\n return (pane) => known.has(pane.command);\n}\n\nexport async function reconcile(db: Db, opts: ReconcileOptions): Promise<ReconcileReport> {\n const sessionName = opts.tmuxSession ?? `mu-${opts.workstream}`;\n const mode: ReconcileMode = opts.mode ?? \"full\";\n const dbAgents = agentSdk.listAgents(db, { workstream: opts.workstream });\n // Load-bearing: reconcile IS the mux read. With no reachable mux there\n // is no reality to reconcile against, and silently reporting \"zero\n // panes\" would prune every registered agent as a ghost.\n const mux = await activeMux();\n const muxPanes = await mux.listPanesInSession(sessionName);\n const paneById = new Map(muxPanes.map((p) => [p.paneId, p]));\n\n let prunedGhosts = 0;\n const orphans: MuxPane[] = [];\n\n // 1. Prune ghosts (DB row references a pane that no longer exists).\n // `full` mode deletes (and therefore reaps); `report-only` counts\n // the would-be-prunes so callers can surface drift, but leaves the\n // row in place. Mid-spawn placeholder pane ids are protected by\n // the defensive skip that lets read paths use full mode safely.\n // Placeholder rows survive this step, but are split out so the title\n // refresh and orphan exclusion only reason about real tmux panes.\n const survivors: agentSdk.AgentRow[] = [];\n const pendingSurvivors: agentSdk.AgentRow[] = [];\n for (const agent of dbAgents) {\n if (agentSdk.isPendingPaneId(agent.paneId)) {\n pendingSurvivors.push(agent);\n } else if (paneById.has(agent.paneId)) {\n survivors.push(agent);\n } else {\n if (mode === \"full\") agentSdk.deleteAgent(db, agent.name, agent.workstreamName);\n prunedGhosts++;\n }\n }\n\n // 2. Refresh durable pane titles in mutating mode. Runtime state is\n // resolved separately and is never written to the registry.\n if (mode === \"full\") {\n for (const agent of survivors) {\n await agentSdk.refreshAgentTitle(db, agent.name, agent.workstreamName);\n }\n }\n\n // 3. Surface orphan panes. `looksLikeAgentPane` is conservative:\n // pane.command must be one we recognise as an agent CLI. A bash\n // pane the user spawned for their own use is never an orphan.\n // Pure read; runs in every mode.\n const dbPaneIds = new Set(survivors.map((a) => a.paneId));\n const looksLikeAgentPane = buildAgentPaneRecogniser();\n for (const pane of muxPanes) {\n if (dbPaneIds.has(pane.paneId)) continue;\n if (looksLikeAgentPane(pane)) orphans.push(pane);\n }\n\n return { prunedGhosts, orphans, mode };\n}\n","// Shared workspace staleness threshold + predicate.\n//\n// The TUI Workspaces card originally owned the magic number (red bucket\n// at ≥10 commits behind main). Dispatch-time warnings need the same\n// definition without importing ink/react code, so the threshold lives here.\n\nexport const WORKSPACE_STALE_THRESHOLD = 10;\n\nexport function isWorkspaceStale(behind: number | null | undefined): boolean {\n return behind !== null && behind !== undefined && behind >= WORKSPACE_STALE_THRESHOLD;\n}\n","// mu — shared workspace row shapes, path helpers, and typed errors.\n\nimport { join } from \"node:path\";\nimport { defaultStateDir } from \"../db.js\";\nimport type { HasNextSteps, NextStep } from \"../output.js\";\nimport type { VcsBackendName } from \"../vcs.js\";\n\nexport interface WorkspaceRow {\n agentName: string;\n workstreamName: string;\n backend: VcsBackendName;\n path: string;\n parentRef: string | null;\n createdAt: string;\n /** How many commits the workspace's parent_ref is behind the project's\n * default branch HEAD, as of the last time the workspace's local refs\n * cache was updated. Undefined when not yet computed (the listWorkspaces\n * fast path leaves it unset; call decorateWithStaleness to populate).\n * Null when staleness was queried but cannot be computed (no main found,\n * none-backend, missing parent_ref, command failure). */\n commitsBehindMain?: number | null;\n /** True when the workspace has uncommitted / unstaged / untracked-not-\n * ignored files, as observed by the backend's `listDirtyFiles`.\n * Undefined when not yet computed (the listWorkspaces fast path leaves\n * it unset; call decorateWithDirty to populate). Null when the dirty\n * check could not be performed (backend command failure). For jj /\n * none backends — which have no operator-visible \"dirty\" concept —\n * this is always false (their listDirtyFiles returns []). */\n dirty?: boolean | null;\n}\n\nexport interface RawWorkspaceRow {\n /** Joined from agents.name. */\n agent: string;\n /** Joined from workstreams.name. */\n workstream: string;\n backend: string;\n path: string;\n parent_ref: string | null;\n created_at: string;\n}\n\n// SELECT clause that joins vcs_workspaces back to operator-facing\n// agent + workstream names (v5 stores surrogate ids; the JS row shape\n// is operator-facing TEXT names).\nexport const SELECT_WS_COLS = `\n ag.name AS agent,\n ws.name AS workstream,\n v.backend AS backend,\n v.path AS path,\n v.parent_ref AS parent_ref,\n v.created_at AS created_at\n`;\n\nexport const WS_FROM_JOIN = `FROM vcs_workspaces v\n JOIN agents ag ON ag.id = v.agent_id\n JOIN workstreams ws ON ws.id = v.workstream_id`;\n\nexport function rowFromDb(row: RawWorkspaceRow): WorkspaceRow {\n return {\n agentName: row.agent,\n workstreamName: row.workstream,\n backend: row.backend as VcsBackendName,\n path: row.path,\n parentRef: row.parent_ref,\n createdAt: row.created_at,\n };\n}\n\nexport class WorkspaceExistsError extends Error implements HasNextSteps {\n override readonly name = \"WorkspaceExistsError\";\n constructor(public readonly agent: string) {\n super(`workspace already exists for agent: ${agent}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"Show its on-disk path\", command: `mu workspace path ${this.agent}` },\n {\n intent: \"Free it (optionally --commit pending changes first)\",\n command: `mu workspace free ${this.agent} (--commit to commit pending changes first)`,\n },\n {\n intent: \"Then re-spawn the agent with a fresh workspace\",\n command: `mu agent spawn ${this.agent} --workspace --workspace-backend <jj|sl|git|none> --workspace-from <ref>`,\n },\n ];\n }\n}\n\nexport class WorkspaceNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"WorkspaceNotFoundError\";\n constructor(public readonly agent: string) {\n super(`no workspace for agent: ${agent}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List workspaces in current workstream\", command: \"mu workspace list\" },\n { intent: \"List workspaces across all workstreams\", command: \"mu workspace list --all\" },\n {\n intent: \"Spawn the agent with a workspace (creation happens at spawn)\",\n command: `mu agent spawn ${this.agent} --workspace`,\n },\n ];\n }\n}\n\n/**\n * Thrown by createWorkspace when the on-disk path it would create is\n * already occupied. Distinct from WorkspaceExistsError (which is about\n * the DB row) so the recovery is clear: the dir is orphaned (no DB\n * row points at it) and needs cleanup.\n *\n * Maps to exit code 4 (conflict).\n */\nexport class WorkspacePathNotEmptyError extends Error implements HasNextSteps {\n override readonly name = \"WorkspacePathNotEmptyError\";\n constructor(\n public readonly agent: string,\n public readonly workstream: string,\n public readonly workspacePath: string,\n ) {\n super(\n `workspace dir already on disk for agent ${agent} (${workspacePath}); refusing to overwrite`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"List every orphan workspace dir in this workstream\",\n command: `mu workspace orphans -w ${this.workstream}`,\n },\n {\n // No row points at the dir (createWorkspace throws\n // WorkspaceExistsError first when one does), so `mu workspace\n // free` has nothing to free. Same recipe as `mu workspace orphans`.\n intent: \"Inspect it, then remove it by hand (git: also drops the worktree registration)\",\n command: `(cd <project-root> && git worktree remove --force ${this.workspacePath}) || rm -rf ${this.workspacePath}`,\n },\n ];\n }\n}\n\n/**\n * Thrown by createWorkspace when the resolved projectRoot is the\n * user's $HOME.\n *\n * Maps to exit code 4 (conflict).\n */\nexport class HomeDirAsProjectRootError extends Error implements HasNextSteps {\n override readonly name = \"HomeDirAsProjectRootError\";\n constructor(\n public readonly agent: string,\n public readonly workstream: string,\n public readonly homeDir: string,\n ) {\n super(\n `refusing to create workspace with projectRoot=$HOME (${homeDir}); a recursive copy/clone of your home directory is almost never what you want`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Re-run from inside a real project directory\",\n command: `cd <your-project> && mu agent spawn ${this.agent} -w ${this.workstream} --workspace`,\n },\n {\n intent: \"Or pass --workspace-project-root explicitly\",\n command: `mu agent spawn ${this.agent} -w ${this.workstream} --workspace --workspace-project-root <your-project>`,\n },\n ];\n }\n}\n\n/**\n * Compose the canonical on-disk path for an agent's workspace. Used by\n * createWorkspace and reachable from `mu workspace path` so the user\n * can `cd $(mu workspace path foo)` even before the directory exists.\n */\nexport function workspacePath(workstream: string, agent: string): string {\n return join(defaultStateDir(), \"workspaces\", workstream, agent);\n}\n\n/** Root dir for a workstream's workspaces — the parent of all\n * per-agent workspace dirs. Used by listWorkspaceOrphans to scan\n * the filesystem. */\nexport function workspacesRoot(workstream: string): string {\n return join(defaultStateDir(), \"workspaces\", workstream);\n}\n\nexport interface WorkspaceStaleness {\n agentName: string;\n workstreamName: string;\n commitsBehindMain: number | null;\n isStale: boolean;\n}\n","// mu — workspace registry CRUD and free/list/commits helpers.\n\nimport { existsSync, rmSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { resolve } from \"node:path\";\nimport { AgentNotFoundError } from \"../agents/errors.js\";\nimport { type Db, tryResolveWorkstreamId } from \"../db.js\";\nimport { emitEvent } from \"../logs.js\";\nimport {\n backendByName,\n type CommitSummary,\n detectBackend,\n type RebaseResult,\n type VcsBackend,\n type VcsBackendName,\n} from \"../vcs.js\";\nimport {\n HomeDirAsProjectRootError,\n type RawWorkspaceRow,\n rowFromDb,\n SELECT_WS_COLS,\n WorkspaceExistsError,\n WorkspaceNotFoundError,\n WorkspacePathNotEmptyError,\n type WorkspaceRow,\n WS_FROM_JOIN,\n workspacePath,\n} from \"./core.js\";\n\nexport interface CreateWorkspaceOptions {\n agent: string;\n workstream: string;\n /** Project root to branch from. Defaults to the current working\n * directory (the `mu` invocation site, which is normally what the\n * user wants). */\n projectRoot?: string;\n /** Override backend detection. Default: walk `detectBackend`.\n * Accepts either a name (\"jj\" / \"sl\" / \"git\" / \"none\") OR a\n * pre-built `VcsBackend` object — the object form lets tests inject\n * a fresh fake backend without mutating the exported singletons. */\n backend?: VcsBackendName | VcsBackend;\n /** Optional ref to base the workspace on. Backend-specific. */\n parentRef?: string;\n}\n\n/**\n * Create a fresh workspace for an agent. Allocates the on-disk\n * directory, records the row, emits a system event. Idempotent ONLY\n * to the extent that the row check is up-front; if the row exists\n * we throw `WorkspaceExistsError` rather than silently re-using a\n * possibly-stale on-disk state. Callers should `freeWorkspace` first.\n */\nexport async function createWorkspace(db: Db, opts: CreateWorkspaceOptions): Promise<WorkspaceRow> {\n if (getWorkspaceForAgent(db, opts.agent, opts.workstream) !== undefined) {\n throw new WorkspaceExistsError(opts.agent);\n }\n\n const projectRoot = opts.projectRoot ?? process.cwd();\n\n // Footgun guard: refuse projectRoot=$HOME. resolve() normalises a\n // trailing slash, `.`, symlinks-in-name, etc., so `cd && mu agent spawn\n // --workspace ...` and `--workspace-project-root ~/` are all blocked\n // the same way.\n // Direct children of $HOME (e.g. ~/Documents) are NOT blocked —\n // that would be overreach. See snap_dogfood Finding 4.\n if (resolve(projectRoot) === resolve(homedir())) {\n throw new HomeDirAsProjectRootError(opts.agent, opts.workstream, homedir());\n }\n\n const backend =\n opts.backend === undefined\n ? await detectBackend(projectRoot)\n : typeof opts.backend === \"string\"\n ? backendByName(opts.backend)\n : opts.backend;\n const path = workspacePath(opts.workstream, opts.agent);\n\n // Surface the dir-already-exists case as a typed error WITH actionable\n // nextSteps before we delegate to the backend (which throws a bare\n // Error). This is the orphan-from-older-mu case: a workspace dir from\n // before the cccba88 close-refuses fix landed; it has no DB row.\n if (existsSync(path)) {\n throw new WorkspacePathNotEmptyError(opts.agent, opts.workstream, path);\n }\n\n const createOpts: { projectRoot: string; workspacePath: string; parentRef?: string } = {\n projectRoot,\n workspacePath: path,\n };\n if (opts.parentRef !== undefined) createOpts.parentRef = opts.parentRef;\n\n // Wrap the backend's on-disk side effect in a cleanup guard. If the\n // backend throws mid-way (cp -a hits a DRM-protected file, git\n // worktree add fails after creating the dir, an interrupt during a\n // long copy), the partial dir would otherwise be left behind with\n // no DB row — exactly the failure mode from snap_dogfood Finding 4,\n // which then blocked subsequent `--workspace` spawns with\n // WorkspacePathNotEmptyError. Best-effort: if the rm itself fails,\n // surface the original error and let the user clean up via\n // `mu workspace orphans`.\n let created: Awaited<ReturnType<typeof backend.createWorkspace>>;\n try {\n created = await backend.createWorkspace(createOpts);\n } catch (err) {\n try {\n rmSync(path, { recursive: true, force: true });\n } catch {\n // best-effort; original error wins\n }\n throw err;\n }\n\n // Roll back the on-disk + VCS-registry side effect if the DB row\n // insert fails (FK violation, schema constraint, sqlite_busy timeout).\n const now = new Date().toISOString();\n try {\n const wsId = tryResolveWorkstreamId(db, opts.workstream);\n if (wsId === null) {\n throw new Error(\n `createWorkspace: workstream not found: ${opts.workstream} (insertAgent should have ensured this)`,\n );\n }\n const agentRow = db\n .prepare(\"SELECT id FROM agents WHERE name = ? AND workstream_id = ? LIMIT 1\")\n .get(opts.agent, wsId) as { id: number } | undefined;\n if (!agentRow) {\n throw new AgentNotFoundError(opts.agent, opts.workstream);\n }\n db.prepare(\n `INSERT INTO vcs_workspaces (agent_id, workstream_id, backend, path, parent_ref, created_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n ).run(agentRow.id, wsId, backend.name, path, created.parentRef, now);\n } catch (err) {\n await backend.freeWorkspace({ workspacePath: path, commit: false }).catch(() => {});\n throw err;\n }\n\n // `vcs_workspaces` is machine-local (absolute paths), so no trigger\n // covers it and this emit is the only record.\n emitEvent(\n db,\n opts.workstream,\n \"workspace.create\",\n `workspace create ${opts.agent} (backend=${backend.name}, path=${path}${created.parentRef ? `, parent=${created.parentRef.slice(0, 12)}` : \"\"})`,\n );\n\n return {\n agentName: opts.agent,\n workstreamName: opts.workstream,\n backend: backend.name,\n path,\n parentRef: created.parentRef,\n createdAt: now,\n };\n}\n\nexport function getWorkspaceForAgent(\n db: Db,\n agent: string,\n workstream: string,\n): WorkspaceRow | undefined {\n // v5: agents.name is per-workstream unique — the lookup must scope\n // by (workstream, agent) so a same-named worker elsewhere can't be\n // resolved instead.\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return undefined;\n const row = db\n .prepare(`SELECT ${SELECT_WS_COLS} ${WS_FROM_JOIN} WHERE ag.name = ? AND v.workstream_id = ?`)\n .get(agent, wsId) as RawWorkspaceRow | undefined;\n return row ? rowFromDb(row) : undefined;\n}\n\nexport function listWorkspaces(db: Db, workstream?: string): WorkspaceRow[] {\n if (workstream === undefined) {\n const rows = db\n .prepare(`SELECT ${SELECT_WS_COLS} ${WS_FROM_JOIN} ORDER BY ws.name, ag.name`)\n .all() as RawWorkspaceRow[];\n return rows.map(rowFromDb);\n }\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(`SELECT ${SELECT_WS_COLS} ${WS_FROM_JOIN} WHERE v.workstream_id = ? ORDER BY ag.name`)\n .all(wsId) as RawWorkspaceRow[];\n return rows.map(rowFromDb);\n}\n\nexport interface FreeWorkspaceOptions {\n /** If true, attempt to commit pending changes before tearing down.\n * Backend-specific; see VcsBackend.freeWorkspace. */\n commit?: boolean;\n}\n\nexport interface FreeWorkspaceResult {\n /** The committed ref, when `commit` was true and there was something\n * to commit. */\n committedRef?: string;\n /** Branch mu created to keep the workspace's commits reachable after\n * removal (git `--commit` on a detached HEAD). */\n branch?: string;\n /** True iff the on-disk path was actually removed. */\n removed: boolean;\n /** True iff the DB row was actually deleted. */\n rowDeleted: boolean;\n}\n\n/**\n * Tear down an agent's workspace. Calls the backend to remove the\n * on-disk directory (with optional auto-commit), then DELETEs the row.\n * Idempotent on a missing workspace (returns all-false).\n */\nexport async function freeWorkspace(\n db: Db,\n agent: string,\n opts: FreeWorkspaceOptions & { workstream: string },\n): Promise<FreeWorkspaceResult> {\n const row = getWorkspaceForAgent(db, agent, opts.workstream);\n if (!row) return { removed: false, rowDeleted: false };\n\n // No pre-mutation snapshot: v9 dropped the `snapshots` table and\n // rollback is inverse ops over the ops log (`mu undo`).\n\n const backend = backendByName(row.backend);\n const result = await backend.freeWorkspace({\n workspacePath: row.path,\n commit: opts.commit ?? false,\n });\n\n // Resolve to surrogate ids scoped by the row's workstream.\n const wsIdForDel = tryResolveWorkstreamId(db, row.workstreamName);\n const del =\n wsIdForDel === null\n ? { changes: 0 }\n : db\n .prepare(\n `DELETE FROM vcs_workspaces\n WHERE agent_id = (SELECT id FROM agents WHERE name = ? AND workstream_id = ?)\n AND workstream_id = ?`,\n )\n .run(agent, wsIdForDel, wsIdForDel);\n // Machine-local table: no trigger, only record.\n emitEvent(\n db,\n row.workstreamName,\n \"workspace.free\",\n `workspace free ${agent} (backend=${row.backend}, path=${row.path}${result.committedRef ? `, committed=${result.committedRef.slice(0, 12)}` : \"\"}${result.branch ? `, branch=${result.branch}` : \"\"})`,\n );\n\n return {\n removed: result.removed,\n rowDeleted: del.changes > 0,\n ...(result.committedRef !== undefined ? { committedRef: result.committedRef } : {}),\n ...(result.branch !== undefined ? { branch: result.branch } : {}),\n };\n}\n\nexport interface RefreshWorkspaceOptions {\n agent: string;\n workstream: string;\n /** Optional override of the rebase target. When undefined, the\n * backend resolves its own default (origin/HEAD for git,\n * `trunk()` for jj/sl). */\n fromRef?: string;\n}\n\nexport interface RefreshWorkspaceResult extends RebaseResult {\n /** Backend name (mirrors the row) so a JSON consumer doesn't have\n * to look up the workspace separately to know what kind of rebase\n * it just got. */\n vcs: VcsBackendName;\n /** The workspace's on-disk path (so the JSON shape is self-contained\n * for piping to a downstream `jq` script). */\n workspacePath: string;\n}\n\n/**\n * Refresh an agent's workspace by rebasing it onto `fromRef` (or the\n * backend's default base). The agent / pane are NOT touched — only\n * the on-disk working copy moves. Writes the backend's new fork point\n * to the row's `parent_ref` so staleness, `mu workspace commits` and\n * the close-time clean check measure from the new base. Only a\n * conflict-free rebase writes it: on WorkspaceConflictError the row\n * keeps the old base, and a refresh after the conflicts are resolved\n * records the new one.\n */\nexport async function refreshWorkspace(\n db: Db,\n opts: RefreshWorkspaceOptions,\n): Promise<RefreshWorkspaceResult> {\n const row = getWorkspaceForAgent(db, opts.agent, opts.workstream);\n if (!row) throw new WorkspaceNotFoundError(opts.agent);\n const backend = backendByName(row.backend);\n const result = await backend.rebaseTo(row.path, opts.fromRef, row.parentRef || undefined);\n if (result.parentRef !== undefined) setWorkspaceParentRef(db, row, result.parentRef);\n // vcs_workspaces is machine-local, so this event is the only record.\n emitEvent(\n db,\n row.workstreamName,\n \"workspace.refresh\",\n `workspace refresh ${opts.agent} (backend=${row.backend}, fromRef=${result.fromRef}, replayed=${result.replayed.length})`,\n );\n return { ...result, vcs: row.backend, workspacePath: row.path };\n}\n\nfunction setWorkspaceParentRef(db: Db, row: WorkspaceRow, parentRef: string): void {\n const wsId = tryResolveWorkstreamId(db, row.workstreamName);\n if (wsId === null) return;\n db.prepare(\n `UPDATE vcs_workspaces SET parent_ref = ?\n WHERE agent_id = (SELECT id FROM agents WHERE name = ? AND workstream_id = ?)\n AND workstream_id = ?`,\n ).run(parentRef, row.agentName, wsId, wsId);\n}\n\nexport interface ListCommitsOptions {\n workstream: string;\n /** Optional override of the base ref (default: the workspace row's\n * parent_ref). Useful when the operator wants to ask \"what's on\n * top of an arbitrary ref\" without re-creating the workspace. */\n since?: string;\n}\n\nexport interface ListCommitsResult {\n /** Backend name (mirrors the row). */\n vcs: VcsBackendName;\n /** The base ref actually used. */\n baseRef: string;\n /** The commits, oldest-first. Empty when the workspace is exactly\n * at baseRef. */\n commits: CommitSummary[];\n /** The workspace's on-disk path (so JSON consumers don't have to\n * call `mu workspace path` separately). */\n workspacePath: string;\n}\n\n/**\n * List commits the workspace has on top of its `parent_ref` (or the\n * `--since` override), oldest-first. Promotes the dogfood-painful\n * cd $(mu workspace path X) && git log <base>..HEAD\n * incantation into a typed verb that knows the workspace's\n * recorded fork point.\n */\nexport async function listCommitsForWorkspace(\n db: Db,\n agent: string,\n opts: ListCommitsOptions,\n): Promise<ListCommitsResult> {\n const row = getWorkspaceForAgent(db, agent, opts.workstream);\n if (!row) throw new WorkspaceNotFoundError(agent);\n const backend = backendByName(row.backend);\n const baseRef = opts.since ?? row.parentRef;\n if (baseRef === null || baseRef.length === 0) {\n if (row.backend === \"none\") {\n await backend.commitsSinceBase(row.path, \"\");\n }\n throw new Error(`workspace ${agent} has no recorded parent_ref; pass --since <ref> explicitly`);\n }\n const commits = await backend.commitsSinceBase(row.path, baseRef);\n return { vcs: row.backend, baseRef, commits, workspacePath: row.path };\n}\n\n/**\n * \"Is this workspace safe to silently free on agent close?\" — i.e.\n * does it have ZERO uncommitted changes AND ZERO commits since its\n * fork point. Used by closeAgent to auto-free clean workspaces.\n */\nexport async function isWorkspaceClean(row: WorkspaceRow): Promise<boolean> {\n const backend = backendByName(row.backend);\n let clean: boolean;\n try {\n clean = await backend.isClean(row.path);\n } catch {\n return false;\n }\n if (!clean) return false;\n if (row.backend === \"none\") return true;\n if (row.parentRef === null || row.parentRef.length === 0) return false;\n try {\n const commits = await backend.commitsSinceBase(row.path, row.parentRef);\n return commits.length === 0;\n } catch {\n return false;\n }\n}\n","// mu — git VCS backend.\n\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport {\n commitSummary,\n ensureParent,\n exec,\n probeVcsRoot,\n rmDirSync,\n run,\n runShow,\n saneLimit,\n} from \"./helpers.js\";\nimport {\n type CommitSummary,\n type FreeWorkspaceResult,\n type VcsBackend,\n WorkspaceConflictError,\n WorkspaceDirtyError,\n} from \"./types.js\";\n\n// Uses `git worktree` so the new workspace shares the .git store with\n// the project root. Cheap (no copy), fast, and integrates with the rest\n// of the agent's git workflow (push, log, etc) trivially.\n\nexport const gitBackend: VcsBackend = {\n name: \"git\",\n\n async detect(projectRoot) {\n return probeVcsRoot(\"git\", [\"rev-parse\", \"--show-toplevel\"], projectRoot);\n },\n\n async createWorkspace(opts) {\n if (existsSync(opts.workspacePath)) {\n throw new Error(`vcs git: workspacePath already exists: ${opts.workspacePath}`);\n }\n await ensureParent(opts.workspacePath);\n // Defensive prune: if a previous workspace at the same path was\n // freed by `rm -rf` (or otherwise lost its dir without proper\n // teardown), git's worktree registry still points at it. Then\n // `git worktree add` fails with 'missing but already registered\n // worktree'. `git worktree prune` is idempotent and cheap; running\n // it BEFORE every add costs ~10ms and immunises us against the\n // mufeedback workspace_free_cleanup_leaves_git case.\n await run(\"git\", [\"worktree\", \"prune\"], opts.projectRoot).catch(() => {\n /* prune is best-effort; if it fails we'll get a clear error from `add` next */\n });\n // `git worktree add <path> [<ref>]`. Without a ref, the new worktree\n // checks out a detached HEAD at the project's current HEAD, which\n // matches the \"fresh per-agent workspace\" semantics we want. Use a\n // detached HEAD by default so two agents from the same parent ref\n // don't collide on a branch name.\n const args = [\"worktree\", \"add\", \"--detach\", opts.workspacePath];\n if (opts.parentRef) args.push(opts.parentRef);\n await run(\"git\", args, opts.projectRoot);\n // Resolve the actual SHA we ended up on for the parentRef record.\n const sha = await run(\"git\", [\"rev-parse\", \"HEAD\"], opts.workspacePath);\n return { parentRef: sha };\n },\n\n // Working-copy clean check: empty `git status --porcelain` output\n // means no working-tree, staged, or untracked-not-ignored changes.\n // Returns false on any failure (workspace path missing, git\n // explodes) — be conservative; auto-free should never \"silently\n // succeed\" because we couldn't check.\n async isClean(workspacePath) {\n if (!existsSync(workspacePath)) return false;\n try {\n const files = await listGitDirtyFiles(workspacePath);\n return files.length === 0;\n } catch {\n return false;\n }\n },\n\n // Compute commits-behind as: count of commits reachable from main\n // but not from `ref`. Resolves \"main\" via origin/HEAD (the symbolic\n // ref the remote advertises), falling back to origin/main and then\n // origin/master. Returns null when none of those resolve, or when\n // the rev-list call fails (e.g. ref unknown locally).\n //\n // Pure observation: NO `git fetch`. The number is as fresh as the\n // last time the user (or some other process) updated the local\n // remote-tracking refs.\n async commitsBehind(workspacePath, ref) {\n if (!existsSync(workspacePath)) return null;\n const main = await resolveGitMainRef(workspacePath);\n if (main === undefined) return null;\n try {\n const out = await run(\"git\", [\"rev-list\", \"--count\", `${ref}..${main}`], workspacePath);\n const n = Number.parseInt(out.trim(), 10);\n return Number.isFinite(n) ? n : null;\n } catch {\n return null;\n }\n },\n\n // Rebase the worktree onto `fromRef` (default = origin/HEAD via\n // resolveGitMainRef). Refuses on a dirty WC; returns the replayed\n // commit subjects oldest-first and the new fork point (parentRef);\n // on conflict aborts the rebase and throws WorkspaceConflictError so\n // the operator never inherits a half-rebased worktree from us.\n //\n // We DO `git fetch` first — otherwise the rebase target would only\n // be as fresh as the local refs cache, and the operator running\n // `mu workspace refresh` is explicitly asking for the latest. This\n // is the one-and-only place mu fetches; commitsBehind() stays pure.\n async rebaseTo(workspacePath, fromRef, previousParentRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs git: workspace path missing: ${workspacePath}`);\n }\n // Dirty-check first: refuse before any side effect.\n const dirtyFiles = await listGitDirtyFiles(workspacePath);\n if (dirtyFiles.length > 0) {\n throw new WorkspaceDirtyError(workspacePath, dirtyFiles);\n }\n // Best-effort fetch so the resolved main ref is fresh. Failure is\n // ignored (offline / no remote) — we still attempt the rebase\n // against whatever the local refs cache holds.\n await run(\"git\", [\"fetch\", \"--quiet\"], workspacePath).catch(() => {});\n let resolvedRef: string;\n if (fromRef !== undefined) {\n resolvedRef = fromRef;\n } else {\n const main = await resolveGitMainRef(workspacePath);\n if (main === undefined) {\n throw new Error(\n `vcs git: cannot resolve default branch (no origin/HEAD, origin/main, or origin/master) in ${workspacePath}; pass --from <ref>`,\n );\n }\n resolvedRef = main;\n }\n // HEAD before the rebase: when the rebase leaves it unchanged,\n // nothing was replayed, whatever sits above the fork point.\n const headBefore = await run(\"git\", [\"rev-parse\", \"HEAD\"], workspacePath);\n try {\n await run(\n \"git\",\n [\"-c\", \"user.email=mu@local\", \"-c\", \"user.name=mu\", \"rebase\", resolvedRef],\n workspacePath,\n );\n } catch (err) {\n // Capture the conflicting paths BEFORE aborting (the abort\n // resets the index and the unmerged paths disappear).\n const conflicts = await listGitUnmergedPaths(workspacePath);\n await run(\"git\", [\"rebase\", \"--abort\"], workspacePath).catch(() => {});\n if (conflicts.length > 0) {\n throw new WorkspaceConflictError(workspacePath, resolvedRef, conflicts);\n }\n // Non-conflict rebase failure (e.g. unknown ref). Surface it raw.\n throw err;\n }\n // After a successful `git rebase <ref>`, HEAD descends from <ref>,\n // so <ref>'s commit is the new fork point, unless the old fork\n // point already descends from <ref> (a no-op refresh onto an older\n // base): moving parent_ref back would relabel the commits between\n // them as the worker's. HEAD's history above it is the replayed set,\n // or nothing when the rebase left HEAD where it was (a no-op).\n const target = await run(\n \"git\",\n [\"rev-parse\", \"--verify\", `${resolvedRef}^{commit}`],\n workspacePath,\n );\n const keepPrevious =\n previousParentRef !== undefined &&\n (await gitIsAncestor(workspacePath, target, previousParentRef)) &&\n (await gitIsAncestor(workspacePath, previousParentRef, \"HEAD\"));\n const parentRef = keepPrevious ? previousParentRef : target;\n const headAfter = await run(\"git\", [\"rev-parse\", \"HEAD\"], workspacePath);\n const logOut =\n headAfter === headBefore\n ? \"\"\n : await run(\n \"git\",\n [\"log\", \"--reverse\", \"--format=%s\", `${parentRef}..HEAD`],\n workspacePath,\n );\n const replayed = logOut.length === 0 ? [] : logOut.split(\"\\n\");\n return { fromRef: resolvedRef, parentRef, replayed, conflicts: [] };\n },\n\n // List commits in (baseRef..HEAD), oldest-first. The format string\n // packs four NUL-delimited fields per record, then a record\n // separator '\\x1e' (RECORD-SEPARATOR control char) so subjects /\n // bodies with embedded newlines or NULs survive parsing. We don't\n // use a JSON template because git's `--format` is field-oriented;\n // %x00 (NUL) and %x1e (RS) are git's portable escape sequences.\n async commitsSinceBase(workspacePath, baseRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs git: workspace path missing: ${workspacePath}`);\n }\n const out = await run(\n \"git\",\n [\"log\", \"--reverse\", \"-z\", \"--format=%H%x00%s%x00%b%x00%aI\", `${baseRef}..HEAD`],\n workspacePath,\n );\n if (out.length === 0) return [];\n // -z makes git use NUL as the record separator; combined with our\n // %x00 field separators each commit looks like:\n // <sha>\\0<subject>\\0<body>\\0<authorDate>\\0\n // i.e. four fields followed by the record-terminating NUL git\n // injects with -z. Splitting on NUL leaves us with 4N fields plus\n // a trailing empty string we drop.\n const fields = out.split(\"\\x00\");\n const records: CommitSummary[] = [];\n for (let i = 0; i + 3 < fields.length; i += 4) {\n const sha = fields[i] ?? \"\";\n const subject = fields[i + 1] ?? \"\";\n const body = fields[i + 2] ?? \"\";\n const authorDate = fields[i + 3] ?? \"\";\n if (sha.length === 0) continue;\n records.push(commitSummary(sha, subject, body, authorDate));\n }\n return records;\n },\n\n async recentCommits(projectRoot, limit) {\n if (!existsSync(projectRoot)) {\n throw new Error(`vcs git: project root missing: ${projectRoot}`);\n }\n const n = saneLimit(limit);\n if (n === 0) return [];\n const out = await run(\n \"git\",\n [\"log\", `--max-count=${n}`, \"-z\", \"--format=%H%x00%s%x00%b%x00%aI%x00%an\"],\n projectRoot,\n );\n return parseGitZRecords(out);\n },\n\n async showCommit(projectRoot, sha) {\n return runShow(\"git\", [\"show\", sha, \"--stat\", \"-p\", \"--color=always\"], projectRoot);\n },\n\n async freeWorkspace(opts) {\n // Disk-missing case: a previous caller (or the user) ran `rm -rf`\n // out from under us, but the git worktree registry STILL has an\n // entry pointing here. Without a prune, the next `git worktree\n // add` at this path errors out (the mufeedback case). We can't\n // reach the project root via the workspace itself (the .git\n // pointer file is gone with the dir), and we can't reliably guess\n // WHICH project root, so we return { removed: false } without\n // pruning; createWorkspace's defensive prune (above) reaps the\n // dead registry entry on next use.\n if (!existsSync(opts.workspacePath)) {\n return { removed: false };\n }\n let committedRef: string | undefined;\n let branch: string | undefined;\n if (opts.commit) {\n // Commit only if there's anything to commit. Reuse the same\n // dirty-file semantics as isClean() and rebaseTo(): `git status\n // --porcelain` includes working-tree, staged, and untracked-not-\n // ignored changes.\n const dirty = (await listGitDirtyFiles(opts.workspacePath)).length > 0;\n if (dirty) {\n await run(\"git\", [\"add\", \"-A\"], opts.workspacePath);\n await run(\n \"git\",\n [\n \"-c\",\n \"user.email=mu@local\",\n \"-c\",\n \"user.name=mu\",\n \"commit\",\n \"-m\",\n \"mu workspace free auto-commit\",\n ],\n opts.workspacePath,\n );\n committedRef = await run(\"git\", [\"rev-parse\", \"HEAD\"], opts.workspacePath);\n }\n // The worktree's HEAD is detached (createWorkspace uses --detach),\n // so removing the worktree would leave the auto-commit, and any\n // commit the agent made, reachable from no ref: git gc deletes\n // them. Pin HEAD with a branch unless a branch, remote or tag\n // already contains it.\n branch = await keepGitHeadReachable(opts.workspacePath);\n }\n // Tear down: git worktree remove --force <path> cleans both the\n // on-disk directory AND the git/worktrees/<name>/ admin entry. We\n // can't easily run it from the project root (we don't store it on\n // the workspace row), but git accepts the worktree's own path from\n // anywhere with --force, so we resolve a usable cwd via git's own\n // pointer back to the project's .git dir.\n const projectRoot = await resolveGitProjectRoot(opts.workspacePath);\n if (projectRoot) {\n await run(\"git\", [\"worktree\", \"remove\", \"--force\", opts.workspacePath], projectRoot);\n } else {\n // Lost the link — just rm the directory. git's admin entry will\n // be cleaned by the next `git worktree prune` invocation.\n rmDirSync(opts.workspacePath);\n }\n const result: FreeWorkspaceResult = { removed: true };\n if (committedRef !== undefined) result.committedRef = committedRef;\n if (branch !== undefined) result.branch = branch;\n return result;\n },\n\n async listDirtyFiles(workspacePath) {\n if (!existsSync(workspacePath)) return [];\n return listGitDirtyFiles(workspacePath);\n },\n};\n\n/**\n * Resolve the workspace's notion of \"main\". Tries, in order:\n * 1. `origin/HEAD` — the symbolic ref the remote published\n * (e.g. \"refs/remotes/origin/main\"). The most accurate signal.\n * 2. `refs/remotes/origin/main` — the convention.\n * 3. `refs/remotes/origin/master` — pre-rename convention.\n * Returns the resolved ref string (suitable for `git rev-list`) or\n * undefined if none of the three resolve.\n */\n/** `git merge-base --is-ancestor`: exit 0 = yes; exit 1 or any failure = no. */\nasync function gitIsAncestor(cwd: string, ancestor: string, descendant: string): Promise<boolean> {\n try {\n await run(\"git\", [\"merge-base\", \"--is-ancestor\", ancestor, descendant], cwd);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolveGitMainRef(workspacePath: string): Promise<string | undefined> {\n for (const candidate of [\n \"refs/remotes/origin/HEAD\",\n \"refs/remotes/origin/main\",\n \"refs/remotes/origin/master\",\n ]) {\n try {\n await exec(\"git\", [\"rev-parse\", \"--verify\", \"--quiet\", candidate], { cwd: workspacePath });\n return candidate;\n } catch {\n /* try next */\n }\n }\n return undefined;\n}\n\n/**\n * Return the list of dirty paths (working-tree modifications + staged\n * + untracked-not-ignored), one entry per file. Empty when clean.\n * Used by `rebaseTo` to refuse with WorkspaceDirtyError carrying the\n * file list — one error message tells the operator both that the\n * workspace is dirty and which files to deal with.\n */\nasync function listGitDirtyFiles(workspacePath: string): Promise<string[]> {\n // `git status --porcelain` prints one line per changed file with\n // a 2-char status prefix; trim the prefix to get the path. Untracked\n // files appear as `?? path` and are included by default.\n const { stdout } = await exec(\"git\", [\"status\", \"--porcelain\"], { cwd: workspacePath });\n const lines = stdout.split(\"\\n\").filter((l) => l.length > 0);\n return lines.map((l) => l.slice(3));\n}\n\n/**\n * Return the unmerged paths after a failed `git rebase`. Used to\n * enrich WorkspaceConflictError before we abort. Empty list means the\n * rebase failed for a non-conflict reason (unknown ref, etc).\n */\nasync function listGitUnmergedPaths(workspacePath: string): Promise<string[]> {\n try {\n const out = await run(\"git\", [\"diff\", \"--name-only\", \"--diff-filter=U\"], workspacePath);\n return out.length === 0 ? [] : out.split(\"\\n\").filter((l) => l.length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Create a branch at the worktree's HEAD when no branch, remote-tracking\n * ref or tag contains it, so the commits survive `git worktree remove`.\n * The name is `mu/<workstream>/<agent>-<sha12>`, taken from the\n * workspace path (`<state>/workspaces/<workstream>/<agent>`); the sha\n * suffix keeps a re-spawned agent's later free from colliding. Returns\n * the branch name, or undefined when HEAD was already reachable.\n */\nasync function keepGitHeadReachable(workspacePath: string): Promise<string | undefined> {\n const containing = await run(\n \"git\",\n [\"for-each-ref\", \"--count=1\", \"--contains\", \"HEAD\", \"refs/heads\", \"refs/remotes\", \"refs/tags\"],\n workspacePath,\n );\n if (containing.length > 0) return undefined;\n const sha = await run(\"git\", [\"rev-parse\", \"HEAD\"], workspacePath);\n const parts = workspacePath.replace(/\\/+$/, \"\").split(\"/\");\n const agent = parts.at(-1) ?? \"workspace\";\n const workstream = parts.at(-2) ?? \"mu\";\n const branch = `mu/${workstream}/${agent}-${sha.slice(0, 12)}`;\n await run(\"git\", [\"branch\", branch, sha], workspacePath);\n return branch;\n}\n\n/**\n * Resolve the project root that owns this worktree. Returns undefined\n * if the link is broken (e.g. project root deleted). git rev-parse\n * --git-common-dir gives us the parent project's .git dir; the worktree's\n * project root is its parent.\n */\nasync function resolveGitProjectRoot(workspacePath: string): Promise<string | undefined> {\n try {\n const { stdout } = await exec(\n \"git\",\n [\"rev-parse\", \"--path-format=absolute\", \"--git-common-dir\"],\n { cwd: workspacePath },\n );\n return resolve(stdout.trim(), \"..\");\n } catch {\n return undefined;\n }\n}\n\nfunction parseGitZRecords(raw: string): CommitSummary[] {\n if (raw.length === 0) return [];\n const fields = raw.split(\"\\x00\");\n const records: CommitSummary[] = [];\n for (let i = 0; i + 4 < fields.length; i += 5) {\n const sha = fields[i] ?? \"\";\n if (sha.length === 0) continue;\n records.push(\n commitSummary(\n sha,\n fields[i + 1] ?? \"\",\n fields[i + 2] ?? \"\",\n fields[i + 3] ?? \"\",\n fields[i + 4] ?? \"\",\n ),\n );\n }\n return records;\n}\n","// mu — shared helpers for concrete VCS backends.\n\nimport { execFile } from \"node:child_process\";\nimport { existsSync, rmSync } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { type CommitSummary, SHOW_COMMIT_MAX_CHARS, type ShowCommitResult } from \"./types.js\";\n\nexport const exec = promisify(execFile);\n\nexport async function probeVcsRootPath(\n cmd: string,\n args: string[],\n cwd: string,\n): Promise<string | null> {\n try {\n const stdout = await run(cmd, args, cwd);\n const root = stdout.trim();\n return root.length > 0 ? root : null;\n } catch {\n // Tool not installed, or tool exited non-zero because cwd is not\n // in that backend's repo type. Both mean \"not this backend\".\n return null;\n }\n}\n\nexport async function probeVcsRoot(cmd: string, args: string[], cwd: string): Promise<boolean> {\n return (await probeVcsRootPath(cmd, args, cwd)) !== null;\n}\n\nexport function isSaplingDotdir(dotdir: string): boolean {\n return /(?:^|[\\\\/])\\.(?:sl|hg)$/.test(dotdir);\n}\n\nexport async function ensureParent(path: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n}\n\nexport function rmDirSync(path: string): boolean {\n if (!existsSync(path)) return false;\n rmSync(path, { recursive: true, force: true });\n return true;\n}\n\n/**\n * Run a binary with args. Throws a typed Error on non-zero exit. Stdout\n * is returned trimmed; stderr is appended to the Error message.\n */\nexport async function run(bin: string, args: readonly string[], cwd?: string): Promise<string> {\n try {\n const { stdout } = await exec(bin, [...args], { cwd, maxBuffer: 16 * 1024 * 1024 });\n return stdout.trim();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`vcs ${bin} ${args.join(\" \")} failed: ${msg}`);\n }\n}\n\n/**\n * Run a VCS `show` and cap its text at SHOW_COMMIT_MAX_CHARS. Output\n * past the exec buffer (2x the cap, in bytes) kills the child with\n * ERR_CHILD_PROCESS_STDIO_MAXBUFFER. A stdout overflow keeps the stdout\n * read so far and is always reported as truncated, even when multi-byte\n * text decodes to fewer characters than the cap. A stderr overflow is a\n * failure and surfaces as an error.\n */\nexport async function runShow(\n bin: string,\n args: readonly string[],\n cwd?: string,\n): Promise<ShowCommitResult> {\n let stdout: string;\n let overflowed = false;\n try {\n ({ stdout } = await exec(bin, [...args], { cwd, maxBuffer: SHOW_COMMIT_MAX_CHARS * 2 }));\n } catch (err) {\n const partial = stdoutOverflow(err);\n if (partial === undefined) {\n return {\n text: \"\",\n truncated: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n stdout = partial;\n overflowed = true;\n }\n if (overflowed || stdout.length > SHOW_COMMIT_MAX_CHARS) {\n return {\n text: `${stdout.slice(0, SHOW_COMMIT_MAX_CHARS)}\\n…(truncated at ${SHOW_COMMIT_MAX_CHARS} chars)`,\n truncated: true,\n };\n }\n return { text: stdout, truncated: false };\n}\n\n/** The partial stdout of a stdout maxBuffer overflow; undefined for any\n * other failure (a stderr overflow included). Node names the stream\n * only in the message: \"stdout maxBuffer length exceeded\". */\nfunction stdoutOverflow(err: unknown): string | undefined {\n if (!(err instanceof Error)) return undefined;\n const e = err as Error & { code?: unknown; stdout?: unknown };\n if (e.code !== \"ERR_CHILD_PROCESS_STDIO_MAXBUFFER\" || typeof e.stdout !== \"string\") {\n return undefined;\n }\n return e.message.startsWith(\"stdout \") ? e.stdout : undefined;\n}\n\nfunction relTimeFromIso(iso: string): string {\n const t = Date.parse(iso);\n if (!Number.isFinite(t)) return \"—\";\n const sec = Math.max(0, Math.floor((Date.now() - t) / 1000));\n if (sec < 60) return `${sec}s`;\n const min = Math.floor(sec / 60);\n if (min < 60) return `${min}m`;\n const hr = Math.floor(min / 60);\n if (hr < 24) return `${hr}h`;\n const day = Math.floor(hr / 24);\n if (day < 7) return `${day}d`;\n return `${Math.floor(day / 7)}w`;\n}\n\nexport function commitSummary(\n sha: string,\n subject: string,\n body: string,\n authorDate: string,\n author = \"\",\n): CommitSummary {\n return { sha, subject, body, author, authorDate, relTime: relTimeFromIso(authorDate) };\n}\n\nexport function saneLimit(limit: number): number {\n if (!Number.isFinite(limit)) return 25;\n return Math.max(0, Math.min(200, Math.floor(limit)));\n}\n\n/**\n * Parse the NUL-field / \\x1e-record format used by the jj/sl\n * commitsSinceBase impls. Each record is `sha\\0subject\\0body\\0date`\n * terminated by \\x1e. Empty input → [].\n */\nexport function parseNulRecords(raw: string): CommitSummary[] {\n if (raw.length === 0) return [];\n const records: CommitSummary[] = [];\n for (const rec of raw.split(\"\\x1e\")) {\n if (rec.length === 0) continue;\n const fields = rec.split(\"\\x00\");\n const sha = fields[0] ?? \"\";\n if (sha.length === 0) continue;\n records.push(\n commitSummary(sha, fields[1] ?? \"\", fields[2] ?? \"\", fields[3] ?? \"\", fields[4] ?? \"\"),\n );\n }\n return records;\n}\n","// mu — shared VCS backend contracts and typed errors.\n\nimport type { HasNextSteps, NextStep } from \"../output.js\";\n\nexport type VcsBackendName = \"jj\" | \"sl\" | \"git\" | \"none\";\n\n// ─── Refresh / rebase result + typed errors ──────────────────────────\n//\n// rebaseTo is the backend-side of `mu workspace refresh`. The errors\n// live in this module (not src/workspace.ts) because they're thrown\n// from inside the backend impls; workspace.ts imports vcs.ts, and the\n// root vcs hub re-exports these concrete definitions.\n\nexport interface RebaseResult {\n /** The ref the workspace was actually rebased onto (resolved\n * symbolic-or-revset → concrete name). For git that is the\n * resolveGitMainRef() symbolic ref; for jj/sl it's the literal\n * `trunk()` revset (or whatever the operator passed via fromRef). */\n fromRef: string;\n /** Concrete commit id of the workspace's new fork point: fromRef's\n * commit, or the previous fork point when that already descends from\n * it (parent_ref never moves backward). The caller writes it to\n * `vcs_workspaces.parent_ref` so staleness, `mu workspace commits`\n * and the close-time clean check measure from the new base. Omitted\n * when the backend cannot resolve a single commit (e.g. a jj revset\n * naming several); the caller then keeps the old value. */\n parentRef?: string;\n /** Commit subjects (or descriptions) that got replayed, oldest-first.\n * Empty when the workspace was already at fromRef (no-op). */\n replayed: string[];\n /** Files / commits that conflicted during the rebase. Always\n * empty for a successful rebase — a non-empty conflicts list\n * means we threw WorkspaceConflictError before returning. The\n * field exists so the error's serialised payload can carry it. */\n conflicts: string[];\n}\n\nexport interface CommitSummary {\n /** Full commit / change id. */\n sha: string;\n /** First-line description / subject. */\n subject: string;\n /** Remainder of the commit message (may be empty). */\n body: string;\n /** Author display name, when the backend exposes one. */\n author: string;\n /** ISO-8601 author / commit timestamp. */\n authorDate: string;\n /** Compact relative author time (e.g. \"3m\", \"2d\"). */\n relTime: string;\n}\n\nexport interface ShowCommitResult {\n /** Captured VCS show output (possibly truncated). Empty string on error. */\n text: string;\n /** True when stdout exceeded SHOW_COMMIT_MAX_CHARS and was clipped. */\n truncated: boolean;\n /** Human-readable error message; omitted on success. */\n error?: string;\n}\n\n/** Cap captured `show` output so giant merge commits can't eat the TUI. */\nexport const SHOW_COMMIT_MAX_CHARS = 100_000;\n\n/**\n * Thrown by `rebaseTo` / `commitsSinceBase` on the `none` backend\n * (cp -a snapshots have no notion of a rebase target / fork point).\n * Maps to exit code 4.\n */\nexport class WorkspaceVcsRequiredError extends Error implements HasNextSteps {\n override readonly name = \"WorkspaceVcsRequiredError\";\n constructor(\n public readonly verb: string,\n public readonly workspacePath: string,\n ) {\n super(\n `vcs none: \\`mu workspace ${verb}\\` requires a real VCS (jj/sl/git); ${workspacePath} is a cp -a snapshot`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Free the snapshot and re-spawn with a real VCS backend\",\n command:\n \"mu workspace free <agent> && mu agent spawn <agent> --workspace --workspace-backend <jj|sl|git>\",\n },\n ];\n }\n}\n\n/**\n * Thrown by `rebaseTo` when the workspace has uncommitted changes\n * the rebase would clobber. Carries the dirty file list so the operator\n * can decide between commit/stash/--force. Maps to exit code 4.\n */\nexport class WorkspaceDirtyError extends Error implements HasNextSteps {\n override readonly name = \"WorkspaceDirtyError\";\n /** The verb that refused. Only `rebaseTo` refuses this way today,\n * but the field keeps the message + nextSteps self-describing. */\n public readonly verb: string;\n constructor(\n public readonly workspacePath: string,\n public readonly files: readonly string[],\n verb = \"rebase\",\n ) {\n super(\n `workspace dirty (${files.length} uncommitted file(s)): ${workspacePath}; refusing to ${verb}`,\n );\n this.verb = verb;\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Inspect the dirty files\",\n command: `(cd ${this.workspacePath} && git status -s) # or jj st / sl st`,\n },\n {\n intent: `Commit them first, then retry ${this.verb}`,\n command: `(cd ${this.workspacePath} && git add -A && git commit -m WIP)`,\n },\n {\n intent: \"Or stash them first (git only)\",\n command: `(cd ${this.workspacePath} && git stash)`,\n },\n {\n intent: \"Or DISCARD the workspace entirely (the lossy escape)\",\n command: \"mu workspace free <agent>\",\n },\n ];\n }\n}\n\n/**\n * Thrown by `rebaseTo` when the rebase produced conflicts. Carries the\n * conflicting paths (git/sl) or commits (jj). Maps to exit code 5.\n *\n * git and sl abort the rebase before throwing, so the workspace is back\n * at its pre-rebase state (`aborted` is true). jj cannot abort: the\n * rebase is done and the conflicts are committed in place (`aborted` is\n * false). Either way the row's parent_ref is unchanged, so the hint\n * ends with re-running refresh: once the conflicts are resolved, that\n * rebase is clean and records the base the disk is on.\n */\nexport class WorkspaceConflictError extends Error implements HasNextSteps {\n override readonly name = \"WorkspaceConflictError\";\n constructor(\n public readonly workspacePath: string,\n public readonly fromRef: string,\n public readonly conflicts: readonly string[],\n /** True when the backend aborted the rebase and the workspace is unchanged. */\n public readonly aborted = true,\n ) {\n super(\n `rebase onto ${fromRef} produced ${conflicts.length} conflict(s)${aborted ? \"; rebase aborted, workspace unchanged\" : \"; conflicts left in place\"}: ${workspacePath}`,\n );\n }\n errorNextSteps(): NextStep[] {\n const resync = {\n intent: \"After resolving, record the new base (a clean refresh updates the fork point)\",\n command: `mu workspace refresh <agent> --from '${this.fromRef}'`,\n };\n if (this.aborted) {\n return [\n {\n intent: \"The rebase was aborted. Rebase by hand in the workspace and resolve\",\n command: `cd ${this.workspacePath} # then: git rebase ${this.fromRef} (sl: sl rebase -d '${this.fromRef}')`,\n },\n resync,\n {\n intent: \"Or DISCARD the workspace entirely (the lossy escape)\",\n command: \"mu workspace free <agent>\",\n },\n ];\n }\n return [\n {\n intent: \"Resolve the conflicted commits in place\",\n command: `cd ${this.workspacePath} # then: jj resolve; or undo the rebase: jj op undo`,\n },\n resync,\n ];\n }\n}\n\nexport interface CreateWorkspaceOptions {\n /** The repository being branched from. Absolute path. */\n projectRoot: string;\n /** Where to place the new workspace. Absolute path; must NOT exist. */\n workspacePath: string;\n /** Optional commit / branch / changeset id to base off. Backend-specific:\n * git uses it as a `git worktree add`'s ref, jj as a revset, sl as a\n * rev. Undefined = current head. */\n parentRef?: string;\n}\n\nexport interface CreateWorkspaceResult {\n /** The actual ref the workspace points at (resolved to a stable id\n * when possible). Stored on the row; useful for `mu workspace list`\n * and for `--commit` flows. May be null for backends that don't\n * expose a meaningful parent (e.g. `none`). */\n parentRef: string | null;\n}\n\nexport interface FreeWorkspaceOptions {\n workspacePath: string;\n /** If true, attempt to commit any pending changes BEFORE removal.\n * Backend-specific: jj auto-commits via `jj describe + jj new`, git\n * needs an explicit commit on the worktree, sl needs `sl commit`,\n * none has nothing to commit. If pending changes exist and `commit`\n * is false, the on-disk directory still gets removed and changes are\n * lost — the verb prints a clear warning. */\n commit: boolean;\n}\n\nexport interface FreeWorkspaceResult {\n /** The commit id that captured the pending changes, when `commit` was\n * true and there was something to commit. Otherwise undefined. */\n committedRef?: string;\n /** Branch created to keep the workspace's commits reachable after the\n * worktree is removed (git with `commit`, when HEAD was on no branch,\n * remote or tag). Otherwise undefined. */\n branch?: string;\n /** True iff the on-disk path was actually removed (vs. already gone). */\n removed: boolean;\n}\n\nexport interface VcsBackend {\n readonly name: VcsBackendName;\n\n /** True iff this backend should handle `projectRoot`. Implementations\n * check for the relevant marker dir (`.jj`, `.sl`, `.git`); `none`\n * always returns true and is consulted last. */\n detect(projectRoot: string): Promise<boolean>;\n\n createWorkspace(opts: CreateWorkspaceOptions): Promise<CreateWorkspaceResult>;\n\n freeWorkspace(opts: FreeWorkspaceOptions): Promise<FreeWorkspaceResult>;\n\n /**\n * Count commits that the project's default branch (\"main\") has but\n * `ref` does not — i.e. how many commits `ref` is BEHIND main.\n *\n * Used by `mu workspace list` and `mu state` to surface staleness\n * (bug_workspace_stale_parent_silent_drift). Cheap, pure-observation:\n * NO automatic fetch. We compare against whatever main resolves to in\n * the workspace's LOCAL refs cache. The user can `git fetch` (or\n * equivalent) themselves if they want a fresher number.\n *\n * Returns null when:\n * - main / trunk cannot be resolved (no origin/HEAD, no origin/main,\n * no origin/master, no trunk() bookmark, etc.)\n * - the underlying VCS command fails for any reason (detached worktree,\n * missing refs, the `none` backend which has no notion of \"main\")\n *\n * Callers treat null as \"unknown — render — — and don't warn\".\n */\n commitsBehind(workspacePath: string, ref: string): Promise<number | null>;\n\n /**\n * Rebase the workspace onto `fromRef` (or the backend's tracked\n * base when undefined: `origin/HEAD` for git, `trunk()` for jj/sl).\n * Returns the resolved ref + replayed commits + conflicts list.\n *\n * Backend-specific behaviour:\n * - git: refuses on dirty WC (WorkspaceDirtyError); fetches first;\n * `git rebase <ref>`. On conflict, aborts the rebase and throws\n * WorkspaceConflictError (aborted); the workspace is unchanged.\n * Any other rebase failure is rethrown.\n * - jj: always-snapshotted, so dirty is never an issue. After\n * `jj rebase -d <ref>` the conflict-set is queried via\n * `jj log -r 'conflict()'`. Conflicts surface as\n * WorkspaceConflictError without an abort (jj's conflict markers\n * persist as commits; the operator resolves in-place).\n * - sl: like git. Refuses on dirty WC (WorkspaceDirtyError);\n * `sl rebase -d <ref>`; conflicts via `sl resolve --list`, then\n * aborts and throws WorkspaceConflictError (aborted). Any other\n * rebase failure is rethrown.\n * - none: throws WorkspaceVcsRequiredError unconditionally.\n *\n * Surfaced by fb_workspace_recycle_verb: dogfood between waves\n * needed `close → free → spawn` to refresh a worker against new\n * main; that killed the worker's LLM context. `refresh` updates\n * the on-disk dir without touching the agent or pane.\n */\n rebaseTo(\n workspacePath: string,\n fromRef?: string,\n /** The row's current parent_ref. Kept as the result's parentRef\n * when it already descends from fromRef and is under the head. */\n previousParentRef?: string,\n ): Promise<RebaseResult>;\n\n /**\n * Cheap \"is the working copy clean?\" probe used by close-auto-free\n * (allow_mu_agent_close_without_discard). Definition: ZERO uncommitted\n * changes (no working-tree modifications, no staged changes, no\n * untracked-not-ignored files). Pure observation; no fetch, no commit.\n *\n * Backend-specific:\n * - git: empty `git status --porcelain` output.\n * - jj: jj is auto-snapshotted, so the @ commit IS the WC; clean\n * here means @ has no diff from its parent (empty `jj diff\n * -r @ --summary`). A description-only difference still\n * counts as clean.\n * - sl: empty `sl status` output.\n * - none: meaningless (cp -a snapshot has no notion of\n * \"committed\" vs \"uncommitted\"); always returns true so the\n * close-auto-free path treats every none-workspace as\n * eligible for silent free (no commits can be lost; the only\n * loss is local file edits, which the operator implicitly\n * accepts by closing the agent).\n *\n * Returns false on any backend command failure — be conservative\n * (we'd rather refuse a close than auto-free a workspace whose\n * cleanliness we couldn't verify).\n */\n isClean(workspacePath: string): Promise<boolean>;\n\n /**\n * List commits the workspace has on top of `baseRef`, oldest-first.\n * Used by `mu workspace commits` (fb_workspace_commits_verb) to\n * promote the dogfood-painful\n * cd $(mu workspace path X) && git log <base>..HEAD\n * incantation into a typed verb that knows the workspace's\n * parent_ref. The CommitSummary fields survive subjects/bodies with\n * embedded newlines (NUL-delimited record format on the wire).\n *\n * `none` throws WorkspaceVcsRequiredError. Returns `[]` when the\n * workspace is exactly at baseRef (no commits since fork). Throws\n * on backend command failure (unknown ref, missing repo).\n */\n commitsSinceBase(workspacePath: string, baseRef: string): Promise<CommitSummary[]>;\n\n /** Last N commits on the project root, newest-first. Used by the\n * TUI Commits card / popup. Unlike commitsSinceBase, this is NOT\n * a per-workspace since-fork query. */\n recentCommits(projectRoot: string, limit: number): Promise<CommitSummary[]>;\n\n /** Show one commit / change from the project root, capped for TUI\n * rendering. Backend-specific equivalent of `git show <sha>`. */\n showCommit(projectRoot: string, sha: string): Promise<ShowCommitResult>;\n\n /**\n * Return the list of dirty (uncommitted / unstaged / untracked-not-\n * ignored) paths in the workspace. Empty array = clean.\n *\n * Used by `mu workspace list`'s dirty decoration and by the\n * dirty-check `rebaseTo` does internally before a refresh.\n *\n * Backend semantics:\n * - git: `git status --porcelain` (working-tree + staged +\n * untracked-not-ignored, mirroring the rebaseTo path).\n * - sl: `sl status` parsed for non-empty output.\n * - jj: always-snapshotted, so no concept of \"dirty\" — returns [].\n * - none: cp -a snapshots have no VCS, so we can't decide \"dirty\";\n * returns [] so the caller doesn't refuse for an unanswerable\n * question.\n *\n * Throws on backend command failure (the operator should see a\n * real error, not a silent \"clean\").\n */\n listDirtyFiles(workspacePath: string): Promise<string[]>;\n}\n","// mu — jj VCS backend.\n\nimport { existsSync } from \"node:fs\";\nimport {\n ensureParent,\n parseNulRecords,\n probeVcsRoot,\n rmDirSync,\n run,\n runShow,\n saneLimit,\n} from \"./helpers.js\";\nimport { type FreeWorkspaceResult, type VcsBackend, WorkspaceConflictError } from \"./types.js\";\n\n// `jj workspace add --name <name> <path>` shares the .jj/repo store\n// while giving each agent its own working copy. Workspaces are named\n// per repo; we use `<workstream>/<agent>` (the last two segments of\n// our on-disk layout) because agent names are unique only per\n// workstream.\n//\n// Free is two-step: `jj workspace forget` (no name: the current\n// workspace) from the workspace itself unregisters; then we rm the dir\n// since jj leaves the files behind. A dir that is already gone cannot\n// be forgotten from here (free has no project root), so createWorkspace\n// forgets a same-named registration whose root is missing, as git's\n// `worktree prune` does.\n//\n// --commit semantics for jj: jj's working copy is always automatically\n// snapshotted, so \"commit\" is really \"capture the current change_id\n// as the result.\" Nothing is ever lost; jj keeps all operations in\n// its op log indefinitely. We additionally call `jj describe` to set\n// a description IF the current commit's description is empty so the\n// captured ref is human-discoverable.\n\nexport const jjBackend: VcsBackend = {\n name: \"jj\",\n\n async detect(projectRoot) {\n return probeVcsRoot(\"jj\", [\"root\"], projectRoot);\n },\n\n async createWorkspace(opts) {\n if (existsSync(opts.workspacePath)) {\n throw new Error(`vcs jj: workspacePath already exists: ${opts.workspacePath}`);\n }\n await ensureParent(opts.workspacePath);\n const name = jjWorkspaceName(opts.workspacePath);\n await forgetIfRootMissing(name, opts.projectRoot);\n const args = [\"workspace\", \"add\", \"--name\", name];\n if (opts.parentRef) args.push(\"--revision\", opts.parentRef);\n args.push(opts.workspacePath);\n await run(\"jj\", args, opts.projectRoot);\n const commitId = await jjCommitId(opts.workspacePath);\n return { parentRef: commitId };\n },\n\n async freeWorkspace(opts) {\n if (!existsSync(opts.workspacePath)) {\n return { removed: false };\n }\n let committedRef: string | undefined;\n if (opts.commit) {\n const desc = await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n \"@\",\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--template\",\n \"description\",\n ],\n opts.workspacePath,\n );\n if (desc.trim().length === 0) {\n await run(\"jj\", [\"describe\", \"-m\", \"mu workspace free auto-commit\"], opts.workspacePath);\n }\n committedRef = await jjCommitId(opts.workspacePath);\n }\n // Bare `jj workspace forget` forgets the workspace it runs in, so\n // free never depends on how the name was derived (older mu used\n // the agent name alone). jj prints a hint about the working copy\n // becoming orphaned; we resolve that immediately by rm-ing the dir.\n await run(\"jj\", [\"workspace\", \"forget\"], opts.workspacePath);\n rmDirSync(opts.workspacePath);\n const result: FreeWorkspaceResult = { removed: true };\n if (committedRef !== undefined) result.committedRef = committedRef;\n return result;\n },\n\n // jj working-copy clean: @ has no diff from its parent.\n // `jj diff -r @ --summary` prints one line per changed file; empty\n // stdout = clean. jj's auto-snapshotting means there's no separate\n // \"untracked\" bucket — every working-tree change is already in @.\n async isClean(workspacePath) {\n if (!existsSync(workspacePath)) return false;\n try {\n const out = await run(\n \"jj\",\n [\"diff\", \"-r\", \"@\", \"--summary\", \"--no-pager\", \"--color\", \"never\"],\n workspacePath,\n );\n return out.length === 0;\n } catch {\n return false;\n }\n },\n\n // Compute commits-behind via jj's `trunk()` revset, which resolves\n // to the project's configured trunk (default-branch heuristic).\n // Returns null when trunk() is unresolvable (e.g. fresh repo with\n // no configured trunk) or when the log call fails.\n //\n // Pure observation: NO `jj git fetch`.\n async commitsBehind(workspacePath, ref) {\n if (!existsSync(workspacePath)) return null;\n try {\n // `<ref>..trunk()` is the set of commits reachable from trunk\n // but not from ref — exactly the staleness number. Template `\"x\\n\"`\n // gives one line per commit, which we count.\n const out = await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n `${ref}..trunk()`,\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--template\",\n '\"x\\\\n\"',\n ],\n workspacePath,\n );\n if (out.length === 0) return 0;\n return out.split(\"\\n\").filter((l) => l.length > 0).length;\n } catch {\n return null;\n }\n },\n\n // Rebase the workspace's @ onto `fromRef` (default = `trunk()`).\n // jj is always-snapshotted so dirty WC is never an issue — the auto-\n // snapshot becomes part of the rebase. After the rebase we query\n // `conflict()` to surface any commits that ended up conflicted; jj\n // doesn't auto-abort on conflicts (they materialise as commits with\n // conflict markers), so the workspace is left in a state the\n // operator can resolve in-place.\n async rebaseTo(workspacePath, fromRef, previousParentRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs jj: workspace path missing: ${workspacePath}`);\n }\n const target = fromRef ?? \"trunk()\";\n // @'s commit id before the rebase (reading it snapshots the WC\n // first). jj skips commits already in place, so an unchanged id\n // after the rebase means a no-op: nothing was replayed.\n const headBefore = await jjCommitId(workspacePath);\n await run(\"jj\", [\"rebase\", \"-d\", target], workspacePath);\n const moved = (await jjCommitId(workspacePath)) !== headBefore;\n // Replayed = first-line descriptions of commits in (target..@),\n // oldest-first, one per line; empty when the rebase was a no-op.\n const replayedRaw = moved\n ? await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n `${target}..@`,\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--reversed\",\n \"--template\",\n 'description.first_line() ++ \"\\\\n\"',\n ],\n workspacePath,\n ).catch(() => \"\")\n : \"\";\n const replayed = replayedRaw\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n // Conflict surface: list change_ids of commits in the rebased\n // range that are conflicted. Empty = clean rebase.\n const conflictRaw = await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n `(${target}..@) & conflict()`,\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--template\",\n 'change_id.short() ++ \"\\\\n\"',\n ],\n workspacePath,\n ).catch(() => \"\");\n const conflicts = conflictRaw\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n if (conflicts.length > 0) {\n // jj has no abort: the rebase stays with the conflicts. The row's\n // parent_ref is left alone; the operator resolves (or `jj op\n // undo`es) and re-runs refresh, whose clean no-op rebase records\n // the base the disk is on.\n throw new WorkspaceConflictError(workspacePath, target, conflicts, false);\n }\n // After `jj rebase -d <target>` the chain sits on <target>, so its\n // commit is the new fork point (the row's parent_ref), unless the\n // old fork point already descends from it and is still under @\n // (no-op refresh onto an older base): parent_ref never moves\n // backward. Omitted when the revset names zero or several commits.\n const targetId = await jjSingleCommitId(workspacePath, target);\n if (targetId === undefined) return { fromRef: target, replayed, conflicts: [] };\n const keepPrevious =\n previousParentRef !== undefined &&\n (await jjSingleCommitId(workspacePath, `${previousParentRef} & ${targetId}:: & ::@`)) !==\n undefined;\n const parentRef = keepPrevious ? previousParentRef : targetId;\n return { fromRef: target, parentRef, replayed, conflicts: [] };\n },\n\n // List jj commits in (baseRef..@), oldest-first, minus @ itself when\n // it is the empty, undescribed working-copy commit jj snapshots on\n // every command: that is \"no work yet\", not a commit. jj's templating\n // gives us per-field strings; we glue them with NUL field-separators\n // and \\x1e record-separators so multi-line descriptions/bodies\n // round-trip cleanly. The author timestamp template is\n // `author.timestamp().format(\"%Y-%m-%dT%H:%M:%S%:z\")` which is\n // ISO-8601 (matches git's --aiso strict / --aI).\n async commitsSinceBase(workspacePath, baseRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs jj: workspace path missing: ${workspacePath}`);\n }\n const out = await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n `(${baseRef}..@) ~ (@ & empty() & description(exact:\"\"))`,\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--reversed\",\n \"--template\",\n jjCommitSummaryTemplate,\n ],\n workspacePath,\n );\n return parseNulRecords(out);\n },\n\n async recentCommits(projectRoot, limit) {\n if (!existsSync(projectRoot)) {\n throw new Error(`vcs jj: project root missing: ${projectRoot}`);\n }\n const n = saneLimit(limit);\n if (n === 0) return [];\n const out = await run(\n \"jj\",\n [\n \"log\",\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"-r\",\n \"::@\",\n \"--limit\",\n String(n),\n \"--template\",\n jjCommitSummaryTemplate,\n ],\n projectRoot,\n );\n return parseNulRecords(out);\n },\n\n async showCommit(projectRoot, sha) {\n return runShow(\"jj\", [\"show\", sha, \"--color\", \"always\"], projectRoot);\n },\n\n // jj is always-snapshotted: there is no \"uncommitted\" state. The\n // working copy is itself a commit; the next snapshot folds any\n // edits in. Surface that by returning [] so dirty-checking callers\n // never refuse a jj workspace as \"dirty\".\n async listDirtyFiles(_workspacePath) {\n return [];\n },\n};\n\nconst jjCommitSummaryTemplate =\n 'commit_id ++ \"\\\\x00\" ++ description.first_line() ++ \"\\\\x00\" ++ description ++ \"\\\\x00\" ++ author.timestamp().format(\"%Y-%m-%dT%H:%M:%S%:z\") ++ \"\\\\x00\" ++ author.name() ++ \"\\\\x1e\"';\n\n/** jj workspace name for a mu workspace path:\n * <state>/workspaces/auth/worker-1 → auth/worker-1. Workstream and\n * agent names cannot contain \"/\", so the name is unique per repo. */\nexport function jjWorkspaceName(workspacePath: string): string {\n const parts = workspacePath.split(\"/\").filter((p) => p.length > 0);\n return parts.slice(-2).join(\"/\") || workspacePath;\n}\n\n// Forget `name` if jj still registers it but its root dir is gone (the\n// dir was rm -rf'd, or freed while already missing). jj reports such a\n// workspace's root as \"\". Best-effort: on a jj without the\n// `WorkspaceRef.root()` template, `workspace add` reports the clash.\nasync function forgetIfRootMissing(name: string, projectRoot: string): Promise<void> {\n try {\n const out = await run(\n \"jj\",\n [\n \"workspace\",\n \"list\",\n \"--color\",\n \"never\",\n \"--template\",\n 'name ++ \"\\\\x00\" ++ self.root() ++ \"\\\\x1e\"',\n ],\n projectRoot,\n );\n const stale = out\n .split(\"\\x1e\")\n .map((r) => r.trim().split(\"\\x00\"))\n .some(([n, root]) => n === name && root === \"\");\n if (stale) await run(\"jj\", [\"workspace\", \"forget\", name], projectRoot);\n } catch {\n // best-effort; `workspace add` surfaces any remaining conflict\n }\n}\n\nasync function jjSingleCommitId(\n workspacePath: string,\n revset: string,\n): Promise<string | undefined> {\n const out = await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n revset,\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--template\",\n 'commit_id ++ \"\\\\n\"',\n ],\n workspacePath,\n ).catch(() => \"\");\n const ids = out.split(\"\\n\").filter((l) => l.length > 0);\n return ids.length === 1 ? ids[0] : undefined;\n}\n\nasync function jjCommitId(workspacePath: string): Promise<string> {\n return run(\n \"jj\",\n [\"log\", \"-r\", \"@\", \"--no-graph\", \"--no-pager\", \"--color\", \"never\", \"--template\", \"commit_id\"],\n workspacePath,\n );\n}\n","// mu — non-VCS fallback backend.\n\nimport { existsSync } from \"node:fs\";\nimport { ensureParent, rmDirSync, run } from \"./helpers.js\";\nimport { type VcsBackend, WorkspaceVcsRequiredError } from \"./types.js\";\n\n// The fallback for projects that aren't under any VCS we recognise.\n// `cp -a` is heavy but correct; the workspace is a full snapshot.\n// Free deletes the snapshot. No commit semantics (no VCS to commit\n// against), so `--commit` is silently ignored.\n\nexport const noneBackend: VcsBackend = {\n name: \"none\",\n\n async detect(_projectRoot) {\n return true;\n },\n\n async createWorkspace(opts) {\n if (existsSync(opts.workspacePath)) {\n throw new Error(`vcs none: workspacePath already exists: ${opts.workspacePath}`);\n }\n await ensureParent(opts.workspacePath);\n // `cp -a` is GNU/BSD-portable for \"preserve everything, recursive\".\n await run(\"cp\", [\"-a\", `${opts.projectRoot}/.`, opts.workspacePath]);\n return { parentRef: null };\n },\n\n async freeWorkspace(opts) {\n const removed = rmDirSync(opts.workspacePath);\n return { removed };\n },\n\n // The `none` backend has no VCS to compare against — there's no\n // notion of \"main\" for a `cp -a` snapshot. Always null.\n async commitsBehind(_workspacePath, _ref) {\n return null;\n },\n\n // none has no notion of clean — a cp -a snapshot doesn't track\n // committed vs uncommitted state. Returning true makes the\n // close-auto-free path silently free a none-workspace (consistent\n // with the fact that there are no commits to lose).\n async isClean(_workspacePath) {\n return true;\n },\n\n // none has no upstream to rebase onto. Throw a typed error so the\n // CLI's handle() maps it to exit 4 with a clean Next: hint.\n async rebaseTo(workspacePath, _fromRef) {\n throw new WorkspaceVcsRequiredError(\"refresh\", workspacePath);\n },\n\n // none has no notion of a fork point either — a cp -a snapshot\n // doesn't track history. Same typed error as rebaseTo.\n async commitsSinceBase(workspacePath, _baseRef) {\n throw new WorkspaceVcsRequiredError(\"commits\", workspacePath);\n },\n\n async recentCommits(_projectRoot, _limit) {\n return [];\n },\n\n async showCommit(_projectRoot, _sha) {\n return { text: \"\", truncated: false, error: \"vcs none: no commits to show\" };\n },\n\n // No VCS → nothing to compare against; \"dirty\" is unanswerable.\n // Callers treat [] as \"clean\" and proceed.\n async listDirtyFiles(_workspacePath) {\n return [];\n },\n};\n","// mu — Sapling VCS backend.\n\nimport { existsSync } from \"node:fs\";\nimport {\n ensureParent,\n isSaplingDotdir,\n parseNulRecords,\n probeVcsRootPath,\n rmDirSync,\n run,\n runShow,\n saneLimit,\n} from \"./helpers.js\";\nimport {\n type FreeWorkspaceResult,\n type VcsBackend,\n WorkspaceConflictError,\n WorkspaceDirtyError,\n} from \"./types.js\";\n\n// `sl worktree` exists in Sapling but only for EdenFS-backed repos.\n// For portability we use `sl clone` instead, which works on any\n// sapling install. The trade-off is heavier (history copy) vs lighter\n// (shared store), but the workspace is fully isolated either way —\n// which is what we care about. EdenFS-specific worktree optimization\n// can layer on later if anyone hits the friction.\n//\n// Free is just rm -rf: sapling has no formal \"unclone\" because each\n// clone is a self-contained repo.\n\nexport const slBackend: VcsBackend = {\n name: \"sl\",\n\n async detect(projectRoot) {\n const root = await probeVcsRootPath(\"sl\", [\"root\"], projectRoot);\n if (root === null) return false;\n // Meta's Sapling build can transparently operate in plain git repos\n // by creating `.git/sl`. That should not beat git in BACKENDS order.\n // `--dotdir` keeps true sl / hg-compat repos (`.sl` / `.hg`) distinct.\n const dotdir = await probeVcsRootPath(\"sl\", [\"root\", \"--dotdir\"], projectRoot);\n return dotdir !== null && isSaplingDotdir(dotdir);\n },\n\n async createWorkspace(opts) {\n if (existsSync(opts.workspacePath)) {\n throw new Error(`vcs sl: workspacePath already exists: ${opts.workspacePath}`);\n }\n await ensureParent(opts.workspacePath);\n const args = [\"clone\"];\n if (opts.parentRef) args.push(\"-r\", opts.parentRef);\n args.push(opts.projectRoot, opts.workspacePath);\n await run(\"sl\", args);\n const commitId = await slCommitId(opts.workspacePath);\n return { parentRef: commitId };\n },\n\n async freeWorkspace(opts) {\n if (!existsSync(opts.workspacePath)) {\n return { removed: false };\n }\n let committedRef: string | undefined;\n if (opts.commit && (await slIsDirty(opts.workspacePath))) {\n // sl commit -A: stage all (including untracked), commit. Exits\n // non-zero if nothing changed, but we just guarded above.\n await run(\n \"sl\",\n [\n \"--config\",\n \"ui.username=mu <mu@local>\",\n \"commit\",\n \"-A\",\n \"-m\",\n \"mu workspace free auto-commit\",\n ],\n opts.workspacePath,\n );\n committedRef = await slCommitId(opts.workspacePath);\n }\n rmDirSync(opts.workspacePath);\n const result: FreeWorkspaceResult = { removed: true };\n if (committedRef !== undefined) result.committedRef = committedRef;\n return result;\n },\n\n // sl working-copy clean: empty `sl status` output. Same shape as\n // listSlDirtyFiles below but inlined to keep the failure-mode\n // boundary tight (any throw → not clean).\n async isClean(workspacePath) {\n if (!existsSync(workspacePath)) return false;\n try {\n const out = await run(\"sl\", [\"status\"], workspacePath);\n return out.length === 0;\n } catch {\n return false;\n }\n },\n\n // Same shape as the jj impl: count commits in trunk() not reachable\n // from ref. Sapling's revset language is close enough to jj's that\n // the same idiom works. Returns null when trunk() is unresolvable\n // (fresh repo, missing remote bookmark, etc.) or the log fails.\n //\n // Pure observation: NO `sl pull`.\n async commitsBehind(workspacePath, ref) {\n if (!existsSync(workspacePath)) return null;\n try {\n const out = await run(\n \"sl\",\n [\"log\", \"-r\", `${ref}::trunk() - ${ref}`, \"--template\", \"x\\\\n\"],\n workspacePath,\n );\n if (out.length === 0) return 0;\n return out.split(\"\\n\").filter((l) => l.length > 0).length;\n } catch {\n return null;\n }\n },\n\n // Rebase the active draft chain onto `fromRef` (default = `trunk()`).\n // We pre-check the WC and refuse with the typed WorkspaceDirtyError.\n // `sl rebase` exits 0 on success (also when there is nothing to\n // rebase) and 1 on unresolved conflicts. Conflict surface via\n // `sl resolve --list --tool=internal:dumpjson` is brittle across\n // versions, so on failure we read the textual `sl resolve --list`\n // and keep the U-prefixed (unresolved) lines. A failure with no\n // unresolved files (bad ref, unresolvable trunk()) is rethrown, as\n // the git impl does.\n async rebaseTo(workspacePath, fromRef, previousParentRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs sl: workspace path missing: ${workspacePath}`);\n }\n const target = fromRef ?? \"trunk()\";\n const dirtyFiles = await listSlDirtyFiles(workspacePath);\n if (dirtyFiles.length > 0) {\n throw new WorkspaceDirtyError(workspacePath, dirtyFiles);\n }\n // `.`'s node before the rebase: unchanged afterwards means sl had\n // nothing to rebase, so nothing was replayed.\n const headBefore = await slCommitId(workspacePath);\n try {\n await run(\n \"sl\",\n [\"--config\", \"ui.username=mu <mu@local>\", \"rebase\", \"-d\", target],\n workspacePath,\n );\n } catch (err) {\n const conflicts = await listSlUnresolved(workspacePath);\n // Best-effort abort so the workspace returns to its pre-rebase\n // state, mirroring the git impl's never-leave-half-rebased policy.\n await run(\"sl\", [\"rebase\", \"--abort\"], workspacePath).catch(() => {});\n if (conflicts.length > 0) {\n throw new WorkspaceConflictError(workspacePath, target, conflicts);\n }\n throw err;\n }\n // After the rebase the draft chain sits on `target`, so `target`'s\n // node is the new fork point (recorded as the row's parent_ref),\n // unless the old fork point already descends from it and is still\n // under `.` (no-op refresh onto an older base): parent_ref never\n // moves backward.\n const targetNode = await run(\n \"sl\",\n [\"log\", \"-r\", `last(${target})`, \"--template\", \"{node}\"],\n workspacePath,\n ).catch(() => \"\");\n const keepPrevious =\n previousParentRef !== undefined &&\n targetNode.length > 0 &&\n (await run(\n \"sl\",\n [\"log\", \"-r\", `${previousParentRef} & ${targetNode}:: & ::.`, \"--template\", \"{node}\"],\n workspacePath,\n ).catch(() => \"\")) !== \"\";\n const parentRef = keepPrevious ? previousParentRef : targetNode;\n // Replayed = log of `target..` post-rebase, oldest-first. Single-\n // line subjects via `{desc|firstline}`. Empty when nothing replayed.\n const moved = (await slCommitId(workspacePath)) !== headBefore;\n const replayedRaw = moved\n ? await run(\n \"sl\",\n [\"log\", \"-r\", `${target}::. - ${target}`, \"--template\", \"{desc|firstline}\\\\n\"],\n workspacePath,\n ).catch(() => \"\")\n : \"\";\n const replayed = replayedRaw\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n return parentRef.length > 0\n ? { fromRef: target, parentRef, replayed, conflicts: [] }\n : { fromRef: target, replayed, conflicts: [] };\n },\n\n // List sl commits in (baseRef..., minus baseRef itself), oldest-\n // first. Same NUL-field / \\x1e-record format as the jj impl;\n // sl's templating uses {field} braces (not jj's `++` operator) but\n // the field set is equivalent ({node}, {desc|firstline}, {desc},\n // {date|isodate}).\n async commitsSinceBase(workspacePath, baseRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs sl: workspace path missing: ${workspacePath}`);\n }\n const out = await run(\n \"sl\",\n [\"log\", \"-r\", `${baseRef}::. - ${baseRef}`, \"--template\", slCommitSummaryTemplate],\n workspacePath,\n );\n // sl emits oldest-last by default; reverse to oldest-first to match\n // the git/jj contract.\n return parseNulRecords(out).reverse();\n },\n\n async recentCommits(projectRoot, limit) {\n if (!existsSync(projectRoot)) {\n throw new Error(`vcs sl: project root missing: ${projectRoot}`);\n }\n const n = saneLimit(limit);\n if (n === 0) return [];\n const out = await run(\n \"sl\",\n [\"log\", \"-l\", String(n), \"--template\", slCommitSummaryTemplate],\n projectRoot,\n );\n return parseNulRecords(out);\n },\n\n async showCommit(projectRoot, sha) {\n return runShow(\"sl\", [\"show\", sha, \"--color=always\"], projectRoot);\n },\n\n async listDirtyFiles(workspacePath) {\n if (!existsSync(workspacePath)) return [];\n return listSlDirtyFiles(workspacePath);\n },\n};\n\n/**\n * List dirty paths for a sapling workspace. `sl status` prints one\n * line per changed file: `<status> <path>`. Empty = clean.\n */\nasync function listSlDirtyFiles(workspacePath: string): Promise<string[]> {\n const out = await run(\"sl\", [\"status\"], workspacePath);\n if (out.length === 0) return [];\n return out\n .split(\"\\n\")\n .filter((l) => l.length > 0)\n .map((l) => l.slice(2));\n}\n\n/**\n * List unresolved (conflicting) paths after an `sl rebase`. `sl resolve\n * --list` prints `<U|R> <path>` per file; U = unresolved.\n */\nasync function listSlUnresolved(workspacePath: string): Promise<string[]> {\n try {\n const out = await run(\"sl\", [\"resolve\", \"--list\"], workspacePath);\n if (out.length === 0) return [];\n return out\n .split(\"\\n\")\n .filter((l) => l.startsWith(\"U \"))\n .map((l) => l.slice(2));\n } catch {\n return [];\n }\n}\n\nconst slCommitSummaryTemplate =\n \"{node}\\\\0{desc|firstline}\\\\0{desc}\\\\0{date|isodatesec}\\\\0{author|user}\\\\x1e\";\n\nasync function slCommitId(workspacePath: string): Promise<string> {\n return run(\"sl\", [\"log\", \"-r\", \".\", \"--template\", \"{node}\"], workspacePath);\n}\n\nasync function slIsDirty(workspacePath: string): Promise<boolean> {\n // sl status prints one line per changed file; empty stdout = clean.\n const out = await run(\"sl\", [\"status\"], workspacePath);\n return out.length > 0;\n}\n","// mu — VCS backend dispatcher.\n\nimport { gitBackend } from \"./git.js\";\nimport { jjBackend } from \"./jj.js\";\nimport { noneBackend } from \"./none.js\";\nimport { slBackend } from \"./sl.js\";\nimport type { VcsBackend, VcsBackendName } from \"./types.js\";\n\nexport { gitBackend } from \"./git.js\";\nexport { jjBackend } from \"./jj.js\";\nexport { noneBackend } from \"./none.js\";\nexport { slBackend } from \"./sl.js\";\nexport {\n type CommitSummary,\n type CreateWorkspaceOptions,\n type CreateWorkspaceResult,\n type FreeWorkspaceOptions,\n type FreeWorkspaceResult,\n type RebaseResult,\n SHOW_COMMIT_MAX_CHARS,\n type ShowCommitResult,\n type VcsBackend,\n type VcsBackendName,\n WorkspaceConflictError,\n WorkspaceDirtyError,\n WorkspaceVcsRequiredError,\n} from \"./types.js\";\n\n/**\n * Detection precedence: jj > sl > git > none. The first backend whose\n * detect() returns true wins. `none` is always last. Detection shells\n * out to each VCS's canonical root probe (`jj root`, `sl root`, `git\n * rev-parse --show-toplevel`) so worktrees and gitdir-pointer files are\n * handled by the owning tool instead of a brittle marker-dir heuristic.\n */\nconst BACKENDS: readonly VcsBackend[] = [jjBackend, slBackend, gitBackend, noneBackend];\n\n/** Return the backend that should handle projectRoot. Walks BACKENDS\n * in precedence order; never returns undefined because noneBackend\n * always claims. */\nexport async function detectBackend(projectRoot: string): Promise<VcsBackend> {\n for (const backend of BACKENDS) {\n if (await backend.detect(projectRoot)) return backend;\n }\n return noneBackend;\n}\n\n/** Look up a backend by name. Throws on unknown name. Used by\n * `mu agent spawn --workspace-backend ...` to honour an explicit\n * override. */\nexport function backendByName(name: VcsBackendName): VcsBackend {\n for (const backend of BACKENDS) {\n if (backend.name === name) return backend;\n }\n throw new Error(`unknown vcs backend: ${name}`);\n}\n","// mu — workspace staleness and dirty decorators.\n\nimport type { Db } from \"../db.js\";\nimport { isWorkspaceStale } from \"../staleness.js\";\nimport { backendByName } from \"../vcs.js\";\nimport type { WorkspaceRow, WorkspaceStaleness } from \"./core.js\";\nimport { getWorkspaceForAgent } from \"./crud.js\";\n\nconst DECORATE_CONCURRENCY = 4;\n\nexport async function getWorkspaceStaleness(\n db: Db,\n agentName: string,\n workstreamName: string,\n): Promise<WorkspaceStaleness | null> {\n const row = getWorkspaceForAgent(db, agentName, workstreamName);\n if (row === undefined) return null;\n const [decorated] = await decorateWithStaleness([row]);\n const commitsBehindMain = decorated?.commitsBehindMain ?? null;\n return {\n agentName,\n workstreamName,\n commitsBehindMain,\n isStale: isWorkspaceStale(commitsBehindMain),\n };\n}\n\n/**\n * Decorate each row with `commitsBehindMain` by asking the row's backend\n * how far the parent_ref is behind the project's default branch HEAD.\n * Cheap, pure observation: NO automatic `git fetch` / `jj git fetch` /\n * `sl pull`. The number is as fresh as the workspace's local refs cache.\n *\n * Returns a NEW array; does not mutate the input. Rows whose parent_ref\n * is missing, or whose backend's commitsBehind throws / returns null,\n * get `commitsBehindMain: null`.\n */\nexport async function decorateWithStaleness(\n rows: readonly WorkspaceRow[],\n): Promise<WorkspaceRow[]> {\n const cache = new Map<string, Promise<number | null>>();\n const fetchBehind = (r: WorkspaceRow): Promise<number | null> => {\n const parentRef = r.parentRef;\n if (parentRef === null) return Promise.resolve(null);\n const key = `${r.backend}\\x00${parentRef}`;\n const cached = cache.get(key);\n if (cached !== undefined) return cached;\n const p = (async (): Promise<number | null> => {\n try {\n const backend = backendByName(r.backend);\n return await backend.commitsBehind(r.path, parentRef);\n } catch {\n return null;\n }\n })();\n cache.set(key, p);\n return p;\n };\n return mapWithConcurrency(rows, DECORATE_CONCURRENCY, async (r) => ({\n ...r,\n commitsBehindMain: await fetchBehind(r),\n }));\n}\n\n/**\n * Decorate every row with a `dirty` marker — true when the backend's\n * `listDirtyFiles` reports any uncommitted / unstaged / untracked-not-\n * ignored files; false when clean; null on backend-command failure.\n *\n * Returns a NEW array; does not mutate the input.\n */\nexport async function decorateWithDirty(rows: readonly WorkspaceRow[]): Promise<WorkspaceRow[]> {\n return mapWithConcurrency(rows, DECORATE_CONCURRENCY, async (r) => {\n let dirty: boolean | null;\n try {\n const backend = backendByName(r.backend);\n const files = await backend.listDirtyFiles(r.path);\n dirty = files.length > 0;\n } catch {\n dirty = null;\n }\n return { ...r, dirty };\n });\n}\n\n/**\n * Tiny p-limit-style helper. Keeps at most `limit` callbacks in flight\n * at once and preserves input order in the result. Stays in this file\n * because it has exactly two local decorator callers; promote out only\n * when a second cluster needs it.\n */\nasync function mapWithConcurrency<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let next = 0;\n const worker = async (): Promise<void> => {\n while (true) {\n const i = next++;\n if (i >= items.length) return;\n const item = items[i] as T;\n results[i] = await fn(item, i);\n }\n };\n const workerCount = Math.min(Math.max(1, limit), items.length);\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n return results;\n}\n","// mu — workspace orphan directory detection.\n\nimport { readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { type Db, defaultStateDir, tryResolveWorkstreamId } from \"../db.js\";\nimport { workspacesRoot } from \"./core.js\";\nimport { listWorkspaces } from \"./crud.js\";\n\nexport interface WorkspaceOrphan {\n /** The on-disk dir name (the agent name it WOULD be for, if mu had\n * registered it). */\n agentName: string;\n /** Workstream the dir is filed under. */\n workstreamName: string;\n /** Absolute path to the orphan dir. */\n path: string;\n}\n\n/**\n * Like WorkspaceOrphan but additionally flags whether the parent\n * workstream itself is gone (no row in `workstreams`). Returned by\n * listAllOrphanWorkspaces; the per-workstream listWorkspaceOrphans\n * doesn't carry this since by construction it only runs against an\n * existing workstream.\n */\nexport interface StrandedWorkspaceOrphan extends WorkspaceOrphan {\n /** True iff the parent workstream has no DB row (the dir was left\n * behind by a `mu workstream teardown` or a manual DELETE). */\n stranded: boolean;\n}\n\n/**\n * Scan `<state-dir>/workspaces/<workstream>/` for directories that\n * have no row in `vcs_workspaces`.\n *\n * Returns `[]` when the workstream's workspaces dir doesn't exist,\n * or when every dir on disk has a corresponding DB row. Filesystem\n * read is best-effort: a missing/inaccessible dir returns `[]`.\n */\nexport function listWorkspaceOrphans(db: Db, workstream: string): WorkspaceOrphan[] {\n const root = workspacesRoot(workstream);\n let dirs: string[];\n try {\n dirs = readdirSync(root, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n return [];\n }\n const registered = new Set(listWorkspaces(db, workstream).map((w) => w.path));\n const orphans: WorkspaceOrphan[] = [];\n for (const agentDir of dirs) {\n const fullPath = join(root, agentDir);\n if (!registered.has(fullPath)) {\n orphans.push({ agentName: agentDir, workstreamName: workstream, path: fullPath });\n }\n }\n return orphans;\n}\n\n/**\n * Cross-workstream variant of listWorkspaceOrphans. Reads\n * `<state-dir>/workspaces/`, recurses one level (per-ws subdir →\n * per-agent subdir), and surfaces every dir with no row in\n * `vcs_workspaces`.\n */\nexport function listAllOrphanWorkspaces(db: Db): StrandedWorkspaceOrphan[] {\n const root = join(defaultStateDir(), \"workspaces\");\n let wsDirs: string[];\n try {\n wsDirs = readdirSync(root, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n return [];\n }\n const registered = new Set(listWorkspaces(db).map((w) => w.path));\n const orphans: StrandedWorkspaceOrphan[] = [];\n for (const wsName of wsDirs) {\n const wsRoot = join(root, wsName);\n let agentDirs: string[];\n try {\n agentDirs = readdirSync(wsRoot, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n continue;\n }\n const stranded = tryResolveWorkstreamId(db, wsName) === null;\n for (const agentDir of agentDirs) {\n const fullPath = join(wsRoot, agentDir);\n if (!registered.has(fullPath)) {\n orphans.push({\n agentName: agentDir,\n workstreamName: wsName,\n path: fullPath,\n stranded,\n });\n }\n }\n }\n return orphans;\n}\n","// mu — task graph shared row-shape helpers.\n//\n// This module owns the snake_case → camelCase task/note mappings and\n// surrogate-id resolution helpers consumed by the task SDK cluster.\n// It stays below the public hub (`src/tasks.ts`): cluster files import\n// from this file or sibling files, never from the hub they're re-exported\n// through.\n\nimport { type Db, tryResolveWorkstreamId } from \"../db.js\";\nimport type { TaskStatus, TaskSubstate } from \"./status.js\";\n\nexport interface TaskRow {\n /** Per-workstream-unique TEXT name. The operator-facing identifier. */\n name: string;\n /** Foreign-name reference to the owning workstream. */\n workstreamName: string;\n title: string;\n status: TaskStatus;\n /** Qualifies status (e.g. OPEN/parked, CLOSED/wontfix). Never null. */\n substate: TaskSubstate;\n impact: number;\n effortDays: number;\n /** Foreign-name reference to the owning agent (NULL when unowned). */\n ownerName: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface TaskNoteRow {\n author: string | null;\n content: string;\n createdAt: string;\n}\n\nexport interface RawTaskRow {\n /** Surrogate id (v5). */\n id: number;\n local_id: string;\n /** Joined from workstreams.name. */\n workstream: string;\n title: string;\n status: string;\n substate: string;\n impact: number;\n effort_days: number;\n /** Joined from agents.name via owner_id. NULL when unowned. */\n owner: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface RawTaskNoteRow {\n author: string | null;\n content: string;\n created_at: string;\n}\n\n// SELECT clause for v5 task reads. Joins workstreams + agents to expose\n// the operator-facing names as `workstream` and `owner`. Used by every\n// read path so callers don't see surrogate ids.\nexport const SELECT_TASK_COLS = `\n t.id AS id,\n t.local_id AS local_id,\n ws.name AS workstream,\n t.title AS title,\n t.status AS status,\n t.substate AS substate,\n t.impact AS impact,\n t.effort_days AS effort_days,\n ag.name AS owner,\n t.created_at AS created_at,\n t.updated_at AS updated_at\n`;\n\nexport const TASK_FROM_JOIN = `\n FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n LEFT JOIN agents ag ON ag.id = t.owner_id\n`;\n\nexport const SELECT_NOTE_COLS = `\n n.author AS author,\n n.content AS content,\n n.created_at AS created_at\n`;\n\nexport function rowFromDb(row: RawTaskRow): TaskRow {\n return {\n name: row.local_id,\n workstreamName: row.workstream,\n title: row.title,\n status: row.status as TaskStatus,\n substate: row.substate as TaskSubstate,\n impact: row.impact,\n effortDays: row.effort_days,\n ownerName: row.owner,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport function noteFromDb(row: RawTaskNoteRow): TaskNoteRow {\n return {\n author: row.author,\n content: row.content,\n createdAt: row.created_at,\n };\n}\n\n/** Look up a task by local_id across every workstream. Task ids are\n * unique per workstream only, so a row in `preferWorkstream` wins;\n * otherwise the first match sorted by workstream name (deterministic).\n * Used by edge verbs (addBlockEdge / reparentTask) to resolve a\n * blocker so a cross-workstream blocker can surface\n * `CrossWorkstreamEdgeError` rather than the less-actionable\n * `TaskNotFoundError`. NOT for operator-facing reads — use\n * `getTask(db, localId, workstream)` for those. */\nexport function lookupTaskAnyWorkstream(\n db: Db,\n localId: string,\n preferWorkstream?: string,\n): TaskRow | undefined {\n const row = db\n .prepare(\n `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} WHERE t.local_id = ?\n ORDER BY (ws.name = ?) DESC, ws.name LIMIT 1`,\n )\n .get(localId, preferWorkstream ?? null) as RawTaskRow | undefined;\n return row ? rowFromDb(row) : undefined;\n}\n\n/** Resolve a (workstream, localId) pair to the surrogate task id.\n * Returns null on miss. */\nexport function taskIdFor(db: Db, localId: string, workstream: string): number | null {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return null;\n const row = db\n .prepare(\"SELECT id FROM tasks WHERE workstream_id = ? AND local_id = ?\")\n .get(wsId, localId) as { id: number } | undefined;\n return row ? row.id : null;\n}\n\n/**\n * Bump `tasks.updated_at` on a surrogate task id. Used by every write\n * that mutates a child row (notes, edges) without touching the task\n * row itself, so `mu task list --sort recency` reflects 'last write of\n * any kind' rather than only 'last status/field change'\n * (task_updatedat_not_bumped_by_reparent). Status changes, field\n * updates, and claim/release already update `updated_at` directly in\n * their own UPDATE statements; don't double-bump from here.\n *\n * Always called inside the same transaction as the child-row mutation\n * so the bump rolls back together with the mutation on error.\n */\nexport function touchTask(db: Db, taskId: number, now?: string): void {\n db.prepare(\"UPDATE tasks SET updated_at = ? WHERE id = ?\").run(\n now ?? new Date().toISOString(),\n taskId,\n );\n}\n","// mu — task read/query primitives.\n\nimport type { Db } from \"../db.js\";\nimport { tryResolveWorkstreamId } from \"../db.js\";\nimport { lastClaimEventAt } from \"../logs.js\";\nimport {\n noteFromDb,\n type RawTaskNoteRow,\n type RawTaskRow,\n rowFromDb,\n SELECT_NOTE_COLS,\n SELECT_TASK_COLS,\n TASK_FROM_JOIN,\n type TaskNoteRow,\n type TaskRow,\n taskIdFor,\n} from \"./core.js\";\nimport type { TaskStatus, TaskSubstate } from \"./status.js\";\n\nexport function getTask(db: Db, localId: string, workstream: string): TaskRow | undefined {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return undefined;\n const row = db\n .prepare(\n `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} WHERE t.workstream_id = ? AND t.local_id = ?`,\n )\n .get(wsId, localId) as RawTaskRow | undefined;\n return row ? rowFromDb(row) : undefined;\n}\n\n/**\n * The task's owner as (name, workstream), resolved through\n * `tasks.owner_id`. The owner's workstream can differ from the task's:\n * `task claim --for <ws>/<agent>` assigns an owner from another\n * workstream, and `TaskRow.ownerName` alone cannot say which.\n * Undefined when the task does not exist or has no owner.\n */\nexport function getTaskOwner(\n db: Db,\n localId: string,\n workstream: string,\n): { name: string; workstreamName: string } | undefined {\n return db\n .prepare(\n `SELECT a.name AS name, aws.name AS workstreamName\n FROM tasks t\n JOIN workstreams tws ON tws.id = t.workstream_id\n JOIN agents a ON a.id = t.owner_id\n JOIN workstreams aws ON aws.id = a.workstream_id\n WHERE t.local_id = ? AND tws.name = ?`,\n )\n .get(localId, workstream) as { name: string; workstreamName: string } | undefined;\n}\n\n/**\n * List tasks. With no `workstream` arg returns every row — used by `mu sql`\n * and by tests; CLI surfaces always pass a workstream so users only see\n * their own.\n */\nexport interface ListTasksOptions {\n /** Filter to one or more lifecycle statuses. Omitted = all statuses. */\n status?: TaskStatus | readonly TaskStatus[];\n /** Filter to one or more substates (ANDed with `status`). */\n substate?: TaskSubstate | readonly TaskSubstate[];\n}\n\n/** Normalise a one-or-many filter option to an array (undefined = no filter). */\nfunction asList<T extends string>(v: T | readonly T[] | undefined): readonly T[] | undefined {\n if (v === undefined) return undefined;\n return typeof v === \"string\" ? [v] : (v as readonly T[]);\n}\n\nexport function listTasks(db: Db, workstream?: string, opts: ListTasksOptions = {}): TaskRow[] {\n const statuses =\n opts.status === undefined\n ? undefined\n : Array.isArray(opts.status)\n ? (opts.status as TaskStatus[])\n : [opts.status as TaskStatus];\n\n const where: string[] = [];\n const params: unknown[] = [];\n if (workstream !== undefined) {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n where.push(\"t.workstream_id = ?\");\n params.push(wsId);\n }\n if (statuses !== undefined) {\n where.push(`t.status IN (${statuses.map(() => \"?\").join(\", \")})`);\n params.push(...statuses);\n }\n const substates = asList(opts.substate);\n if (substates !== undefined && substates.length > 0) {\n where.push(`t.substate IN (${substates.map(() => \"?\").join(\", \")})`);\n params.push(...substates);\n }\n const sql =\n where.length === 0\n ? `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} ORDER BY t.local_id`\n : `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} WHERE ${where.join(\" AND \")} ORDER BY t.local_id`;\n const rows = db.prepare(sql).all(...params) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\n// The three views (ready, blocked, goals) project tasks.* directly,\n// so they expose v5 columns (id, workstream_id, owner_id). We wrap\n// them with the same JOINs as TASK_FROM_JOIN to translate back to the\n// operator-facing TaskRow shape (workstream + owner as TEXT names).\nconst VIEW_FROM_JOIN = (view: string) => `\n FROM ${view} v\n JOIN workstreams ws ON ws.id = v.workstream_id\n LEFT JOIN agents ag ON ag.id = v.owner_id\n`;\nconst SELECT_VIEW_COLS = `\n v.id AS id,\n v.local_id AS local_id,\n ws.name AS workstream,\n v.title AS title,\n v.status AS status,\n v.substate AS substate,\n v.impact AS impact,\n v.effort_days AS effort_days,\n ag.name AS owner,\n v.created_at AS created_at,\n v.updated_at AS updated_at\n`;\n\n/** Options for listReady. The optional `statuses` filter composes\n * on top of the `ready` view (which itself constrains to\n * `status='OPEN'`); passing only OPEN is identical to today's no-\n * filter shape, passing only non-OPEN values returns []. Exists so\n * `mu task next --status` can mirror the multi-status flag shape\n * shipped on `mu task list` (task_list_multi_status_union). */\nexport interface ListReadyOptions {\n status?: TaskStatus | readonly TaskStatus[];\n /** Substate filter; the `ready` view already excludes OPEN/parked. */\n substate?: TaskSubstate | readonly TaskSubstate[];\n}\n\nexport function listReady(db: Db, workstream: string, opts: ListReadyOptions = {}): TaskRow[] {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const statuses =\n opts.status === undefined\n ? undefined\n : Array.isArray(opts.status)\n ? (opts.status as TaskStatus[])\n : [opts.status as TaskStatus];\n const where: string[] = [\"v.workstream_id = ?\"];\n const params: unknown[] = [wsId];\n if (statuses !== undefined && statuses.length > 0) {\n where.push(`v.status IN (${statuses.map(() => \"?\").join(\", \")})`);\n params.push(...statuses);\n }\n const substates = asList(opts.substate);\n if (substates !== undefined && substates.length > 0) {\n where.push(`v.substate IN (${substates.map(() => \"?\").join(\", \")})`);\n params.push(...substates);\n }\n const rows = db\n .prepare(\n `SELECT ${SELECT_VIEW_COLS} ${VIEW_FROM_JOIN(\"ready\")} WHERE ${where.join(\" AND \")} ORDER BY v.local_id`,\n )\n .all(...params) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\nexport function listBlocked(db: Db, workstream: string): TaskRow[] {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_VIEW_COLS} ${VIEW_FROM_JOIN(\"blocked\")} WHERE v.workstream_id = ? ORDER BY v.local_id`,\n )\n .all(wsId) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\nexport function listGoals(db: Db, workstream: string): TaskRow[] {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_VIEW_COLS} ${VIEW_FROM_JOIN(\"goals\")} WHERE v.workstream_id = ? ORDER BY v.local_id`,\n )\n .all(wsId) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\n/** All IN_PROGRESS tasks in a workstream, most-recently-touched first.\n * Used by `mu state` to populate its in-progress slice; exposed as a\n * named SDK helper so CLI renderers don't re-derive the row-shape\n * conversion (review_code_raw_task_state_duplicate). */\nexport function listInProgress(db: Db, workstream: string): TaskRow[] {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} WHERE t.workstream_id = ? AND t.status = 'IN_PROGRESS' ORDER BY t.updated_at DESC`,\n )\n .all(wsId) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\n/** CLOSED tasks in a workstream, most recently updated first, capped at\n * `limit` (default 5). `updated_at` is a proxy for close time: there is\n * no `closed_at` column, and a later note or edge change on a CLOSED\n * task bumps `updated_at` (touchTask), moving it up. Used by `mu state`\n * for its 'recent closed'\n * slice; exposed as a named SDK helper so the CLI no longer needs the\n * raw-row type that was duplicating RawTaskRow\n * (review_code_raw_task_state_duplicate). */\nexport function listRecentClosed(db: Db, workstream: string, limit = 5): TaskRow[] {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} WHERE t.workstream_id = ? AND t.status = 'CLOSED' ORDER BY t.updated_at DESC LIMIT ?`,\n )\n .all(wsId, limit) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\n/** Optional filter knobs for `listNotes`. Default-everything-undefined\n * preserves the historical \"return every note, oldest-first\" shape so\n * every existing caller (cmdTaskShow's notes block, agents.test.ts)\n * keeps working unchanged.\n *\n * Filters compose multiplicatively when both apply (`since` AND\n * `tail`): the timestamp filter is applied first, then `tail` slices\n * the last N of what survived. The CLI surface (`mu task notes\n * --tail / --since / --since-claim`) lives in src/cli/tasks/edit.ts;\n * the mutex between `--since` and `--since-claim` is a CLI concern,\n * not enforced here — if both arrive at the SDK, `since` wins (it's\n * the explicit one) and `sinceClaim` is ignored. The auto-resolve\n * for `sinceClaim` (look up the most recent `task claim` event in\n * agent_logs) happens here so the SDK is self-contained for scripted\n * callers. */\nexport interface ListNotesOptions {\n /** Print only the last N notes (after any timestamp filter). Must\n * be a positive integer; a value of 0 returns no rows but is not\n * an error here — CLI-side validation rejects `--tail 0`. */\n tail?: number;\n /** ISO-8601 cutoff: only notes with `created_at > since` survive.\n * Any `Date.parse`-able value; it is normalised to `toISOString()`\n * form (the stored shape) before the text comparison, so offsets\n * and second-precision cutoffs compare as instants. */\n since?: string;\n /** When true and `since` is unset, look up the `created_at` of the\n * most recent `task claim` event for this task and use it as the\n * cutoff. Falls back to no filter when no claim event exists\n * (equivalent to `--since-beginning`). */\n sinceClaim?: boolean;\n}\n\n/** List notes for a task. Operator-facing local_id; resolves to the\n * surrogate task id via taskIdFor (with optional workstream scope).\n *\n * Optional filters: see {@link ListNotesOptions}. Default behaviour\n * (no opts) is unchanged — every note, oldest-first. */\nexport function listNotes(\n db: Db,\n taskLocalId: string,\n workstream: string,\n opts: ListNotesOptions = {},\n): TaskNoteRow[] {\n const taskId = taskIdFor(db, taskLocalId, workstream);\n if (taskId === null) return [];\n // Resolve the cutoff once: explicit `since` wins; otherwise\n // `sinceClaim` resolves via lastClaimEventAt (null → no filter).\n // created_at is always `toISOString()` (UTC, millis, `Z`) and the\n // filter compares text, so normalise `since` to that same shape: a\n // raw `10:07:00Z` sorts after `10:07:00.411Z` ('Z' > '.') and an\n // offset like `+02:00` compares as local time. An unparseable value\n // passes through unchanged (the CLI rejects it before here).\n let cutoff: string | undefined = opts.since;\n if (cutoff !== undefined) {\n const ms = Date.parse(cutoff);\n if (!Number.isNaN(ms)) cutoff = new Date(ms).toISOString();\n }\n if (cutoff === undefined && opts.sinceClaim === true) {\n const at = lastClaimEventAt(db, workstream, taskLocalId);\n if (at !== null) cutoff = at;\n }\n const rows =\n cutoff !== undefined\n ? (db\n .prepare(\n `SELECT ${SELECT_NOTE_COLS} FROM task_notes n JOIN tasks t ON t.id = n.task_id\n WHERE n.task_id = ? AND n.created_at > ? ORDER BY n.id`,\n )\n .all(taskId, cutoff) as RawTaskNoteRow[])\n : (db\n .prepare(\n `SELECT ${SELECT_NOTE_COLS} FROM task_notes n JOIN tasks t ON t.id = n.task_id\n WHERE n.task_id = ? ORDER BY n.id`,\n )\n .all(taskId) as RawTaskNoteRow[]);\n const mapped = rows.map(noteFromDb);\n if (opts.tail !== undefined && opts.tail >= 0) {\n return opts.tail === 0 ? [] : mapped.slice(-opts.tail);\n }\n return mapped;\n}\n\n/**\n * All tasks currently owned by `agent` in a given workstream\n * (v5: agents.name is per-workstream unique). Sorted by local_id.\n *\n * Defaults to **excluding CLOSED** since the verb's purpose is \"what\n * is X currently working on?\" and a closed task is no longer being\n * worked on. closeTask intentionally preserves `owner` as a\n * historical record (so audit/notes can attribute decisions); pass\n * `{ includeClosed: true }` to surface that history.\n */\nexport function listTasksByOwner(\n db: Db,\n workstream: string,\n owner: string,\n opts: { includeClosed?: boolean } = {},\n): TaskRow[] {\n // 'Live work' = not CLOSED. includeClosed re-includes CLOSED tasks\n // so historical attribution is recoverable.\n // Filter on the joined ag.name so the operator-facing owner string\n // still drives the lookup; FK is now via owner_id.\n const filter = opts.includeClosed ? \"\" : \"AND t.status <> 'CLOSED'\";\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const sql = `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN}\n WHERE ag.name = ? AND t.workstream_id = ? ${filter}\n ORDER BY t.local_id`;\n return (db.prepare(sql).all(owner, wsId) as RawTaskRow[]).map(rowFromDb);\n}\n\n/**\n * Cross-workstream variant of `listTasksByOwner`. Returns tasks owned\n * by ANY agent of the given name across every workstream. Used by\n * `mu task owned-by --all` for the genuine cross-workstream view\n * (audit / dashboards). The bare name is the join key, so two\n * distinct same-named agents in different workstreams contribute\n * their tasks to the same result list.\n */\nexport function listTasksByOwnerCrossWorkstream(\n db: Db,\n owner: string,\n opts: { includeClosed?: boolean } = {},\n): TaskRow[] {\n const filter = opts.includeClosed ? \"\" : \"AND t.status <> 'CLOSED'\";\n const sql = `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN}\n WHERE ag.name = ? ${filter}\n ORDER BY ws.name, t.local_id`;\n return (db.prepare(sql).all(owner) as RawTaskRow[]).map(rowFromDb);\n}\n","// mu — task id validation and title slug helpers.\n\nimport type { Db } from \"../db.js\";\nimport type { HasNextSteps, NextStep } from \"../output.js\";\nimport { getTask } from \"./queries.js\";\n\n/** Thrown when a title has no ASCII letter or digit, so no id can be\n * derived from it (any non-ASCII script, or pure punctuation). Exit 2\n * (usage) with the verb's --help; the fix is the `<id>` positional.\n * Lives here, not in errors.ts, because errors.ts imports this file. */\nexport class TaskTitleSlugEmptyError extends Error implements HasNextSteps {\n override readonly name = \"TaskTitleSlugEmptyError\";\n constructor(public readonly title: string) {\n super(\n `title yields empty slug: ${JSON.stringify(title)} (no ASCII letter or digit to derive an id from)`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Pass the id explicitly as the positional\",\n command: 'mu task add <id> --title \"...\" --impact <n> --effort-days <n>',\n },\n ];\n }\n}\n\n/** Lowercase alpha first, then alnum / underscore / hyphen, ≤64 chars. */\nconst TASK_ID_RE = /^[a-z][a-z0-9_-]{0,63}$/;\n\nexport function isValidTaskId(id: string): boolean {\n return TASK_ID_RE.test(id);\n}\n\n/**\n * Derive a task id from a free-form title.\n *\n * \"Build the auth module\" → \"build_the_auth_module\"\n * \"FILES: foo.ts (refactor)\" → \"files_foo_ts_refactor\"\n */\n\n/**\n * Soft cap for auto-generated slugs. The collision-suffix loop in\n * idFromTitle can push past this (`_2`, `_3`, ...) without going past\n * the hard ceiling. 40 chars hits the sweet spot of 'short enough to\n * type and to look reasonable in mu task tree' without losing too\n * much of the title's meaning.\n */\nconst SLUG_SOFT_CAP = 40;\n\n/**\n * Hard ceiling for any generated id. Schema has no length limit, but\n * 64 keeps ids comfortable in tables, JSON, and tmux pane titles.\n */\nconst SLUG_HARD_CAP = 64;\n\n/**\n * Lowercase title; collapse non-alnum runs into single `_`; trim\n * leading/trailing `_`; prefix `t_` if the result starts with a digit\n * (schema requires first char letter); apply the soft cap with\n * word-boundary trim (cut at the last `_` at-or-before SLUG_SOFT_CAP\n * when one exists, else hard-truncate). Mirrors `tg`'s `id_from_title`\n * but adds the soft cap.\n *\n * Throws `TaskTitleSlugEmptyError` if `title` yields an empty slug.\n */\nexport function slugifyTitle(title: string): string {\n return slugifyTitleVerbose(title).slug;\n}\n\n/**\n * Result of `slugifyTitleVerbose`: the slug plus enough metadata for\n * the CLI to decide whether to warn the user that meaning was lost.\n *\n * slug — the same string `slugifyTitle` returns.\n * strippedLength — length of the post-strip pre-cap slug. When this\n * exceeds the SLUG_SOFT_CAP the verbose form had to\n * cut at a word boundary (or hard-truncate); the\n * cut clauses are gone with no in-band signal.\n * originalSlug — what the slug WOULD have been without the\n * SLUG_SOFT_CAP cut: full stripped slug with the\n * same `t_` digit-prefix correction and the same\n * SLUG_HARD_CAP ceiling, but no word-boundary\n * truncation. Equal to `slug` when nothing was\n * cut. The CLI surfaces this in `mu task add\n * --json` so scripted callers can detect the\n * truncation without grepping stderr.\n * truncated — true iff `slug.length < strippedLength` AFTER the\n * `t_` digit-prefix correction, i.e. real bytes were\n * dropped. False for any title that fits under the\n * soft cap or whose only diff vs the stripped slug\n * is the `t_` prefix.\n *\n * The CLI's `mu task add` uses `truncated` to print a one-line stderr\n * hint pointing at the `<id>` positional override and (under --json)\n * to surface `originalSlug` alongside `truncated:true`\n * (slugifytitle_silently_drops_clauses; task_add_slugify_silently_truncates_ids).\n */\nexport interface SlugifyResult {\n slug: string;\n strippedLength: number;\n originalSlug: string;\n truncated: boolean;\n}\n\n/**\n * Verbose sibling of `slugifyTitle`: returns the slug AND a\n * `truncated` flag so the CLI can hint to the user when the soft cap\n * dropped clauses (the meaning-shift hazard documented in\n * slugifytitle_silently_drops_clauses).\n *\n * Algorithm is byte-for-byte identical to `slugifyTitle`; this just\n * surfaces the metadata that the plain form throws away.\n */\nexport function slugifyTitleVerbose(title: string): SlugifyResult {\n const stripped = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\");\n if (stripped.length === 0) {\n throw new TaskTitleSlugEmptyError(title);\n }\n // Soft cap with word-boundary preference: if the slug exceeds the\n // soft cap, look for the last `_` at-or-before the cap and cut there\n // (so we never break a word). If no underscore is in the cap window\n // (ie the title is one giant word), fall back to a hard truncate at\n // the soft cap. The result is always <= SLUG_SOFT_CAP after this.\n let trimmed: string;\n if (stripped.length <= SLUG_SOFT_CAP) {\n trimmed = stripped;\n } else {\n // lastIndexOf's fromIndex is inclusive, so an `_` AT index\n // SLUG_SOFT_CAP (a word ending exactly at the cap) is found too.\n const lastSep = stripped.lastIndexOf(\"_\", SLUG_SOFT_CAP);\n trimmed = lastSep > 0 ? stripped.slice(0, lastSep) : stripped.slice(0, SLUG_SOFT_CAP);\n }\n // First char must be a letter → prefix `t_` if it isn't. v5 has no\n // global namespace and no reserved prefix; `mu_foo` is a perfectly\n // valid local_id (per-workstream unique).\n const applyPrefix = (s: string): string =>\n /^[a-z]/.test(s) ? s.slice(0, SLUG_HARD_CAP) : `t_${s}`.slice(0, SLUG_HARD_CAP);\n const slug = applyPrefix(trimmed);\n // `originalSlug` is what the slug would have been without the\n // soft-cap word-boundary cut: same prefix correction, same hard\n // cap, no word-boundary trim. Used by `mu task add --json` so\n // scripted callers can detect truncation programmatically.\n const originalSlug = applyPrefix(stripped);\n // `truncated` reflects whether real characters were dropped — the\n // `t_` prefix doesn't count as truncation. We compare the trimmed\n // length against the stripped length (both pre-prefix) so a digit-\n // led title that fit under the cap still reports truncated=false.\n return {\n slug,\n strippedLength: stripped.length,\n originalSlug,\n truncated: trimmed.length < stripped.length,\n };\n}\n\n/**\n * Generate a unique task id from a title. v5: tasks.local_id is\n * per-workstream unique, so the collision check scopes to one\n * workstream. On collision, appends `_2`, `_3`, … until unique.\n */\nexport function idFromTitle(db: Db, workstream: string, title: string): string {\n return idFromTitleVerbose(db, workstream, title).id;\n}\n\n/**\n * Result of `idFromTitleVerbose`: the unique-in-workstream id plus the\n * truncated flag from the underlying slugify pass. Used by `mu task\n * add` to decide whether to surface the stderr hint about lost clauses\n * (slugifytitle_silently_drops_clauses) and to surface the un-truncated\n * slug in `--json` (task_add_slugify_silently_truncates_ids).\n *\n * id — the unique-in-workstream task id.\n * truncated — true iff the underlying slugify pass cut real\n * characters (collision-suffixing does NOT flip\n * this).\n * originalSlug — what the slug would have been without the\n * SLUG_SOFT_CAP cut. Equal to `id` when nothing was\n * cut AND no collision suffix was appended; for\n * the truncation-detection use case the only thing\n * the CLI cares about is the lossy-vs-not\n * comparison surfaced via `truncated`.\n */\nexport interface IdFromTitleResult {\n id: string;\n truncated: boolean;\n originalSlug: string;\n}\n\n/**\n * Verbose sibling of `idFromTitle`: returns the unique id, the\n * `truncated` flag from the slugify pass, and the un-truncated\n * `originalSlug` for `--json` consumers. Collision-suffixing (`_2`,\n * `_3`, …) does not flip `truncated` — the underlying slug's lossiness\n * is what the CLI hint cares about.\n */\nexport function idFromTitleVerbose(db: Db, workstream: string, title: string): IdFromTitleResult {\n const { slug: base, truncated, originalSlug } = slugifyTitleVerbose(title);\n if (getTask(db, base, workstream) === undefined) return { id: base, truncated, originalSlug };\n for (let i = 2; i < 1000; i++) {\n const candidate = `${base}_${i}`.slice(0, SLUG_HARD_CAP);\n if (getTask(db, candidate, workstream) === undefined)\n return { id: candidate, truncated, originalSlug };\n }\n throw new Error(`could not derive a unique id from title in workstream ${workstream}: ${title}`);\n}\n\n/**\n * Sanitise a free-form string into a candidate task id.\n *\n * Lowercases, replaces every non-`[a-z0-9_-]` char with `_`, trims any\n * leading non-letter (the schema requires the first char to be a\n * letter), truncates to 64 chars. Returns `\"task\"` when the input has\n * no usable letters at all so the suggestion in\n * `TaskIdInvalidError.errorNextSteps()` is always a runnable command.\n *\n * Mirrors `slugifyTitle`'s prefix corrections so suggested ids will\n * pass `isValidTaskId` if the user runs them verbatim. Lives next to\n * `slugifyTitle` rather than in `tasks/errors.ts` because it's a slug\n * helper, not an error helper — the only caller happens to be\n * `TaskIdInvalidError.errorNextSteps()`.\n */\nexport function sanitiseTaskId(input: string): string {\n const s = input\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, \"_\")\n .replace(/^[^a-z]+/, \"\")\n .slice(0, SLUG_HARD_CAP);\n return s.length === 0 ? \"task\" : s;\n}\n","// mu — task error classes.\n//\n// Every task verb that can fail in a typed way has its own error class\n// here. The CLI's `classifyError()` (src/cli.ts) maps them to exit codes:\n// not found → 3 (TaskNotFoundError)\n// conflict → 4 (TaskExistsError, TaskNotInWorkstreamError,\n// TaskAlreadyOwnedError, ClaimerNotRegisteredError,\n// CrossWorkstreamEdgeError,\n// SubstateReasonRequiredError, TaskParkStateError,\n// TaskParkedError, TaskInTriageError,\n// InvalidSubstateError)\n// cycle → 4 (CycleError — also a conflict)\n// usage → 2 (TaskIdInvalidError; TaskTitleSlugEmptyError in id.ts)\n//\n// Each error implements HasNextSteps so the CLI can render a per-error\n// `Next:` block with the most useful follow-up commands.\n//\n// Extracted from src/tasks.ts as part of refactor_split_large_src_files.\n\nimport type { HasNextSteps, NextStep } from \"../output.js\";\nimport { WORKSPACE_STALE_THRESHOLD, type WorkspaceStaleness } from \"../workspace.js\";\nimport { sanitiseTaskId } from \"./id.js\";\nimport { TASK_SUBSTATES, type TaskStatus, type TaskSubstate } from \"./status.js\";\n\nexport class TaskNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"TaskNotFoundError\";\n constructor(public readonly taskId: string) {\n super(`no such task: ${taskId}`);\n }\n errorNextSteps(): NextStep[] {\n // v5: tasks.workstream (TEXT) was replaced by tasks.workstream_id\n // (FK → workstreams.id). The pre-v5 recipe SELECTed a column that\n // no longer exists and crashed at runtime — and this is THE first\n // hint a user sees on a missed-task lookup, so the breakage was\n // high-traffic. Mirror the join pattern used by AgentExistsError.\n const idLit = this.taskId.replace(/'/g, \"''\").toLowerCase();\n const recipe = `mu sql \"SELECT ws.name AS workstream, t.local_id, t.status, t.title FROM tasks t JOIN workstreams ws ON ws.id = t.workstream_id WHERE LOWER(t.local_id) LIKE '%${idLit}%' OR LOWER(t.title) LIKE '%${idLit}%'\"`;\n return [\n { intent: \"List tasks in workstream\", command: \"mu task list -w <workstream>\" },\n { intent: \"Search every workstream by substring (id + title)\", command: recipe },\n ];\n }\n}\n\n/**\n * Thrown by `addTask` when `localId` violates the schema regex\n * `/^[a-z][a-z0-9_-]{0,63}$/`. Replaces a bare `TypeError` so the\n * CLI's `handle()` wrapper can map it to exit code 2 (usage, with the\n * verb's --help, like WorkstreamNameInvalidError) and surface a `--json` `nextSteps` block pointing at\n * the auto-derived-id workflow and a sanitised candidate.\n */\nexport class TaskIdInvalidError extends Error implements HasNextSteps {\n override readonly name = \"TaskIdInvalidError\";\n constructor(public readonly attempted: string) {\n super(`invalid task id: ${JSON.stringify(attempted)} (expected /^[a-z][a-z0-9_-]{0,63}$/)`);\n }\n errorNextSteps(): NextStep[] {\n const sanitised = sanitiseTaskId(this.attempted);\n return [\n {\n intent: \"Use the auto-derived id (drop the <id> positional; --title derives it)\",\n command: 'mu task add --title \"...\" --impact <n> --effort-days <n>',\n },\n {\n intent: \"Sanitise to a valid id\",\n command: `mu task add ${sanitised} --title \"...\" --impact <n> --effort-days <n>`,\n },\n ];\n }\n}\n\nexport class TaskExistsError extends Error implements HasNextSteps {\n override readonly name = \"TaskExistsError\";\n constructor(public readonly taskId: string) {\n super(`task already exists: ${taskId}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"Show the existing task\", command: `mu task show ${this.taskId}` },\n {\n intent: \"Update fields on the existing task\",\n command: `mu task update ${this.taskId} --title \"...\" --impact <n> --effort-days <n>`,\n },\n {\n intent: \"Pick a different id\",\n command: 'mu task add <new-id> --title \"...\" --impact <n> --effort-days <n>',\n },\n ];\n }\n}\n\n/**\n * Thrown when a verb is invoked with `-w/--workstream <name>` but the\n * named task lives in a different workstream. Distinguishes \"the user\n * typo'd the workstream\" from \"the task doesn't exist anywhere\"\n * (which surfaces as `TaskNotFoundError`). Maps to exit code 4\n * (conflict / wrong scope).\n */\nexport class TaskNotInWorkstreamError extends Error implements HasNextSteps {\n override readonly name = \"TaskNotInWorkstreamError\";\n constructor(\n public readonly taskId: string,\n public readonly expectedWorkstream: string,\n public readonly actualWorkstream: string,\n ) {\n super(`task ${taskId} is in workstream ${actualWorkstream}, not ${expectedWorkstream}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Use the correct workstream\",\n command: `mu task show ${this.taskId} -w ${this.actualWorkstream}`,\n },\n {\n intent: \"List tasks in the requested workstream\",\n command: `mu task list -w ${this.expectedWorkstream}`,\n },\n ];\n }\n}\n\nexport class TaskAlreadyOwnedError extends Error implements HasNextSteps {\n override readonly name = \"TaskAlreadyOwnedError\";\n constructor(\n public readonly taskId: string,\n public readonly currentOwner: string,\n ) {\n super(`task ${taskId} is already owned by ${currentOwner}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"See the current owner's task list\",\n command: `mu task owned-by ${this.currentOwner}`,\n },\n {\n intent: \"Release the current claim (if you ARE the owner)\",\n command: `mu task release ${this.taskId}`,\n },\n { intent: \"Show full task state\", command: `mu task show ${this.taskId}` },\n ];\n }\n}\n\n/**\n * Thrown when `mu task claim` resolves a claimer agent name (from the\n * pane title or --for) that has no matching row in the agents table.\n *\n * The FK on `tasks.owner_id` references `agents.id`; without this guard\n * the claim attempt would fail with the unhelpful 'FOREIGN KEY constraint\n * failed' from SQLite. This typed error gives the user actionable next\n * steps (run `mu agent adopt <pane-id>` to register, or use --for to pick a\n * different agent).\n *\n * Maps to exit code 4 (conflict) via the cli.ts handler.\n */\nexport class ClaimerNotRegisteredError extends Error implements HasNextSteps {\n override readonly name = \"ClaimerNotRegisteredError\";\n constructor(\n public readonly agentName: string,\n public readonly paneId: string | null,\n ) {\n const paneHint = paneId !== null ? ` (pane ${paneId})` : \"\";\n super(\n `claimer '${agentName}'${paneHint} is not a registered mu agent (no row in agents table)`,\n );\n }\n\n /**\n * Three actionable resolutions in expected-frequency order:\n * 1. --self : orchestrator pattern (working directly)\n * 2. --for : dispatcher pattern (assigning to a worker)\n * 3. mu agent adopt: registration pattern (promote pane to worker)\n */\n errorNextSteps(): NextStep[] {\n const steps: NextStep[] = [\n { intent: \"Work directly (anonymous)\", command: \"mu task claim <id> --self\" },\n { intent: \"Dispatch to a worker\", command: \"mu task claim <id> --for <worker>\" },\n ];\n steps.push(\n this.paneId !== null\n ? { intent: \"Register this pane\", command: `mu agent adopt ${this.paneId}` }\n : {\n intent: \"Register a pane\",\n command: \"mu agent adopt <pane-id> (must be in mu-<workstream> tmux session)\",\n },\n );\n return steps;\n }\n}\n\nexport class TaskClaimStaleWorkspaceError extends Error implements HasNextSteps {\n override readonly name = \"TaskClaimStaleWorkspaceError\";\n constructor(public readonly staleness: WorkspaceStaleness) {\n super(\n `${staleness.agentName} workspace is ${staleness.commitsBehindMain} commits behind main (≥${WORKSPACE_STALE_THRESHOLD} = stale); refresh before dispatch or rerun without --strict-staleness`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Refresh first\",\n command: `mu workspace refresh ${this.staleness.agentName} -w ${this.staleness.workstreamName}`,\n },\n ];\n }\n}\n\nexport class CycleError extends Error implements HasNextSteps {\n override readonly name = \"CycleError\";\n constructor(\n public readonly from: string,\n public readonly to: string,\n ) {\n super(`adding edge ${from} -> ${to} would create a cycle`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Show the dependency tree\",\n command: `mu task tree ${this.to} --down`,\n },\n {\n intent: \"Show the prereq tree (what blocks the from-task)\",\n command: `mu task tree ${this.from}`,\n },\n {\n intent: \"Remove an edge in the path to break the cycle\",\n command: \"mu task unblock <blocked> --by <blocker>\",\n },\n ];\n }\n}\n\n/**\n * Thrown by the `mu task wait` CLI wrapper when the per-poll\n * reconciler detects that a watched task transitioned\n * `IN_PROGRESS → OPEN` between polls (the reaper saw the owner's\n * pane was gone and flipped the task back). With `--status CLOSED`\n * (the default) the wait can never satisfy by progress — the worker\n * is dead — so we abort fast instead of running out the operator's\n * `--timeout`.\n *\n * Maps to exit code 6 (REAPER_DETECTED) via the cli.ts handler. The\n * suppression rule (only fire when target=CLOSED) lives in the\n * caller; this error type is a pure data carrier.\n *\n * Surfaced live by task_wait_reconcile_dead_panes (twice in one\n * v0.3 dispatch wave: tmux restart killed worker panes; `mu task\n * wait --timeout 1800` blocked silently for 25 min instead of\n * failing in seconds).\n */\nexport class ReaperDetectedDuringWaitError extends Error implements HasNextSteps {\n override readonly name = \"ReaperDetectedDuringWaitError\";\n constructor(\n public readonly taskId: string,\n public readonly previousOwner: string | null,\n public readonly workstream: string,\n ) {\n const ownerBit = previousOwner !== null ? `owner=${previousOwner}` : \"owner=<unknown>\";\n super(\n `task ${taskId} was IN_PROGRESS ${ownerBit} until just now; reaper detected dead pane and flipped to OPEN. wait abandoned. Re-dispatch a worker and retry.`,\n );\n }\n errorNextSteps(): NextStep[] {\n const ws = this.workstream;\n return [\n {\n intent: \"Inspect the task's current state\",\n command: `mu task show ${this.taskId} -w ${ws}`,\n },\n {\n intent: \"List live agents in the workstream (post-reap)\",\n command: `mu agent list -w ${ws}`,\n },\n {\n intent: \"Re-dispatch a fresh worker, then re-run the wait\",\n command: `mu agent spawn <name> -w ${ws} && mu task claim ${this.taskId} --for <name> -w ${ws}`,\n },\n ];\n }\n}\n\n/**\n * Thrown by the `mu task wait` CLI wrapper when `--on-stall exit` is\n * in effect and the existing `--stuck-after` predicate fires on a\n * watched task — the task is IN_PROGRESS, owned by a registered\n * agent whose detected status is `needs_input` for `>= stuckAfterMs`.\n *\n * Pairs with `ReaperDetectedDuringWaitError` (exit 6, dead pane).\n * Stall is the AMBIGUOUS sibling: the worker is alive but not\n * progressing — the operator decides whether it's transient (poke +\n * retry) or terminal (release + reopen). Exit code 7 = STALL_DETECTED\n * via classifyError, distinct from 6 so consumer scripts can branch.\n *\n * Carve-out (lives at the call site, not here): only fires when the\n * wait target is CLOSED — same logic as exit-6's reaper-flip\n * suppression. With `--status OPEN`/etc the worker reaching\n * needs_input might BE the success path.\n *\n * Surfaced by task_wait_stall_action_flag (the warn-only behaviour\n * pre-dates this; the typed-throw path is the new escape hatch for\n * unattended orchestrators).\n */\n/**\n * A stalled pi owner's last answer, read exactly: `--after-runs <runs-1>`\n * returns the last settled run's lastText at once. Empty unless the\n * owner's socket reported at least one settled run. The pane read stays\n * next to it: dialogs and crashes show in the pane, not the session.\n */\nexport function stallWaitHint(owner: string, ws: string, runs: number | undefined): NextStep[] {\n if (runs === undefined || runs < 1) return [];\n return [\n {\n intent: \"Read the worker's last answer (its final text, at once)\",\n command: `mu agent wait ${owner} --after-runs ${runs - 1} --json -w ${ws}`,\n },\n ];\n}\n\nexport class StallDetectedDuringWaitError extends Error implements HasNextSteps {\n override readonly name = \"StallDetectedDuringWaitError\";\n constructor(\n public readonly taskName: string,\n public readonly owner: string | null,\n public readonly workstream: string,\n public readonly ageSecs: number,\n /** The owner runs pi (control socket): `mu agent abort` stops its turn. */\n public readonly ownerExpectsCtl = false,\n /** The owner's ctl run count at the stall, when its socket answered. */\n public readonly ownerRuns?: number,\n /** The owner's workstream when it differs from the task's\n * (`task claim --for <ws>/<agent>`). Agent commands use it. */\n public readonly ownerWorkstream: string = workstream,\n ) {\n const ownerBit = owner !== null ? owner : \"<unknown>\";\n super(\n `task ${taskName} owned by ${ownerBit} has been needs_input for ${ageSecs}s; exiting per --on-stall exit. Read the pane to see which it is — finished without closing, waiting on an answer, or sitting at a prompt.`,\n );\n }\n /**\n * Diagnose before acting. `mu agent read` comes FIRST because the\n * predicate that threw cannot tell the three causes of needs_input\n * apart, and the evidence is in the pane rather than the task row.\n * The earlier order led with \"poke the worker\", which is the right\n * move for only one of the three and actively wrong for a worker\n * waiting on an answer — a poke with no answer in it just restates\n * the question.\n */\n errorNextSteps(): NextStep[] {\n const ws = this.workstream;\n const ownerWs = this.ownerWorkstream;\n const ownerBit = this.owner !== null ? this.owner : \"<owner>\";\n return [\n ...stallWaitHint(ownerBit, ownerWs, this.ownerRuns),\n {\n intent: \"Read the worker's pane to see what it is waiting on\",\n command: `mu agent read ${ownerBit} -w ${ownerWs} --lines 60`,\n },\n {\n intent: \"Answer a question, or poke a worker that stalled mid-step\",\n command: `mu agent send ${ownerBit} '<answer-or-retry-instruction>' -w ${ownerWs}`,\n },\n ...(this.ownerExpectsCtl\n ? [\n {\n intent: \"Stuck mid-turn? Stop it through the control socket before kick or close\",\n command: `mu agent abort ${ownerBit} -w ${ownerWs}`,\n },\n ]\n : []),\n {\n intent: \"Close the task if the worker finished but skipped it\",\n command: `mu task close ${this.taskName} -w ${ws}`,\n },\n {\n intent: \"Release the task back to OPEN (declare the stall terminal)\",\n command: `mu task release ${this.taskName} --reopen -w ${ws}`,\n },\n ];\n }\n}\n\nexport class CrossWorkstreamEdgeError extends Error implements HasNextSteps {\n override readonly name = \"CrossWorkstreamEdgeError\";\n constructor(\n public readonly blocker: string,\n public readonly blockerWorkstream: string,\n public readonly dependent: string,\n public readonly dependentWorkstream: string,\n ) {\n super(\n `cross-workstream edge: blocker '${blocker}' is in workstream '${blockerWorkstream}', dependent '${dependent}' is in workstream '${dependentWorkstream}'`,\n );\n }\n errorNextSteps(): NextStep[] {\n // No \"move the blocker\" recipe. A task's workstream is part of its\n // natural key (`<ws>/<local_id>`), which every op is filed under, so\n // a raw `UPDATE tasks SET workstream_id=...` re-keys the row with no\n // op for either key: sync, undo and `mu doctor` then disagree with\n // the table. The capture layer refuses that UPDATE (src/capture.ts\n // KEY_COLUMNS). The \"rename one workstream to the other\" hint went\n // earlier for the same reason, and because it moved every task.\n return [\n {\n intent: \"Duplicate the blocker into the dependent's workstream\",\n command: `mu task add <new-id> -w ${this.dependentWorkstream} --title \"<copy of ${this.blocker}>\" --impact <n> --effort-days <n>`,\n },\n {\n intent: \"Or wait on the blocker across workstreams instead of an edge\",\n command: `mu task wait ${this.blockerWorkstream}/${this.blocker}`,\n },\n ];\n }\n}\n\n/**\n * Thrown by `closeTask` (non-`done` `--as`) and `parkTask` when no\n * non-empty `--why` was given. The reason is stored as a note, so a\n * classification with no rationale is refused before any write.\n */\nexport class SubstateReasonRequiredError extends Error implements HasNextSteps {\n override readonly name = \"SubstateReasonRequiredError\";\n constructor(\n public readonly verb: \"close\" | \"park\",\n public readonly substate: TaskSubstate,\n public readonly taskId: string,\n ) {\n super(\n verb === \"close\"\n ? `closing ${taskId} as ${substate} needs a reason (--why \"...\")`\n : `parking ${taskId} needs a reason (--why \"...\")`,\n );\n }\n errorNextSteps(): NextStep[] {\n const cmd =\n this.verb === \"close\"\n ? `mu task close ${this.taskId} --as ${this.substate} --why \"<reason>\"`\n : `mu task park ${this.taskId} --why \"<reason>\"`;\n return [{ intent: \"Retry with a reason\", command: cmd }];\n }\n}\n\n/**\n * Thrown by `parkTask` on an IN_PROGRESS or CLOSED task. Park is one\n * transition (OPEN/todo → OPEN/parked); the owner releases, or the task\n * is reopened, first.\n */\nexport class TaskParkStateError extends Error implements HasNextSteps {\n override readonly name = \"TaskParkStateError\";\n constructor(\n public readonly taskId: string,\n public readonly status: \"IN_PROGRESS\" | \"CLOSED\",\n public readonly workstream: string,\n ) {\n super(\n status === \"IN_PROGRESS\"\n ? `cannot park ${taskId}: it is IN_PROGRESS (release it first)`\n : `cannot park ${taskId}: it is CLOSED (open it first)`,\n );\n }\n errorNextSteps(): NextStep[] {\n const ws = this.workstream;\n const first: NextStep =\n this.status === \"IN_PROGRESS\"\n ? { intent: \"Release the claim first\", command: `mu task release ${this.taskId} -w ${ws}` }\n : { intent: \"Reopen the task first\", command: `mu task open ${this.taskId} -w ${ws}` };\n return [\n first,\n {\n intent: \"Then park it\",\n command: `mu task park ${this.taskId} --why \"<reason>\" -w ${ws}`,\n },\n ];\n }\n}\n\n/**\n * Thrown by `claimTask` on an OPEN/triage task without `force`. A triage\n * task is a proposal (often a review finding) nobody has accepted as\n * work yet; accept it, or close it rejected / duplicate.\n */\nexport class TaskInTriageError extends Error implements HasNextSteps {\n override readonly name = \"TaskInTriageError\";\n constructor(\n public readonly taskId: string,\n public readonly workstream: string,\n ) {\n super(`task ${taskId} is in triage (OPEN/triage); accept it or claim with --force`);\n }\n errorNextSteps(): NextStep[] {\n const ws = this.workstream;\n return [\n {\n intent: \"Accept it as work, then claim\",\n command: `mu task accept ${this.taskId} -w ${ws}`,\n },\n {\n intent: \"Decline it\",\n command: `mu task close ${this.taskId} --as rejected --why \"<reason>\" -w ${ws}`,\n },\n { intent: \"Claim it anyway\", command: `mu task claim ${this.taskId} --force -w ${ws}` },\n ];\n }\n}\n\n/**\n * Thrown by `claimTask` on an OPEN/parked task without `force`. Parked\n * means \"keep out of the scheduler\"; claiming it anyway is explicit.\n */\nexport class TaskParkedError extends Error implements HasNextSteps {\n override readonly name = \"TaskParkedError\";\n constructor(\n public readonly taskId: string,\n public readonly workstream: string,\n ) {\n super(`task ${taskId} is parked (OPEN/parked); unpark it or claim with --force`);\n }\n errorNextSteps(): NextStep[] {\n const ws = this.workstream;\n return [\n { intent: \"Unpark it, then claim\", command: `mu task unpark ${this.taskId} -w ${ws}` },\n { intent: \"Claim it anyway\", command: `mu task claim ${this.taskId} --force -w ${ws}` },\n ];\n }\n}\n\n/**\n * Thrown when a verb is asked for a (status, substate) pair that the\n * task_substates table does not allow, e.g. `close --as parked`.\n * Caught here rather than by the deferred FK, which would fail only at\n * commit with an opaque constraint message.\n */\nexport class InvalidSubstateError extends Error implements HasNextSteps {\n override readonly name = \"InvalidSubstateError\";\n constructor(\n public readonly status: TaskStatus,\n public readonly substate: string,\n ) {\n super(\n `invalid substate for ${status}: ${JSON.stringify(substate)} (expected one of: ${TASK_SUBSTATES[status].join(\", \")})`,\n );\n }\n errorNextSteps(): NextStep[] {\n return TASK_SUBSTATES[this.status].map((s) => ({\n intent: `Use ${this.status}/${s}`,\n command:\n this.status === \"CLOSED\" ? `mu task close <id> --as ${s}` : `mu task list --substate ${s}`,\n }));\n }\n}\n","// mu — task edge reads and mutations.\n\nimport type { Db } from \"../db.js\";\nimport { withOpContext } from \"../op-context.js\";\nimport { lookupTaskAnyWorkstream, taskIdFor, touchTask } from \"./core.js\";\nimport { CrossWorkstreamEdgeError, CycleError, TaskNotFoundError } from \"./errors.js\";\nimport { getTask } from \"./queries.js\";\nimport type { TaskStatus, TaskSubstate } from \"./status.js\";\n\nexport interface TaskEdges {\n /** Tasks that must close before this one can start (blockers). */\n blockers: string[];\n /** Tasks that this one blocks (dependents). */\n dependents: string[];\n}\n\nexport interface CanonicalBlocker {\n localId: string;\n id: number;\n}\n\nexport function dedupeBlockersById(blockers: readonly CanonicalBlocker[]): CanonicalBlocker[] {\n const seen = new Set<number>();\n const canonical: CanonicalBlocker[] = [];\n for (const blocker of blockers) {\n if (seen.has(blocker.id)) continue;\n seen.add(blocker.id);\n canonical.push(blocker);\n }\n return canonical;\n}\n\nfunction sameNumberSet(left: readonly number[], right: readonly number[]): boolean {\n if (left.length !== right.length) return false;\n const rightSet = new Set(right);\n if (rightSet.size !== right.length) return false;\n return left.every((value) => rightSet.has(value));\n}\n\n/** One end of an edge with the neighbour's current status attached.\n * Used by `mu task show` to group blockers/dependents into\n * \"still gating\" vs \"satisfied\" buckets without making the renderer\n * do a second round-trip to the DB per neighbour. */\nexport interface TaskEdgeWithStatus {\n name: string;\n status: TaskStatus;\n substate: TaskSubstate;\n}\n\nexport interface TaskEdgesWithStatus {\n /** Tasks that must close before this one can start (blockers),\n * carrying each blocker's current status. */\n blockers: TaskEdgeWithStatus[];\n /** Tasks that this one blocks (dependents), carrying each\n * dependent's current status. */\n dependents: TaskEdgeWithStatus[];\n}\n\n/**\n * Direct (one-hop) edges for a task. For transitive prerequisites, use\n * `getPrerequisites()`; this helper is the immediate-neighbour view used\n * by `mu task show`.\n */\nexport function getTaskEdges(db: Db, taskLocalId: string, workstream: string): TaskEdges {\n const taskId = taskIdFor(db, taskLocalId, workstream);\n if (taskId === null) return { blockers: [], dependents: [] };\n const blockers = (\n db\n .prepare(\n `SELECT t.local_id AS id FROM task_edges e\n JOIN tasks t ON t.id = e.from_task_id\n WHERE e.to_task_id = ? ORDER BY t.local_id`,\n )\n .all(taskId) as { id: string }[]\n ).map((r) => r.id);\n const dependents = (\n db\n .prepare(\n `SELECT t.local_id AS id FROM task_edges e\n JOIN tasks t ON t.id = e.to_task_id\n WHERE e.from_task_id = ? ORDER BY t.local_id`,\n )\n .all(taskId) as { id: string }[]\n ).map((r) => r.id);\n return { blockers, dependents };\n}\n\n/**\n * Same one-hop edge view as `getTaskEdges`, but each neighbour is\n * returned as `{ name, status }` so callers can group / colour by\n * status without an N+1 round-trip. Used by `mu task show` to split\n * \"blocked by\" (still-gating) from \"satisfied\" (already-CLOSED)\n * blockers, and the symmetric split on the dependents side\n * (task_show_blocked_by_renders_closed). OPEN and IN_PROGRESS stay in\n * the still-gating bucket; CLOSED is satisfied.\n */\nexport function getTaskEdgesWithStatus(\n db: Db,\n taskLocalId: string,\n workstream: string,\n): TaskEdgesWithStatus {\n const taskId = taskIdFor(db, taskLocalId, workstream);\n if (taskId === null) return { blockers: [], dependents: [] };\n const blockers = db\n .prepare(\n `SELECT t.local_id AS name, t.status AS status, t.substate AS substate FROM task_edges e\n JOIN tasks t ON t.id = e.from_task_id\n WHERE e.to_task_id = ? ORDER BY t.local_id`,\n )\n .all(taskId) as TaskEdgeWithStatus[];\n const dependents = db\n .prepare(\n `SELECT t.local_id AS name, t.status AS status, t.substate AS substate FROM task_edges e\n JOIN tasks t ON t.id = e.to_task_id\n WHERE e.from_task_id = ? ORDER BY t.local_id`,\n )\n .all(taskId) as TaskEdgeWithStatus[];\n return { blockers, dependents };\n}\n\n/**\n * All tasks transitively reachable from `taskId` via reverse-edge\n * traversal (i.e. the set of tasks that block this one), including the\n * task itself.\n */\nexport function getPrerequisites(db: Db, taskLocalId: string, workstream: string): Set<string> {\n const taskId = taskIdFor(db, taskLocalId, workstream);\n if (taskId === null) return new Set([taskLocalId]);\n // Walk reverse edges in surrogate-id space, then translate back to\n // local_id strings. The seed must be the surrogate id; the result\n // includes the seed task itself (callers like the tracks union-find\n // rely on the inclusive set).\n const rows = db\n .prepare(\n `WITH RECURSIVE reach(node) AS (\n SELECT ?\n UNION\n SELECT from_task_id FROM task_edges, reach WHERE to_task_id = reach.node\n )\n SELECT t.local_id AS local_id FROM reach r JOIN tasks t ON t.id = r.node`,\n )\n .all(taskId) as { local_id: string }[];\n return new Set(rows.map((r) => r.local_id));\n}\n\n/**\n * Adding edge `from -> to` creates a cycle iff there's already a path\n * `to -> ... -> from`. SQL recursive CTE expresses this exactly.\n */\nexport function wouldCreateCycle(db: Db, fromId: number, toId: number): boolean {\n if (fromId === toId) return true;\n const result = db\n .prepare(\n `WITH RECURSIVE forward(node) AS (\n SELECT ?\n UNION\n SELECT to_task_id FROM task_edges, forward WHERE from_task_id = forward.node\n )\n SELECT 1 AS hit FROM forward WHERE node = ? LIMIT 1`,\n )\n .get(toId, fromId) as { hit: number } | undefined;\n return result !== undefined;\n}\n\nexport interface BlockEdgeResult {\n /** True iff a row was actually inserted (vs. already present). */\n added: boolean;\n}\n\n/**\n * Add the edge `blocker → blocked` ('blocker blocks blocked').\n * Idempotent (existing edge → `added: false`). Validates:\n *\n * - both tasks exist\n * - same workstream (cross-workstream edges forbidden)\n * - no cycle (the new edge wouldn't form a path blocked → ... → blocker)\n * - blocker ≠ blocked (no self-reference)\n */\nexport function addBlockEdge(\n db: Db,\n workstream: string,\n blocked: string,\n blocker: string,\n): BlockEdgeResult {\n return withOpContext(db, { intent: \"task.block\", group: \"new\" }, () =>\n addBlockEdgeImpl(db, workstream, blocked, blocker),\n );\n}\n\nfunction addBlockEdgeImpl(\n db: Db,\n workstream: string,\n blocked: string,\n blocker: string,\n): BlockEdgeResult {\n if (blocked === blocker) {\n // Surface as a typed CycleError so the CLI maps it to exit 4 (conflict)\n // rather than letting the schema CHECK fire as a generic SQL error.\n throw new CycleError(blocker, blocked);\n }\n const blockedRow = getTask(db, blocked, workstream);\n if (!blockedRow) throw new TaskNotFoundError(blocked);\n // Resolve the blocker in the dependent's workstream first, then\n // globally so a cross-workstream blocker surfaces\n // CrossWorkstreamEdgeError (clearer than TaskNotFoundError). Cycle\n // check + same-workstream guard run after.\n const blockerRow = lookupTaskAnyWorkstream(db, blocker, blockedRow.workstreamName);\n if (!blockerRow) throw new TaskNotFoundError(blocker);\n if (blockedRow.workstreamName !== blockerRow.workstreamName) {\n throw new CrossWorkstreamEdgeError(\n blocker,\n blockerRow.workstreamName,\n blocked,\n blockedRow.workstreamName,\n );\n }\n const blockedId = taskIdFor(db, blocked, blockedRow.workstreamName);\n const blockerId = taskIdFor(db, blocker, blockerRow.workstreamName);\n if (blockedId === null || blockerId === null) throw new TaskNotFoundError(blocked);\n if (wouldCreateCycle(db, blockerId, blockedId)) {\n throw new CycleError(blocker, blocked);\n }\n const now = new Date().toISOString();\n const added = db\n .transaction(() => {\n const result = db\n .prepare(\n \"INSERT OR IGNORE INTO task_edges (from_task_id, to_task_id, created_at) VALUES (?, ?, ?)\",\n )\n .run(blockerId, blockedId, now);\n if (result.changes > 0) {\n // Bump the BLOCKED task — its blocker set changed. The blocker\n // itself is unaffected. Aligned with reparentTask, which also\n // bumps the FROM_TASK side (the task whose blockers shifted).\n touchTask(db, blockedId, now);\n return true;\n }\n return false;\n })\n .immediate();\n // No emitEvent: the task_edges INSERT fired the capture trigger\n // (intent='task.block', key='<ws>/<blocker>-><ws>/<blocked>').\n return { added };\n}\n\nexport interface RemoveBlockEdgeResult {\n /** True iff a row was actually deleted (vs. no such edge). */\n removed: boolean;\n}\n\n/**\n * Remove the edge `blocker → blocked`. Idempotent (no edge →\n * `removed: false`). Does NOT validate task existence — if the\n * edge is gone there's nothing to do, regardless of whether the\n * tasks are gone too.\n */\nexport function removeBlockEdge(\n db: Db,\n workstream: string,\n blocked: string,\n blocker: string,\n): RemoveBlockEdgeResult {\n return withOpContext(db, { intent: \"task.unblock\", group: \"new\" }, () =>\n removeBlockEdgeImpl(db, workstream, blocked, blocker),\n );\n}\n\nfunction removeBlockEdgeImpl(\n db: Db,\n workstream: string,\n blocked: string,\n blocker: string,\n): RemoveBlockEdgeResult {\n const blockedRow = getTask(db, blocked, workstream);\n if (!blockedRow) return { removed: false };\n const blockerRow = getTask(db, blocker, workstream);\n if (!blockerRow) return { removed: false };\n const blockedId = taskIdFor(db, blocked, blockedRow.workstreamName);\n const blockerId = taskIdFor(db, blocker, blockerRow.workstreamName);\n if (blockedId === null || blockerId === null) return { removed: false };\n const removed = db\n .transaction(() => {\n const result = db\n .prepare(\"DELETE FROM task_edges WHERE from_task_id = ? AND to_task_id = ?\")\n .run(blockerId, blockedId);\n if (result.changes > 0) {\n // Bump the BLOCKED task — its blocker set just shrank.\n touchTask(db, blockedId);\n return true;\n }\n return false;\n })\n .immediate();\n if (removed) {\n // No emitEvent: the DELETE fired the capture trigger, writing an\n // edge tombstone with intent='task.unblock'.\n }\n return { removed };\n}\n\nexport interface ReparentTaskResult {\n /** Edges removed (i.e. all incoming `to_task = taskId` edges). */\n removedEdges: number;\n /** Edges added (after duplicate blockers are canonicalised). */\n addedEdges: number;\n}\n\n/**\n * Atomically replace every incoming edge of `taskId` with new ones\n * `blocker[i] → taskId`. Pass an empty `blockers` array to clear all\n * incoming edges (the task becomes ready iff its status allows).\n *\n * Validates ALL new blockers up-front (existence + same workstream +\n * cycle check); if any fails, no DELETE happens — the call is fully\n * atomic via a single transaction.\n *\n * Cycle reasoning: removing the existing incoming edges to `taskId`\n * doesn't change `taskId`'s OUTGOING reachability, so\n * `wouldCreateCycle(db, blocker, taskId)` evaluated against the\n * pre-state gives the right answer for each new edge.\n */\nexport function reparentTask(\n db: Db,\n taskLocalId: string,\n blockers: readonly string[],\n scope: { workstream: string },\n): ReparentTaskResult {\n // One group for the whole reparent: it DELETEs the old blocker edges\n // and INSERTs the new ones, which is one operator action and must\n // undo as one.\n return withOpContext(db, { intent: \"task.reparent\", group: \"new\" }, () =>\n reparentTaskImpl(db, taskLocalId, blockers, scope),\n );\n}\n\nfunction reparentTaskImpl(\n db: Db,\n taskLocalId: string,\n blockers: readonly string[],\n scope: { workstream: string },\n): ReparentTaskResult {\n const task = getTask(db, taskLocalId, scope.workstream);\n if (!task) throw new TaskNotFoundError(taskLocalId);\n const taskSurrogateId = taskIdFor(db, task.name, task.workstreamName);\n if (taskSurrogateId === null) throw new TaskNotFoundError(taskLocalId);\n\n // Resolve every blocker up-front to its surrogate id; do all\n // existence + same-workstream + cycle checks before any DELETE.\n // Look up blockers in the task's workstream first, then across all\n // workstreams so a blocker that exists only in a DIFFERENT workstream surfaces CrossWorkstreamEdgeError (clearer\n // than TaskNotFoundError). Duplicate blockers are an ergonomic input\n // accident from repeat/CSV forms, so silently canonicalise by\n // surrogate id while preserving first-seen order.\n const requestedBlockers: CanonicalBlocker[] = [];\n for (const blockerLocalId of blockers) {\n if (blockerLocalId === taskLocalId) {\n throw new CycleError(blockerLocalId, taskLocalId);\n }\n const blocker = lookupTaskAnyWorkstream(db, blockerLocalId, task.workstreamName);\n if (!blocker) throw new TaskNotFoundError(blockerLocalId);\n if (blocker.workstreamName !== task.workstreamName) {\n throw new CrossWorkstreamEdgeError(\n blockerLocalId,\n blocker.workstreamName,\n taskLocalId,\n task.workstreamName,\n );\n }\n const blockerId = taskIdFor(db, blocker.name, blocker.workstreamName);\n if (blockerId === null) throw new TaskNotFoundError(blockerLocalId);\n if (wouldCreateCycle(db, blockerId, taskSurrogateId)) {\n throw new CycleError(blockerLocalId, taskLocalId);\n }\n requestedBlockers.push({ localId: blockerLocalId, id: blockerId });\n }\n const canonicalBlockers = dedupeBlockersById(requestedBlockers);\n const blockerIds = canonicalBlockers.map((blocker) => blocker.id);\n const currentBlockerIds = (\n db\n .prepare(\"SELECT from_task_id AS id FROM task_edges WHERE to_task_id = ?\")\n .all(taskSurrogateId) as { id: number }[]\n ).map((row) => row.id);\n\n if (sameNumberSet(currentBlockerIds, blockerIds)) {\n return { removedEdges: 0, addedEdges: 0 };\n }\n\n return db\n .transaction(() => {\n const removed = db\n .prepare(\"DELETE FROM task_edges WHERE to_task_id = ?\")\n .run(taskSurrogateId);\n const insertEdge = db.prepare(\n \"INSERT INTO task_edges (from_task_id, to_task_id, created_at) VALUES (?, ?, ?)\",\n );\n const now = new Date().toISOString();\n for (const blockerId of blockerIds) {\n insertEdge.run(blockerId, taskSurrogateId, now);\n }\n // Bump the reparented task itself — its blocker set just changed.\n touchTask(db, taskSurrogateId, now);\n // No emitEvent: every edge DELETE and INSERT in this transaction\n // fired the capture trigger under intent='task.reparent'.\n return { removedEdges: removed.changes, addedEdges: blockerIds.length };\n })\n .immediate();\n}\n","// mu — workstream-level operations.\n//\n// One workstream = one mux session + N agents + M tasks (and their\n// edges/notes) all sharing the workstream column. 0.1.0 ships `mu init`\n// (create the mux session) and `mu workstream teardown` (this module: nuke the\n// mux session and every DB row tagged with the workstream name).\n//\n// `teardownWorkstream` is idempotent on every leg:\n// - mux session already gone → killSession swallows the error\n// - no agents/tasks for this name → DELETE returns zero changes\n// - workstream never existed at all → returns all-zero counts\n//\n// Both summarize and teardown take an optional `muxSession` override so\n// tests (and the rare workstream whose mux session was created with a\n// non-default name) work without env-var gymnastics.\n\nimport { existsSync, readdirSync, rmdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { unlinkCtlSocket } from \"./ctl/path.js\";\nimport { type Db, defaultStateDir } from \"./db.js\";\nimport { intentSpellings } from \"./legacy-ops.js\";\nimport { activeMux } from \"./mux.js\";\nimport { withOpContext } from \"./op-context.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\nimport { backendByName, type VcsBackend, type VcsBackendName } from \"./vcs.js\";\nimport { listWorkspaces } from \"./workspace.js\";\n\n/**\n * Allowed workstream-name shape: lowercase alpha first, then alnum,\n * underscore, or hyphen, up to 32 chars total. Mirrors the agent-name\n * rule in docs/reference/naming.md.\n *\n * Critically, this rule excludes `.` and `:` — tmux silently rewrites\n * `.` to `_` in session names (because `.` is the window/pane separator\n * in tmux's `session:window.pane` target syntax) and `:` is reserved\n * outright. A workstream name with `.` would create a session that mu\n * couldn't subsequently look up, breaking every downstream verb. We\n * fail loud at init time instead.\n */\nconst WORKSTREAM_NAME_RE = /^[a-z][a-z0-9_-]{0,31}$/;\n\n/** Reserved prefix — mu auto-prepends `mu-` to derive the tmux session\n * name (so workstream `auth` lives in tmux session `mu-auth`). A\n * workstream named `mu-auth` would produce session `mu-mu-auth`,\n * which the user almost certainly didn't intend. Fail loud rather\n * than silently double-prefix. */\nexport const RESERVED_WORKSTREAM_PREFIX = \"mu-\";\n\n/** Reserved workstream names that may only be auto-created on spawn,\n * never via `mu workstream init`. `scratch` is the off-the-cuff bucket\n * for ad-hoc agents you'll keep talking to without crew/DAG ceremony;\n * it auto-creates on first `mu agent spawn <name> -w scratch` (via\n * `ensureWorkstream`, which stays permissive) but `init` is rejected\n * loud so the operator doesn't treat it as a durable crew workstream.\n * See docs/VOCABULARY.md \"scratch workstream\". */\nexport const RESERVED_WORKSTREAM_NAMES = new Set([\"scratch\"]);\n\n// ─── Best-effort mux reads ─────────────────────────────────────────\n//\n// Every workstream READ decorates DB truth with mux liveness. The DB\n// half always works, so an unreachable mux must degrade the decoration\n// (\"no sessions\", \"not alive\") rather than fail `mu workstream list`\n// on a box where nobody is running a multiplexer right now.\n\nasync function listMuxSessions(): Promise<readonly { name: string }[]> {\n try {\n return await (await activeMux()).listSessions();\n } catch {\n return [];\n }\n}\n\nasync function sessionAlive(session: string): Promise<boolean> {\n try {\n return await (await activeMux()).sessionExists(session);\n } catch {\n return false;\n }\n}\n\n/** True iff `name` is a scratch/ephemeral workstream (special-cased by\n * the staleness nudge, the TUI ephemeral marker, the kept-alive\n * session on spawn, and the `teardown --empty` exemption). */\nexport function isScratchWorkstream(name: string): boolean {\n return RESERVED_WORKSTREAM_NAMES.has(name);\n}\n\n/** Name of the placeholder shell window every `mu-<ws>` session keeps,\n * so the session outlives its last agent pane. */\nexport const MU_PLACEHOLDER_WINDOW = \"_mu\";\n\n/**\n * Make sure mux session `session` exists and holds the placeholder\n * `_mu` window: create the session with it when missing, and add the\n * window when the session exists without it (the operator killed it,\n * or the session predates it). Load-bearing: mux errors propagate.\n *\n * Used by `mu workstream init` and by the first spawn into `scratch`\n * (never init-ed). Without `_mu`, closing the last agent kills the\n * last window and tmux destroys the session.\n */\nexport async function ensureWorkstreamSession(\n session: string,\n): Promise<{ created: boolean; muWindowRepaired: boolean }> {\n const mux = await activeMux();\n if (!(await mux.sessionExists(session))) {\n await mux.newSession(session, { detached: true, windowName: MU_PLACEHOLDER_WINDOW });\n return { created: true, muWindowRepaired: false };\n }\n const windows = await mux.listWindows(session).catch(() => []);\n if (windows.some((w) => w.name === MU_PLACEHOLDER_WINDOW)) {\n return { created: false, muWindowRepaired: false };\n }\n await mux.newWindow({\n session,\n name: MU_PLACEHOLDER_WINDOW,\n // A backend with no create-and-run form rejects a command and\n // starts a shell anyway (see createOrReusePane in agents/spawn.ts).\n command: mux.startAgentInPane === undefined ? (process.env.SHELL ?? \"/bin/sh\") : \"\",\n detached: true,\n });\n return { created: false, muWindowRepaired: true };\n}\n\n/**\n * The workstream implied by the mux session the caller sits in, or\n * null. Best-effort: this is one rung of the -w resolution ladder, so\n * an unreachable mux means \"no ambient answer\", never a thrown verb.\n */\nexport async function resolveMuxSessionWorkstreamName(): Promise<string | null> {\n try {\n const name = await (await activeMux()).currentSessionName();\n if (name?.startsWith(RESERVED_WORKSTREAM_PREFIX)) {\n return name.slice(RESERVED_WORKSTREAM_PREFIX.length);\n }\n } catch {\n // fall through: mux context is best-effort for workstream resolution\n }\n return null;\n}\n\nexport function isValidWorkstreamName(name: string): boolean {\n if (!WORKSTREAM_NAME_RE.test(name)) return false;\n if (name.startsWith(RESERVED_WORKSTREAM_PREFIX)) return false;\n return true;\n}\n\n/** A workstream name collision. Exported SDK error class mapped to exit\n * 4 (conflict) by `classifyError`; no mu verb throws it today\n * (`ensureWorkstream` is idempotent). Invalid names throw\n * `WorkstreamNameInvalidError`. */\nexport class WorkstreamExistsError extends Error implements HasNextSteps {\n override readonly name: string = \"WorkstreamExistsError\";\n constructor(public readonly workstream: string) {\n super(`workstream already exists: ${workstream}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List existing workstreams\", command: \"mu workstream list\" },\n {\n intent: \"Tear down the existing workstream first\",\n command: `mu workstream teardown -w ${this.workstream} --yes`,\n },\n ];\n }\n}\n\nexport class WorkstreamNameInvalidError extends Error implements HasNextSteps {\n override readonly name = \"WorkstreamNameInvalidError\";\n constructor(public readonly attempted: string) {\n const reason = attempted.startsWith(RESERVED_WORKSTREAM_PREFIX)\n ? `the 'mu-' prefix is reserved (mu auto-prepends 'mu-' to derive the tmux session name; '${attempted}' would produce session 'mu-${attempted}', which is double-prefixed and almost never what you want). Drop the 'mu-' from the workstream name.`\n : `must match /^[a-z][a-z0-9_-]{0,31}$/. tmux silently rewrites '.' to '_' and reserves ':' as a target separator, so workstream names containing those characters would create tmux sessions mu couldn't look up afterwards. Use letters, digits, '_', and '-' only.`;\n super(`invalid workstream name ${JSON.stringify(attempted)}: ${reason}`);\n }\n errorNextSteps(): NextStep[] {\n // Suggest a sanitized form: lowercase; strip the mu- prefix; map\n // every char outside [a-z0-9_-] (dots, colons, slashes, spaces) to\n // '_'; drop leading non-letters; cap at 32. A result that still\n // fails validation becomes the `<name>` placeholder, so the hint\n // never suggests a command that fails the same check.\n const candidate = this.attempted\n .toLowerCase()\n .replace(/^mu-/, \"\")\n .replace(/[^a-z0-9_-]/g, \"_\")\n .replace(/^[^a-z]+/, \"\")\n .slice(0, 32);\n const sanitized = isValidWorkstreamName(candidate) ? candidate : \"<name>\";\n // Branch the intent label on the failure class. For the mu-prefix\n // case the correction is unambiguous (drop the prefix), so phrase\n // the next-step as a direct action — \"Try a … (best guess)\" reads\n // as a hedge and dogfooding showed agents skip past the rationale\n // line entirely (workstream_init_name_rejected_mu in feedback ws).\n // For the regex/mangle branch the sanitiser really is guessing\n // (`.`/`:`/case all collapse), so the hedge stays honest there.\n const isPrefixCase = this.attempted.toLowerCase().startsWith(RESERVED_WORKSTREAM_PREFIX);\n const intent = isPrefixCase\n ? \"Retry without the 'mu-' prefix\"\n : \"Try a sanitized name (best guess)\";\n return [\n { intent, command: `mu workstream init ${sanitized}` },\n { intent: \"List existing workstreams\", command: \"mu workstream list\" },\n ];\n }\n}\n\nfunction assertValidWorkstreamName(name: string): void {\n if (!isValidWorkstreamName(name)) throw new WorkstreamNameInvalidError(name);\n}\n\n/** Thrown by `mu workstream init` when the operator tries to create a\n * reserved workstream (e.g. `scratch`) explicitly. The name is not\n * invalid — it auto-creates on spawn — it just can't be `init`ed. */\nexport class WorkstreamNameReservedError extends Error implements HasNextSteps {\n override readonly name = \"WorkstreamNameReservedError\";\n constructor(public readonly attempted: string) {\n super(\n `workstream name ${JSON.stringify(attempted)} is reserved: it is the off-the-cuff bucket and auto-creates on first spawn. Don't 'init' it.`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Just spawn into it (auto-creates)\",\n command: `mu agent spawn <name> -w ${this.attempted}`,\n },\n { intent: \"Use a durable workstream instead\", command: \"mu workstream init <name>\" },\n ];\n }\n}\n\n/** Reject reserved names for the explicit `mu workstream init` path.\n * `ensureWorkstream` (the auto-create-on-spawn path) deliberately does\n * NOT call this — spawning into `scratch` must Just Work. */\nexport function assertWorkstreamInitable(name: string): void {\n if (RESERVED_WORKSTREAM_NAMES.has(name)) throw new WorkstreamNameReservedError(name);\n}\n\n/**\n * Ensure a row exists in the `workstreams` table for `name`. Idempotent;\n * INSERT OR IGNORE so concurrent callers race safely. Called by\n * `insertAgent` and `addTask` so callers don't need to remember to call\n * `mu init` before adding a task / spawning an agent (preserves the\n * spawn-without-init ergonomics now that agents.workstream and\n * tasks.workstream are real FKs into this table).\n *\n * Validates the name before inserting; throws `WorkstreamNameInvalidError`\n * for names tmux would silently mangle (containing '.' or ':') or that\n * exceed 32 chars / start with a non-letter.\n *\n * Returns true iff a row was actually inserted (vs. already present).\n */\nexport function ensureWorkstream(db: Db, name: string): boolean {\n assertValidWorkstreamName(name);\n return withOpContext(db, { intent: \"workstream.init\", group: \"new\" }, () =>\n ensureWorkstreamImpl(db, name),\n );\n}\n\nfunction ensureWorkstreamImpl(db: Db, name: string): boolean {\n const result = db\n .prepare(\"INSERT OR IGNORE INTO workstreams (name, created_at) VALUES (?, ?)\")\n .run(name, new Date().toISOString());\n const created = result.changes > 0;\n // No emitEvent: the workstreams INSERT fired the capture trigger\n // (intent='workstream.init', key=<name>).\n return created;\n}\n\nexport interface WorkstreamSummary {\n /** The workstream's own name. */\n name: string;\n /** Mux session name, defaults to `mu-<name>`. */\n muxSession: string;\n /** True iff the mux session `<muxSession>` is alive right now. */\n muxAlive: boolean;\n /** Rows in `agents` for this workstream. */\n agentCount: number;\n /** Rows in `tasks` for this workstream. */\n taskCount: number;\n /** Rows in `task_notes` whose task is in this workstream. */\n noteCount: number;\n /** Rows in `task_edges` whose `from_task` is in this workstream. */\n edgeCount: number;\n /** Rows in `vcs_workspaces` for this workstream. Surfaced so the\n * teardown dry-run can warn about per-agent worktrees that need\n * cleanup before the FK cascade silently nukes their rows. */\n workspaceCount: number;\n /** True iff a row exists in the `workstreams` table itself. False\n * for tmux-only `mu-*` sessions that mu never observed via\n * `mu workstream init`. Surfaced so teardown can clean up bare\n * registry rows (workstream row exists, no agents/tasks/etc.) —\n * otherwise such rows are orphaned forever (the previous\n * `nothingToDo` heuristic short-circuited on them). */\n registered: boolean;\n}\n\nexport interface TeardownResult {\n /** True iff killing the mux session actually killed something. */\n killedMux: boolean;\n /** Number of `agents` rows deleted. */\n deletedAgents: number;\n /** Number of `tasks` rows deleted (edges/notes cascade via FK). */\n deletedTasks: number;\n /** Number of `task_notes` deleted by the cascade — informational. */\n deletedNotes: number;\n /** Number of `task_edges` deleted by the cascade — informational. */\n deletedEdges: number;\n /** Number of vcs_workspaces whose on-disk path was actually\n * removed by the backend on this teardown. Excludes\n * `alreadyGoneWorkspaces` (those were no-ops on disk). */\n freedWorkspaces: number;\n /** Number of vcs_workspaces whose registry row existed but\n * whose on-disk path was already gone (manual rm -rf or a prior\n * interrupted teardown). The DB row was cascade-deleted; the\n * backend did no filesystem work. Tracked separately so the\n * teardown report doesn't lie about how much cleanup it actually\n * performed. */\n alreadyGoneWorkspaces: number;\n /** Workspaces whose backend cleanup failed (e.g. `git worktree\n * remove` refused because of uncommitted changes). The DB row\n * was still cascade-deleted; the on-disk path remains and needs\n * manual cleanup. */\n failedWorkspaces: WorkspaceFailure[];\n}\n\nexport interface WorkspaceFailure {\n agent: string;\n backend: string;\n path: string;\n error: string;\n}\n\nexport interface WorkstreamOptions {\n workstream: string;\n /** Override the mux session name. Defaults to `mu-<workstream>`. */\n muxSession?: string;\n /** Override the per-name VcsBackend resolver. Defaults to\n * `backendByName`. Lets tests inject a fake backend (e.g. one whose\n * `freeWorkspace` throws) without mutating the exported singletons —\n * same pattern as `createWorkspace`'s `opts.backend` accepting a\n * pre-built `VcsBackend` object. Production callers leave this\n * unset. */\n resolveBackend?: (name: VcsBackendName) => VcsBackend;\n}\n\nexport interface TeardownWorkstreamOptions extends WorkstreamOptions {}\n\n/** One past **teardown**, reconstructed from the ops log. */\nexport interface TornDownWorkstream {\n /** The workstream's name. Not unique across the list: a name can be\n * torn down, recreated and torn down again, and each is its own\n * entry (the group id is the identity). */\n name: string;\n /** The op group to pass to `mu undo`. */\n group: string;\n /** ISO timestamp of the teardown. */\n at: string;\n /** Rows the teardown removed, so the entry conveys what is at stake\n * without a second query per row. */\n tasks: number;\n notes: number;\n edges: number;\n /** True when a LATER put recreated this workstream, i.e. the\n * teardown was already undone or the name was reused. Kept in the\n * list rather than filtered out: \"I tore this down, then undid it\"\n * is history the operator may be looking for, and silently hiding\n * it would make the list disagree with `mu log`. */\n recreated: boolean;\n}\n\n/**\n * Every **teardown** the ops log remembers, newest first.\n *\n * The list exists because a teardown is REVERSIBLE and that is useless\n * if the group id cannot be found: `mu undo` lists only recent groups,\n * so a teardown from last month was effectively unreachable without\n * hand-written SQL over `ops`.\n *\n * Read straight from the log rather than from a side table — the log\n * already holds it, and a projection would be one more thing that can\n * disagree with the log (see the `provenance` reasoning in\n * docs/VOCABULARY.md).\n *\n * Accepts both intent spellings, so teardowns predating the 1.2.0\n * rename are included.\n */\nexport function listTornDownWorkstreams(db: Db): TornDownWorkstream[] {\n const spellings = intentSpellings(\"workstream.teardown\");\n const placeholders = spellings.map(() => \"?\").join(\", \");\n return db\n .prepare(\n `SELECT o.key AS name,\n o.group_id AS \"group\",\n o.created_at AS at,\n (SELECT COUNT(*) FROM ops t\n WHERE t.group_id = o.group_id AND t.entity = 'task' AND t.op = 'del') AS tasks,\n (SELECT COUNT(*) FROM ops n\n WHERE n.group_id = o.group_id AND n.entity = 'note' AND n.op = 'del') AS notes,\n (SELECT COUNT(*) FROM ops e\n WHERE e.group_id = o.group_id AND e.entity = 'edge' AND e.op = 'del') AS edges,\n EXISTS (SELECT 1 FROM ops p\n WHERE p.entity = 'workstream' AND p.key = o.key\n AND p.op = 'put' AND p.hlc > o.hlc) AS recreated\n FROM ops o\n WHERE o.entity = 'workstream'\n AND o.op = 'del'\n AND o.intent IN (${placeholders})\n ORDER BY o.created_at DESC, o.hlc DESC`,\n )\n .all(...spellings)\n .map((r) => {\n const row = r as Omit<TornDownWorkstream, \"recreated\"> & { recreated: number };\n return { ...row, recreated: row.recreated === 1 };\n });\n}\n\n/**\n * Discover every workstream visible on this machine. The union of:\n * - rows in the `workstreams` table (canonical DB source; populated by\n * `mu init` and auto-created by insertAgent / addTask)\n * - mux sessions named `mu-*` (with the prefix stripped) — catches\n * externally-created sessions (e.g. `tmux new-session -s mu-foo`)\n * that mu hasn't observed yet\n *\n * Returns one `WorkstreamSummary` per workstream, sorted by name.\n * Useful as a pre-flight before `mu init` (\"is this name taken?\") and\n * for `mu doctor`-style diagnostics.\n */\nexport async function listWorkstreams(db: Db): Promise<WorkstreamSummary[]> {\n const dbNames = new Set<string>(\n (db.prepare(\"SELECT name FROM workstreams\").all() as { name: string }[]).map((r) => r.name),\n );\n\n // Best-effort: the DB half of the union is always answerable, so a\n // missing mux degrades to \"registered workstreams only\" rather than\n // failing a read-only listing.\n const muxNames = new Set<string>();\n for (const session of await listMuxSessions()) {\n if (session.name.startsWith(RESERVED_WORKSTREAM_PREFIX))\n muxNames.add(session.name.slice(RESERVED_WORKSTREAM_PREFIX.length));\n }\n\n const allNames = Array.from(new Set([...dbNames, ...muxNames])).sort();\n return Promise.all(allNames.map((name) => summarizeWorkstream(db, { workstream: name })));\n}\n\n/**\n * Discover every REGISTERED workstream that has no user-meaningful\n * state attached: a row in `workstreams` with zero tasks, zero\n * agents, zero vcs_workspaces. Mux session presence and agent_logs\n * entries do NOT disqualify — the session itself was created at\n * init time and contains no agent panes; the events are audit, not\n * state.\n *\n * Unregistered `mu-*` mux sessions (no `workstreams` row) are NEVER\n * returned. \"No DB row\" only means THIS DB does not know the\n * session: a run against a throwaway `MU_DB_PATH` sees every real\n * workstream as unregistered, and sweeping them killed a live crew's\n * agent panes. Nothing the mux reports proves such a session idle\n * either: `pane_current_command` is `bash` for a `bash -c` script or\n * loop, and herdr reports no command at all. See\n * listUnregisteredMuxWorkstreams for the names the sweep leaves to an\n * explicit `mu workstream teardown <name> --yes`.\n *\n * The `scratch` workstream is NEVER returned: its idle session is kept\n * alive on purpose (see ensureWorkstreamSession), so the next delegate\n * does not pay for creating it. `mu workstream teardown scratch --yes`\n * still removes it.\n *\n * Used by `mu workstream teardown --empty` to sweep empty\n * workstreams in one command (instead of the per-name jq incantation\n * over `mu workstream list --json`). Returns one `WorkstreamSummary`\n * per match, sorted by name.\n */\nexport async function listEmptyWorkstreams(db: Db): Promise<WorkstreamSummary[]> {\n const registeredRows = db\n .prepare(\n `SELECT ws.name AS name\n FROM workstreams ws\n LEFT JOIN tasks t ON t.workstream_id = ws.id\n LEFT JOIN agents a ON a.workstream_id = ws.id\n LEFT JOIN vcs_workspaces v ON v.workstream_id = ws.id\n GROUP BY ws.id, ws.name\n HAVING COUNT(DISTINCT t.id) = 0\n AND COUNT(DISTINCT a.id) = 0\n AND COUNT(DISTINCT v.id) = 0\n ORDER BY ws.name`,\n )\n .all() as { name: string }[];\n return Promise.all(\n registeredRows\n .filter((r) => !isScratchWorkstream(r.name))\n .map((r) => summarizeWorkstream(db, { workstream: r.name })),\n );\n}\n\n/** Workstream names of live `mu-*` mux sessions with no row in\n * `workstreams`, sorted. `teardown --empty` never sweeps these (see\n * listEmptyWorkstreams); it lists them so the operator can tear one\n * down by name. An unreachable mux yields []. */\nexport async function listUnregisteredMuxWorkstreams(db: Db): Promise<string[]> {\n const dbNames = new Set<string>(\n (db.prepare(\"SELECT name FROM workstreams\").all() as { name: string }[]).map((r) => r.name),\n );\n const names: string[] = [];\n for (const session of await listMuxSessions()) {\n if (!session.name.startsWith(RESERVED_WORKSTREAM_PREFIX)) continue;\n const name = session.name.slice(RESERVED_WORKSTREAM_PREFIX.length);\n if (!dbNames.has(name)) names.push(name);\n }\n return names.sort();\n}\n\nexport async function summarizeWorkstream(\n db: Db,\n opts: WorkstreamOptions,\n): Promise<WorkstreamSummary> {\n const muxSession = opts.muxSession ?? `mu-${opts.workstream}`;\n return {\n name: opts.workstream,\n muxSession,\n muxAlive: await sessionAlive(muxSession),\n agentCount: countAgents(db, opts.workstream),\n taskCount: countTasks(db, opts.workstream),\n noteCount: countNotes(db, opts.workstream),\n edgeCount: countEdges(db, opts.workstream),\n workspaceCount: listWorkspaces(db, opts.workstream).length,\n registered: isRegistered(db, opts.workstream),\n };\n}\n\nfunction isRegistered(db: Db, workstream: string): boolean {\n const row = db.prepare(\"SELECT 1 AS x FROM workstreams WHERE name = ?\").get(workstream) as\n | { x: number }\n | undefined;\n return row !== undefined;\n}\n\n/**\n * Tear down a workstream: kill its mux session and delete every DB row\n * tagged with its name. Cascades on `tasks` clean up `task_edges` and\n * `task_notes` automatically (FK ON DELETE CASCADE in the schema).\n *\n * Idempotent: safe to call against a workstream that never existed; safe\n * to call repeatedly. Returns counts so the caller can print a useful\n * summary.\n */\nexport async function teardownWorkstream(\n db: Db,\n opts: TeardownWorkstreamOptions,\n): Promise<TeardownResult> {\n const muxSession = opts.muxSession ?? `mu-${opts.workstream}`;\n\n // Teardown does not snapshot. v9 dropped the `snapshots` table; the\n // teardown writes tombstone ops instead and `mu undo` replays the\n // inverses (VISION.md § 2b).\n\n // Pre-count the cascade victims so we can report them — SQLite's\n // changes() only reports rows directly affected by the last statement,\n // not cascade victims.\n const agentsBefore = countAgents(db, opts.workstream);\n const tasksBefore = countTasks(db, opts.workstream);\n const notesBefore = countNotes(db, opts.workstream);\n const edgesBefore = countEdges(db, opts.workstream);\n const workspacesBefore = listWorkspaces(db, opts.workstream);\n const agentNames = listAgentNames(db, opts.workstream);\n\n // Mux session first: if killSession throws we don't want the DB rows\n // already gone with no way to recover. (killSession is itself\n // idempotent on missing sessions — a real throw here is an\n // unexpected mux error.) Load-bearing: teardown must actually kill\n // the session, not silently report success while leaving panes\n // running.\n const mux = await activeMux();\n const muxAliveBefore = await mux.sessionExists(muxSession);\n if (muxAliveBefore) {\n await mux.killSession(muxSession);\n }\n\n // Workspaces SECOND, before the FK cascade. The cascade silently\n // deletes vcs_workspaces rows but leaves the on-disk worktrees\n // (and the git worktree registry entries) behind — the bug from\n // mufeedback note #195. Per backend, the right cleanup is\n // 'git worktree remove --force' / 'jj workspace forget' / etc.,\n // not 'rm -rf'. We surface failures so the user can recover; we\n // do NOT abort the teardown on workspace failure (the workstream\n // semantics are 'tear it all down', not 'partial cleanup').\n let freedWorkspaces = 0;\n let alreadyGoneWorkspaces = 0;\n const failedWorkspaces: WorkspaceFailure[] = [];\n const resolveBackend = opts.resolveBackend ?? backendByName;\n for (const ws of workspacesBefore) {\n try {\n const backend = resolveBackend(ws.backend);\n const result = await backend.freeWorkspace({\n workspacePath: ws.path,\n commit: false,\n });\n if (result.removed) {\n // Backend actually removed the on-disk path. This is the\n // only case that counts as 'work done by teardown'.\n freedWorkspaces += 1;\n } else {\n // Path was already gone (manual rm -rf or interrupted prior\n // teardown). The DB row is cascade-deleted below either way,\n // but we don't claim to have freed anything on disk — it was\n // already in the desired state. Tracked separately so the\n // user can spot stale registry rows from past mishaps.\n alreadyGoneWorkspaces += 1;\n }\n } catch (err) {\n failedWorkspaces.push({\n agent: ws.agentName,\n backend: ws.backend,\n path: ws.path,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n // After every per-agent worktree is freed, the parent\n // <state>/workspaces/<workstream>/ directory is empty — reap it\n // too. Best-effort: rmdir refuses if non-empty (e.g. backend\n // removal failed and left files behind), which is the right\n // outcome (don't silently rm -rf user data). Skipped if the\n // parent doesn't exist (workstream never had any workspaces).\n const parentDir = join(defaultStateDir(), \"workspaces\", opts.workstream);\n if (existsSync(parentDir)) {\n try {\n if (readdirSync(parentDir).length === 0) rmdirSync(parentDir);\n } catch {\n // Non-empty or otherwise unreapable. The failed-workspaces\n // list above already tells the user what to clean.\n }\n }\n\n // The cascade skips deleteAgent, so unlink each agent's control\n // socket here (ssh -L forwards leave theirs behind). Do it BEFORE the\n // DELETE: the agent rows are how a retry finds the sockets, so an\n // interrupted unlink must leave them in place. Then reap\n // sock/<ws>/ only if empty, never recursively: hashed long paths\n // share sock/h/ across workstreams, so a workstream named \"h\" must\n // not take the others' sockets with it.\n for (const name of agentNames) unlinkCtlSocket(db, name, opts.workstream);\n if (!db.memory) {\n const sockDir = join(dirname(db.name), \"sock\", opts.workstream);\n try {\n if (readdirSync(sockDir).length === 0) rmdirSync(sockDir);\n } catch {\n // Missing or non-empty: nothing of ours left to reap.\n }\n }\n\n // One DELETE: the FK CASCADE chain (workstreams → agents,\n // workstreams → tasks → task_edges + task_notes, workstreams →\n // agent_logs, workstreams → vcs_workspaces) cleans every row in\n // one shot, atomically. If the workstream was never registered\n // (e.g. an orphan mux session that mu never observed),\n // changes() = 0 and we still report the killed mux session\n // honestly.\n // One group for the entire teardown: the DELETE cascades to tasks,\n // edges and notes, and each cascaded row gets its own tombstone op\n // (SQLite FK CASCADE DOES fire triggers — verified empirically, see\n // src/capture.ts). They all share this group so the whole teardown is\n // one unit for `mu undo`.\n withOpContext(db, { intent: \"workstream.teardown\", group: \"new\" }, () =>\n db.prepare(\"DELETE FROM workstreams WHERE name = ?\").run(opts.workstream),\n );\n // No emitEvent: the DELETE cascade fired the capture triggers, which\n // wrote tombstone ops (op='del', intent='workstream.teardown') for the\n // workstream AND for every task/edge/note that cascaded with it —\n // strictly more information than the prose counts, and it survives the\n // cascade because ops is FK-free.\n\n return {\n killedMux: muxAliveBefore,\n deletedAgents: agentsBefore,\n deletedTasks: tasksBefore,\n deletedNotes: notesBefore,\n deletedEdges: edgesBefore,\n freedWorkspaces,\n alreadyGoneWorkspaces,\n failedWorkspaces,\n };\n}\n\n// ─── Counts ────────────────────────────────────────────────────────────\n\nfunction listAgentNames(db: Db, workstream: string): string[] {\n const rows = db\n .prepare(\n `SELECT a.name FROM agents a\n JOIN workstreams ws ON ws.id = a.workstream_id\n WHERE ws.name = ?`,\n )\n .all(workstream) as { name: string }[];\n return rows.map((r) => r.name);\n}\n\nfunction countAgents(db: Db, workstream: string): number {\n const row = db\n .prepare(\n `SELECT COUNT(*) AS n FROM agents a\n JOIN workstreams ws ON ws.id = a.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number };\n return row.n;\n}\n\nfunction countTasks(db: Db, workstream: string): number {\n const row = db\n .prepare(\n `SELECT COUNT(*) AS n FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number };\n return row.n;\n}\n\nfunction countNotes(db: Db, workstream: string): number {\n const row = db\n .prepare(\n `SELECT COUNT(*) AS n\n FROM task_notes n\n JOIN tasks t ON t.id = n.task_id\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number };\n return row.n;\n}\n\nfunction countEdges(db: Db, workstream: string): number {\n // Count edges whose blocker (from_task) is in the workstream. Since\n // cross-workstream edges are forbidden by addTask, this equals the\n // edge count for the workstream subgraph.\n const row = db\n .prepare(\n `SELECT COUNT(*) AS n\n FROM task_edges e\n JOIN tasks t ON t.id = e.from_task_id\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number };\n return row.n;\n}\n","// mu — task edit/write verbs: add task, add note, update, delete.\n\nimport { type Db, resolveWorkstreamId } from \"../db.js\";\nimport { currentOpContext, withOpContext } from \"../op-context.js\";\nimport { ensureWorkstream } from \"../workstream.js\";\nimport { taskIdFor, touchTask } from \"./core.js\";\nimport { dedupeBlockersById, wouldCreateCycle } from \"./edges.js\";\nimport {\n CrossWorkstreamEdgeError,\n CycleError,\n TaskExistsError,\n TaskIdInvalidError,\n TaskNotFoundError,\n} from \"./errors.js\";\nimport { isValidTaskId } from \"./id.js\";\nimport { getTask } from \"./queries.js\";\n\nexport interface AddTaskOptions {\n localId: string;\n workstream: string;\n title: string;\n /** 1..100; enforced by schema CHECK. */\n impact: number;\n /** > 0; enforced by schema CHECK. */\n effortDays: number;\n /**\n * Tasks that block this one. Edges inserted as `blocker -> newTask`.\n * Each blocker must already exist AND share this task's workstream\n * (cross-workstream edges are forbidden); cycle check guards each\n * edge. The CLI surfaces this as `--blocked-by`; the SDK key matches.\n */\n blockedBy?: string[];\n /** Start as OPEN/triage (a proposal, e.g. a review finding) instead of OPEN/todo. */\n triage?: boolean;\n}\n\n/**\n * Atomically create a task and (optionally) its incoming blocked-by\n * edges.\n *\n * The task insert + every edge insert + cycle check happen inside one\n * SQLite transaction. If any blocker is missing or any edge would\n * create a cycle, the entire add rolls back.\n *\n * Cycle check for `addTask` is structurally trivial (a fresh task has\n * no outgoing edges, so `to -> ... -> from` is impossible). It's still\n * called here so the same primitive is exercised by tests.\n */\nexport function addTask(db: Db, opts: AddTaskOptions) {\n return withOpContext(db, { intent: \"task.add\", group: \"new\" }, () => addTaskImpl(db, opts));\n}\n\nfunction addTaskImpl(db: Db, opts: AddTaskOptions) {\n if (!isValidTaskId(opts.localId)) {\n throw new TaskIdInvalidError(opts.localId);\n }\n\n return db\n .transaction(() => {\n // Auto-create the workstream row so tasks.workstream_id FK is\n // satisfied (preserves spawn-without-init ergonomics).\n ensureWorkstream(db, opts.workstream);\n const wsId = resolveWorkstreamId(db, opts.workstream);\n\n // Per-workstream uniqueness: a duplicate local_id within the same\n // workstream throws TaskExistsError. Different workstreams may\n // legitimately share local_ids in v5.\n const existing = db\n .prepare(\"SELECT id FROM tasks WHERE workstream_id = ? AND local_id = ?\")\n .get(wsId, opts.localId) as { id: number } | undefined;\n if (existing) {\n throw new TaskExistsError(opts.localId);\n }\n\n const now = new Date().toISOString();\n const insertResult = db\n .prepare(\n `INSERT INTO tasks (workstream_id, local_id, title, status, substate, impact, effort_days, created_at, updated_at)\n VALUES (?, ?, ?, 'OPEN', ?, ?, ?, ?, ?)`,\n )\n .run(\n wsId,\n opts.localId,\n opts.title,\n opts.triage === true ? \"triage\" : \"todo\",\n opts.impact,\n opts.effortDays,\n now,\n now,\n );\n const newTaskId = Number(insertResult.lastInsertRowid);\n\n if (opts.blockedBy && opts.blockedBy.length > 0) {\n // Prefer the same-workstream blocker first (v5 per-workstream\n // local_id), then fall back to a global lookup so a cross-ws\n // blocker still surfaces CrossWorkstreamEdgeError (not\n // TaskNotFoundError). Without the same-ws preference, two\n // blockers of the same local_id (one in this ws, one elsewhere)\n // could silently bind to the wrong row\n // (bug_v5_name_clash_silent_misroute).\n const blockerLookupSameWs = db.prepare(\n `SELECT t.id AS id, ws.name AS workstream FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE t.local_id = ? AND t.workstream_id = ?`,\n );\n const blockerLookupAnyWs = db.prepare(\n `SELECT t.id AS id, ws.name AS workstream FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE t.local_id = ? LIMIT 1`,\n );\n const requestedBlockers = opts.blockedBy.map((blocker) => {\n const row = (blockerLookupSameWs.get(blocker, wsId) ?? blockerLookupAnyWs.get(blocker)) as\n | { id: number; workstream: string }\n | undefined;\n if (!row) {\n throw new TaskNotFoundError(blocker);\n }\n if (row.workstream !== opts.workstream) {\n throw new CrossWorkstreamEdgeError(\n blocker,\n row.workstream,\n opts.localId,\n opts.workstream,\n );\n }\n if (wouldCreateCycle(db, row.id, newTaskId)) {\n throw new CycleError(blocker, opts.localId);\n }\n return { localId: blocker, id: row.id };\n });\n const canonicalBlockers = dedupeBlockersById(requestedBlockers);\n const insertEdge = db.prepare(\n \"INSERT INTO task_edges (from_task_id, to_task_id, created_at) VALUES (?, ?, ?)\",\n );\n for (const blocker of canonicalBlockers) {\n insertEdge.run(blocker.id, newTaskId, now);\n }\n }\n\n const row = getTask(db, opts.localId, opts.workstream);\n if (!row) throw new Error(`addTask: row missing after insert: ${opts.localId}`);\n // No emitEvent: the INSERT above fired the tasks capture trigger,\n // which already wrote intent='task.add' key='<ws>/<id>' with the\n // full row as payload (v2-retire-log-shim).\n return row;\n })\n .immediate();\n}\n\nexport interface AddNoteOptions {\n /** Free-form author label. Convention: agent name, \"user\", or \"orchestrator\". */\n author?: string;\n /** Workstream context (operator-facing name). v5: tasks.local_id is\n * per-workstream unique, so this is required to disambiguate. */\n workstream: string;\n}\n\nexport function addNote(db: Db, taskLocalId: string, content: string, opts: AddNoteOptions) {\n return withOpContext(db, { intent: \"task.note\", actor: opts.author, group: \"new\" }, () =>\n insertNote(db, taskLocalId, content, opts),\n );\n}\n\n/** addNote without its own op context: the note joins the CALLER's\n * group and intent. For verbs whose note is part of one action (the\n * `--why` of `close --as` / `park`), so `mu undo` reverts both together.\n * Internal — not re-exported from src/tasks.ts. */\nexport function insertNote(db: Db, taskLocalId: string, content: string, opts: AddNoteOptions) {\n const task = getTask(db, taskLocalId, opts.workstream);\n if (!task) {\n throw new TaskNotFoundError(taskLocalId);\n }\n const taskId = taskIdFor(db, task.name, task.workstreamName);\n if (taskId === null) throw new TaskNotFoundError(taskLocalId);\n const now = new Date().toISOString();\n db.transaction(() => {\n const r = db\n .prepare(\"INSERT INTO task_notes (task_id, author, content, created_at) VALUES (?, ?, ?, ?)\")\n .run(taskId, opts.author ?? null, content, now);\n // Bump the parent task so `mu task list --sort recency` surfaces\n // freshly-noted tasks (task_updatedat_not_bumped_by_reparent).\n touchTask(db, taskId, now);\n return r;\n }).immediate();\n // No emitEvent: the task_notes INSERT fired the capture trigger\n // (intent='task.note', key='<ws>/<id>#<n>').\n return {\n author: opts.author ?? null,\n content,\n createdAt: now,\n };\n}\n\nexport interface DeleteTaskResult {\n /** True iff the row existed and was deleted. False on a dry-run\n * (preview) AND on the idempotent missing-row case. */\n deleted: boolean;\n /** Number of `task_edges` rows cascaded out (informational). On a\n * dry-run, this is the would-be count. */\n deletedEdges: number;\n /** Number of `task_notes` rows cascaded out (informational). On a\n * dry-run, this is the would-be count. */\n deletedNotes: number;\n /** True iff this was a dry-run (`opts.dryRun: true`). On a\n * dry-run `deleted` is false and the counts are the would-be\n * counts; the DB is unchanged. Always false on a commit / on a\n * missing-row idempotent no-op. */\n dryRun: boolean;\n /** True iff a matching task row was found at the time of the\n * call. Discriminator for the CLI: a dry-run that found nothing\n * (`present: false`) renders differently from a dry-run that\n * found an existing task with zero edges and zero notes\n * (`present: true, deletedEdges: 0, deletedNotes: 0`). */\n present: boolean;\n /** The op group the delete wrote, for `mu undo <group> --yes`. Null\n * when nothing was deleted (dry-run or missing row). */\n group: string | null;\n}\n\nexport interface DeleteTaskOptions {\n /** When true, return the cascade preview (would-be edge / note\n * counts) without mutating. The CLI uses this to power the bare\n * `mu task delete <id>` two-phase pattern (mirrors\n * `mu workstream teardown` / `mu undo`). Surfaced\n * by feedback ws task\n * fb_task_delete_no_yes (impact=30): a dogfood report typed\n * `mu task delete X --yes` (mirroring workstream teardown) and got\n * 'unknown option --yes' — the verb took no confirmation flag at\n * all. Two failed deletes left long-named tasks lingering. */\n dryRun?: boolean;\n}\n\n/**\n * Delete a task. FK CASCADE on `task_edges` (from + to) and\n * `task_notes` cleans the joined rows automatically. Idempotent on\n * a missing task (returns `deleted: false`).\n *\n * Pre-counts the cascade victims for reporting because SQLite's\n * `changes()` only reports rows directly affected by the DELETE.\n *\n * With `opts.dryRun: true`, returns the would-be counts without\n * touching the DB (no mutation, so no op is written). The CLI bare\n * `mu task delete <id>` form uses this; `--yes` calls through with\n * `dryRun: false`.\n */\nexport function deleteTask(\n db: Db,\n localId: string,\n workstream: string,\n opts: DeleteTaskOptions = {},\n): DeleteTaskResult {\n return withOpContext(db, { intent: \"task.delete\", group: \"new\" }, () =>\n deleteTaskImpl(db, localId, workstream, opts),\n );\n}\n\nfunction deleteTaskImpl(\n db: Db,\n localId: string,\n workstream: string,\n opts: DeleteTaskOptions,\n): DeleteTaskResult {\n const dryRun = opts.dryRun === true;\n const before = getTask(db, localId, workstream);\n if (!before) {\n // Idempotent on a missing row regardless of dryRun.\n return {\n deleted: false,\n deletedEdges: 0,\n deletedNotes: 0,\n dryRun,\n present: false,\n group: null,\n };\n }\n const taskId = taskIdFor(db, localId, before.workstreamName);\n if (taskId === null) {\n return {\n deleted: false,\n deletedEdges: 0,\n deletedNotes: 0,\n dryRun,\n present: false,\n group: null,\n };\n }\n const edgesBefore = (\n db\n .prepare(\"SELECT COUNT(*) AS n FROM task_edges WHERE from_task_id = ? OR to_task_id = ?\")\n .get(taskId, taskId) as { n: number }\n ).n;\n const notesBefore = (\n db.prepare(\"SELECT COUNT(*) AS n FROM task_notes WHERE task_id = ?\").get(taskId) as {\n n: number;\n }\n ).n;\n if (dryRun) {\n return {\n deleted: false,\n deletedEdges: edgesBefore,\n deletedNotes: notesBefore,\n dryRun: true,\n present: true,\n group: null,\n };\n }\n // No pre-mutation snapshot: v9 dropped the `snapshots` table and\n // rollback is inverse ops over the ops log (`mu undo`).\n const result = db.prepare(\"DELETE FROM tasks WHERE id = ?\").run(taskId);\n // No emitEvent: the DELETE fired the capture trigger, which wrote a\n // tombstone op (op='del', intent='task.delete').\n const deleted = result.changes > 0;\n return {\n deleted,\n deletedEdges: edgesBefore,\n deletedNotes: notesBefore,\n dryRun: false,\n present: true,\n // deleteTask opened a fresh group around this call.\n group: deleted ? currentOpContext(db).groupId : null,\n };\n}\n\nexport interface UpdateTaskOptions {\n title?: string;\n /** 1..100; enforced by schema CHECK. */\n impact?: number;\n /** > 0; enforced by schema CHECK. */\n effortDays?: number;\n}\n\nexport interface UpdateTaskResult {\n /** True iff at least one field actually changed. */\n updated: boolean;\n /** The fields whose values differ post-update (in `UpdateTaskOptions`'s\n * camelCase shape). Empty when `updated: false`. */\n changedFields: string[];\n}\n\n/**\n * Update scalar fields on a task. Each option is independently optional;\n * passing none is a typed no-op (returns `updated: false, changedFields: []`).\n * Fields whose new value equals the current value are skipped (no row change).\n *\n * NOT for status (use `closeTask` / `openTask` / `setTaskStatus`), owner\n * (use `claimTask` / `releaseTask`), local_id (rename is deferred), or\n * workstream (cross-workstream moves are deferred).\n */\nexport interface UpdateTaskScopeOption {\n workstream: string;\n}\n\nexport function updateTask(\n db: Db,\n localId: string,\n opts: UpdateTaskOptions,\n scope: UpdateTaskScopeOption,\n): UpdateTaskResult {\n return withOpContext(db, { intent: \"task.update\", group: \"new\" }, () =>\n updateTaskImpl(db, localId, opts, scope),\n );\n}\n\nfunction updateTaskImpl(\n db: Db,\n localId: string,\n opts: UpdateTaskOptions,\n scope: UpdateTaskScopeOption,\n): UpdateTaskResult {\n const before = getTask(db, localId, scope.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n const taskId = taskIdFor(db, before.name, before.workstreamName);\n if (taskId === null) throw new TaskNotFoundError(localId);\n\n const setters: string[] = [];\n const params: unknown[] = [];\n const changedFields: string[] = [];\n\n if (opts.title !== undefined && opts.title !== before.title) {\n setters.push(\"title = ?\");\n params.push(opts.title);\n changedFields.push(\"title\");\n }\n if (opts.impact !== undefined && opts.impact !== before.impact) {\n setters.push(\"impact = ?\");\n params.push(opts.impact);\n changedFields.push(\"impact\");\n }\n if (opts.effortDays !== undefined && opts.effortDays !== before.effortDays) {\n setters.push(\"effort_days = ?\");\n params.push(opts.effortDays);\n changedFields.push(\"effortDays\");\n }\n\n if (setters.length === 0) {\n return { updated: false, changedFields: [] };\n }\n\n setters.push(\"updated_at = ?\");\n params.push(new Date().toISOString());\n params.push(taskId);\n\n // No emitEvent: the UPDATE fired the capture trigger, whose payload is\n // a semantic partial update naming exactly the changed columns —\n // strictly more useful than the prose \"(changed: a, b)\" it replaces.\n db.prepare(`UPDATE tasks SET ${setters.join(\", \")} WHERE id = ?`).run(...params);\n return { updated: true, changedFields };\n}\n","// mu — task lifecycle verbs: setTaskStatus, closeTask, openTask.\n//\n// Lifecycle = \"transition a task from one status to another, with\n// the right captured-op and evidence-note side effects\".\n//\n// EvidenceOption is shared with claim/release (in tasks/claim.ts) and\n// re-exported here as the canonical home; claim.ts imports from this\n// file.\n//\n// Extracted from src/tasks.ts as part of refactor_split_large_src_files.\n\nimport type { Db } from \"../db.js\";\nimport { withOpContext } from \"../op-context.js\";\nimport { taskIdFor } from \"./core.js\";\nimport { getTaskEdgesWithStatus } from \"./edges.js\";\nimport { addNote, insertNote } from \"./edit.js\";\nimport {\n InvalidSubstateError,\n SubstateReasonRequiredError,\n TaskNotFoundError,\n TaskParkStateError,\n} from \"./errors.js\";\nimport { getTask, listNotes } from \"./queries.js\";\nimport {\n DEFAULT_SUBSTATE,\n isValidPair,\n type TASK_SUBSTATES,\n type TaskStatus,\n type TaskSubstate,\n} from \"./status.js\";\n\nexport interface SetStatusResult {\n /** Status before the call. */\n previousStatus: TaskStatus;\n /** Status after the call (== requested status). */\n status: TaskStatus;\n /** Substate before the call. */\n previousSubstate: TaskSubstate;\n /** Substate after the call (== requested substate, or the status default). */\n substate: TaskSubstate;\n /** True iff status OR substate changed. False on idempotent no-op. */\n changed: boolean;\n}\n\n/** The substates a task can be closed as (`mu task close --as`). */\nexport type CloseSubstate = (typeof TASK_SUBSTATES)[\"CLOSED\"][number];\n\n/**\n * Optional evidence string carried on lifecycle verbs (close / open /\n * claim / release). Lands in the auto-emitted `kind='event'` payload\n * verbatim, prefixed with `evidence=`. The first inch of distinguishing\n * \"observed\" from \"claimed\" state per an internal critique: the\n * verb still trusts the caller (it's not a verifier), but the audit\n * trail records what the caller said it relied on.\n */\nexport interface EvidenceOption {\n evidence?: string;\n}\n\n/** Evidence plus the actor its note is attributed to. The CLI passes\n * `resolveActorIdentity()`; without it the note has no author and\n * renders as `<orchestrator>`. */\nexport interface AttributedEvidence extends EvidenceOption {\n author?: string;\n}\n\n/**\n * Persist `--evidence` as a task note, so it survives as a captured op.\n *\n * mu once put evidence in the prose event payload AND (for close only) in a\n * synthetic note. v2-retire-log-shim deleted the prose events, which\n * would have silently dropped evidence on open/release. The note is now\n * the single home for it: notes are portable and sync, whereas the prose\n * event was machine-local and unparseable.\n *\n * Only fires when the verb actually changed something (an idempotent\n * re-close attests nothing new) and the evidence is a non-empty string.\n */\nexport function recordEvidenceNote(\n db: Db,\n localId: string,\n workstream: string,\n label: string,\n opts: AttributedEvidence | undefined,\n): void {\n if (!opts || opts.evidence === undefined || opts.evidence === \"\") return;\n const noteOpts: { author?: string; workstream: string } = { workstream };\n if (opts.author !== undefined && opts.author !== \"\") noteOpts.author = opts.author;\n addNote(db, localId, `${label}: ${opts.evidence}`, noteOpts);\n}\n\n/** Render the optional `--evidence \"<text>\"` payload as the trailing\n * ' evidence=\"...\"' on every state-changing event. Exported because\n * claimTask/releaseTask in src/tasks/claim.ts also use it. */\nexport function evidenceSuffix(opts: EvidenceOption | undefined): string {\n if (!opts || opts.evidence === undefined) return \"\";\n return ` evidence=${JSON.stringify(opts.evidence)}`;\n}\n\nexport interface SetStatusOptions extends EvidenceOption {\n workstream: string;\n /** Substate to land on; defaults to DEFAULT_SUBSTATE[status]. A pair\n * outside TASK_SUBSTATES throws InvalidSubstateError before any write. */\n substate?: TaskSubstate;\n}\n\n/**\n * Flip a task's status to any of OPEN / IN_PROGRESS / CLOSED, writing\n * status and substate in ONE UPDATE (one op, one HLC — spec D7).\n * Idempotent: setting a task to its current pair is a no-op (returns\n * `changed: false`) rather than throwing. Owner is unchanged.\n */\nexport function setTaskStatus(\n db: Db,\n localId: string,\n status: TaskStatus,\n opts: SetStatusOptions,\n): SetStatusResult {\n // NOTE: no `group` here, so a nested call inherits the enclosing\n // group. A direct call with no enclosing context still gets its own\n // group (withOpContext mints one).\n //\n // `intentIfUnset` (not `intent`): when reached via closeTask, the\n // outer verb is the label the operator recognises, so it must win.\n // Only a direct setTaskStatus call labels itself.\n return withOpContext(db, { intentIfUnset: `task.set-${status.toLowerCase()}` }, () =>\n setTaskStatusImpl(db, localId, status, opts),\n );\n}\n\nfunction setTaskStatusImpl(\n db: Db,\n localId: string,\n status: TaskStatus,\n opts: SetStatusOptions,\n): SetStatusResult {\n const substate = opts.substate ?? DEFAULT_SUBSTATE[status];\n if (!isValidPair(status, substate)) throw new InvalidSubstateError(status, substate);\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n const base = {\n previousStatus: before.status,\n status,\n previousSubstate: before.substate,\n substate,\n };\n if (before.status === status && before.substate === substate) {\n return { ...base, changed: false };\n }\n // v5: tasks.local_id is per-workstream unique. Scope to the row's\n // workstream so the UPDATE doesn't accidentally touch a same-named\n // task in another workstream.\n db.prepare(\n `UPDATE tasks SET status = ?, substate = ?, updated_at = ?\n WHERE local_id = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)`,\n ).run(status, substate, new Date().toISOString(), localId, before.workstreamName);\n // No emitEvent: the UPDATE fired the capture trigger, whose intent is\n // the specific verb (task.close / task.open, or task.set-<status> for\n // a bare status set) and whose payload names the new status. Evidence,\n // when passed, lands as a task note — itself a captured op.\n return { ...base, changed: true };\n}\n\n/** Result of `closeTask` when called with `ifReady: true` and the\n * task is NOT yet ready to close (still has at least one OPEN /\n * IN_PROGRESS blocker). Distinguished from a regular `SetStatusResult`\n * by the literal `skipped` field; the CLI keys on it to switch\n * between the \"closed\" and \"waiting\" rendering paths.\n *\n * Surfaced in `fb_umbrella_no_auto_close` (impact=60): a wave umbrella\n * with N blockers stayed OPEN after every blocker reached a terminal\n * status. `--if-ready` is the cheap fix: bare `mu task close` is\n * unchanged (closes regardless), `--if-ready` is a no-op unless every\n * blocker is CLOSED. */\nexport interface CloseSkippedResult {\n /** Always 'not_ready' when set; future cause-codes can extend this\n * without reshaping the JSON payload (the literal-union narrows\n * safely in the CLI rendering path). */\n skipped: \"not_ready\";\n /** Status before the call (always the current status, no change). */\n previousStatus: TaskStatus;\n /** Status after the call (== previousStatus, since we no-op). */\n status: TaskStatus;\n /** Substate before (and after) the call. */\n previousSubstate: TaskSubstate;\n substate: TaskSubstate;\n /** Always false on a skip (no row mutated). */\n changed: false;\n /** Local ids of every blocker still in OPEN or IN_PROGRESS, sorted\n * alphabetically for deterministic rendering. Empty list is\n * impossible on this branch — the no-op only fires when ≥1\n * blocker is non-terminal. */\n blockingIds: string[];\n}\n\nexport interface CloseTaskOptions extends EvidenceOption {\n workstream: string;\n /** When true, no-op the close unless every blocker is CLOSED.\n * Returns a `CloseSkippedResult` carrying the still-blocking ids;\n * the CLI renders the skip with a Next: hint pointing at\n * `mu task wait`. When false / omitted, behaves as bare `closeTask`\n * (closes regardless of blocker status). */\n ifReady?: boolean;\n /** Optional actor identity attributed to the synthetic `CLOSE: …`\n * note auto-inserted when `evidence` is non-empty (see closeTask\n * body). The CLI resolves this via `resolveActorIdentity()` so the\n * note carries the closing worker's name; SDK callers (tests,\n * internal use) may omit it (the note then carries no author, same\n * as a bare `addNote` without `--author`). Surfaced in mufeedback\n * task_close_evidence_does_not_append_the. */\n author?: string;\n /** Closing substate; default \"done\" (or, for an already-CLOSED task,\n * its current substate). Any CLOSED/* satisfies edges. */\n as?: CloseSubstate;\n /** Required (non-empty) unless `as` is \"done\"; stored as a\n * `<AS>: <why>` note in the same op group. */\n why?: string;\n}\n\n/** Result of a close that ran (not skipped). */\nexport interface CloseTaskResult extends SetStatusResult {\n /** Direct same-workstream dependents that entered the `ready` view\n * because of this close, sorted. Computed only when `as` is not\n * \"done\" (spec D3: a non-done close must show what it released);\n * always [] for a done close. */\n unblocked: string[];\n}\n\n/** Convenience: setTaskStatus(db, id, \"CLOSED\"). Accepts evidence.\n * Notes are skipped for the idempotent no-op (already CLOSED with the\n * same substate; a bare close without `as` keeps the current one) so\n * retry loops don't accumulate duplicates.\n *\n * With `ifReady: true`, returns a `CloseSkippedResult` (no mutation,\n * no snapshot) when any blocker is still OPEN / IN_PROGRESS. Used by\n * `mu task close --if-ready` so an orchestrator can fire-and-forget\n * the umbrella close after every blocker resolves without first\n * re-querying the graph. */\nexport function closeTask(\n db: Db,\n localId: string,\n opts: CloseTaskOptions,\n): CloseTaskResult | CloseSkippedResult {\n // Validate before any write (and before withOpContext mints a group).\n const as = opts.as ?? \"done\";\n if (!isValidPair(\"CLOSED\", as)) throw new InvalidSubstateError(\"CLOSED\", as);\n if (as !== \"done\" && (opts.why === undefined || opts.why.trim() === \"\")) {\n throw new SubstateReasonRequiredError(\"close\", as, localId);\n }\n return withOpContext(db, { intent: \"task.close\", actor: opts.author, group: \"new\" }, () =>\n closeTaskImpl(db, localId, opts),\n );\n}\n\nfunction closeTaskImpl(\n db: Db,\n localId: string,\n opts: CloseTaskOptions,\n): CloseTaskResult | CloseSkippedResult {\n const before = getTask(db, localId, opts.workstream);\n // A bare re-close (no --as) of an already-CLOSED task keeps its\n // substate, so it stays the idempotent no-op the help promises\n // instead of rewriting wontfix/rejected/duplicate to done.\n const as: CloseSubstate =\n opts.as ?? (before?.status === \"CLOSED\" ? (before.substate as CloseSubstate) : \"done\");\n if (opts.ifReady && before) {\n // Inspect direct blockers only — the umbrella convention is one\n // hop (umbrella -[blocked-by]→ each wave task). If any direct\n // blocker is not CLOSED, the umbrella isn't ready.\n const edges = getTaskEdgesWithStatus(db, localId, before.workstreamName);\n const blocking = edges.blockers\n .filter((e) => e.status !== \"CLOSED\")\n .map((e) => e.name)\n .sort();\n if (blocking.length > 0) {\n return {\n skipped: \"not_ready\",\n previousStatus: before.status,\n status: before.status,\n previousSubstate: before.substate,\n substate: before.substate,\n changed: false,\n blockingIds: blocking,\n };\n }\n }\n // No pre-mutation snapshot: v9 dropped the `snapshots` table and\n // rollback is inverse ops over the ops log (`mu undo`).\n return db\n .transaction((): CloseTaskResult => {\n const track = as !== \"done\" && before !== undefined;\n const readyBefore = track ? readyDependents(db, localId, before.workstreamName) : [];\n const r = setTaskStatus(db, localId, \"CLOSED\", {\n workstream: opts.workstream,\n substate: as,\n ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}),\n });\n if (!r.changed || !before) return { ...r, unblocked: [] };\n recordReasonNote(db, localId, before.workstreamName, as, opts.why, opts.author);\n // mufeedback task_close_evidence_does_not_append_the: evidence must\n // reach `mu task notes <id>` / `mu task show <id>`, not just the log.\n // Since v2-retire-log-shim the note is the ONLY home for it.\n recordEvidenceNote(db, localId, before.workstreamName, \"CLOSE\", opts);\n const unblocked = track\n ? readyDependents(db, localId, before.workstreamName).filter(\n (n) => !readyBefore.includes(n),\n )\n : [];\n return { ...r, unblocked };\n })\n .immediate();\n}\n\n/** Direct dependents of `localId` currently in the `ready` view, sorted.\n * Edges never cross workstreams, so these are all in `workstream`. */\nfunction readyDependents(db: Db, localId: string, workstream: string): string[] {\n const id = taskIdFor(db, localId, workstream);\n if (id === null) return [];\n const rows = db\n .prepare(\n `SELECT r.local_id AS name FROM task_edges e\n JOIN ready r ON r.id = e.to_task_id\n WHERE e.from_task_id = ? ORDER BY r.local_id`,\n )\n .all(id) as { name: string }[];\n return rows.map((r) => r.name);\n}\n\n/** Store a classification's `--why` as a `<SUBSTATE>: <why>` note\n * (e.g. `WONTFIX: out of scope`). Notes are captured, so it syncs,\n * and it lands in the enclosing verb's op group. No-op for an empty\n * reason (a done close needs none). */\nfunction recordReasonNote(\n db: Db,\n localId: string,\n workstream: string,\n substate: TaskSubstate,\n why: string | undefined,\n author: string | undefined,\n): void {\n if (why === undefined || why.trim() === \"\") return;\n const noteOpts: { author?: string; workstream: string } = { workstream };\n if (author !== undefined && author !== \"\") noteOpts.author = author;\n const text =\n substate === \"rejected\" || substate === \"superseded\"\n ? withTitles(db, why, workstream, localId)\n : why;\n // insertNote, not addNote: the note joins the verb's group and\n // intent instead of minting its own, so one undo reverts both.\n insertNote(db, localId, `${substate.toUpperCase()}: ${text}`, noteOpts);\n}\n\n/** A token shaped like a task id, not inside a word, path, or file\n * name: never preceded by a word char, `.`, `/` or `-`, and never\n * followed by an extension (`fix.ts`) or a line number (`fix:12`). */\nconst TASK_ID_TOKEN = /(?<![\\w./-])[a-z][a-z0-9_-]{0,63}(?![\\w-]|\\.\\w|:\\d)/g;\n\n/** {@link TASK_ID_TOKEN}, plus an optional parenthetical opener right\n * after it (group 1), so the title can merge into it. */\nconst TASK_ID_WITH_PAREN = new RegExp(`(?:${TASK_ID_TOKEN.source})(\\\\s*\\\\()?`, \"g\");\n\n/** Ids in `text` naming another task in `workstream`. */\nfunction namedTaskIds(db: Db, text: string, workstream: string, self: string): string[] {\n const ids = (text.match(TASK_ID_TOKEN) ?? []).filter((t) => t !== self);\n return [...new Set(ids)].filter((t) => getTask(db, t, workstream) !== undefined);\n}\n\n/** Append each named task's title after its id, so a REJECTED /\n * SUPERSEDED note reads without looking the ids up: `f2 (title)`.\n * An id already followed by a parenthetical (the skill's REJECT form\n * `<id> (<why it fails>)`) gets one merged parenthetical instead:\n * `f2 (title; why it fails)`. Text that names no task in the\n * workstream is left as is. */\nfunction withTitles(db: Db, why: string, workstream: string, self: string): string {\n const named = new Set(namedTaskIds(db, why, workstream, self));\n if (named.size === 0) return why;\n return why.replace(TASK_ID_WITH_PAREN, (match: string, paren: string | undefined) => {\n const id = paren === undefined ? match : match.slice(0, -paren.length);\n if (!named.has(id)) return match;\n const title = getTask(db, id, workstream)?.title ?? \"\";\n return paren === undefined ? `${id} (${title})` : `${id} (${title}; `;\n });\n}\n\n/** Below this many characters a decision reason names no check. */\nexport const WEAK_REASON_CHARS = 40;\n\n/** A note line recording a verdict: `VERDICT: …`, or the header\n * `REFUTER <label> (...)` a recorded delegate answer starts with. */\nconst VERDICT_LINE = /^\\s*(VERDICT:|REFUTER\\s)/m;\n\n/**\n * Decision-time guardrail for a finding. Returns one warning (or\n * none) for accepting a task in triage (`kind: \"accept\"`, reason =\n * `--evidence`) or closing it as rejected / wontfix / duplicate\n * (reason = `--why`). Warns only when the task is OPEN/triage, the\n * reason is missing or under {@link WEAK_REASON_CHARS} chars, and no\n * note records a verdict. A duplicate whose reason names an existing\n * task is fine. Read-only: call it before the verb writes.\n */\nexport function weakDecisionWarning(\n db: Db,\n localId: string,\n workstream: string,\n kind: \"accept\" | CloseSubstate,\n reason: string | undefined,\n): string | undefined {\n if (kind === \"done\" || kind === \"superseded\") return undefined;\n const task = getTask(db, localId, workstream);\n if (task?.status !== \"OPEN\" || task.substate !== \"triage\") return undefined;\n const trimmed = (reason ?? \"\").trim();\n if (trimmed.length >= WEAK_REASON_CHARS) return undefined;\n if (kind === \"duplicate\" && namedTaskIds(db, trimmed, workstream, localId).length > 0) {\n return undefined;\n }\n if (listNotes(db, localId, workstream).some((n) => VERDICT_LINE.test(n.content))) {\n return undefined;\n }\n const flag = kind === \"accept\" ? \"--evidence\" : \"--why\";\n const what = trimmed === \"\" ? `no ${flag}` : `${flag} is ${trimmed.length} chars`;\n return `${localId}: ${kind === \"accept\" ? \"accepting\" : `closing as ${kind}`} a finding with ${what} and no verdict recorded (no VERDICT: line or REFUTER note); record what confirmed it`;\n}\n\n/** Convenience: setTaskStatus(db, id, \"OPEN\"). Owner intentionally NOT\n * cleared — use `releaseTask` for that. Accepts evidence. */\nexport function openTask(\n db: Db,\n localId: string,\n opts: AttributedEvidence & { workstream: string },\n): SetStatusResult {\n return withOpContext(db, { intent: \"task.open\", actor: opts.author, group: \"new\" }, () => {\n const before = getTask(db, localId, opts.workstream);\n const r = setTaskStatus(db, localId, \"OPEN\", {\n workstream: opts.workstream,\n ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}),\n });\n if (r.changed && before) recordEvidenceNote(db, localId, before.workstreamName, \"OPEN\", opts);\n return r;\n });\n}\n\nexport interface ParkTaskOptions extends EvidenceOption {\n workstream: string;\n /** Required, non-empty; stored as a `PARKED: <why>` note. */\n why: string;\n author?: string;\n}\n\n/**\n * OPEN/todo → OPEN/parked: keep the task out of `ready` / `next` and\n * make `claim` refuse it without `--force`. Its dependents stay blocked\n * (parked is still OPEN). Idempotent on OPEN/parked (no second note).\n * Refuses IN_PROGRESS (release first) and CLOSED (open first) — one\n * verb, one transition.\n */\nexport function parkTask(db: Db, localId: string, opts: ParkTaskOptions): SetStatusResult {\n if (opts.why.trim() === \"\") throw new SubstateReasonRequiredError(\"park\", \"parked\", localId);\n return withOpContext(db, { intent: \"task.park\", actor: opts.author, group: \"new\" }, () =>\n db\n .transaction((): SetStatusResult => {\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n if (before.status !== \"OPEN\") {\n throw new TaskParkStateError(localId, before.status, before.workstreamName);\n }\n const r = setTaskStatus(db, localId, \"OPEN\", {\n workstream: opts.workstream,\n substate: \"parked\",\n });\n if (r.changed) {\n recordReasonNote(db, localId, before.workstreamName, \"parked\", opts.why, opts.author);\n recordEvidenceNote(db, localId, before.workstreamName, \"PARK\", opts);\n }\n return r;\n })\n .immediate(),\n );\n}\n\n/**\n * OPEN/triage → OPEN/todo: accept a proposed task (a review finding) as\n * real work, back in `ready` / `next`. A no-op (`changed: false`) on any\n * other pair. Declining is `close --as rejected | duplicate --why`.\n */\nexport function acceptTask(\n db: Db,\n localId: string,\n opts: AttributedEvidence & { workstream: string },\n): SetStatusResult {\n return withOpContext(db, { intent: \"task.accept\", actor: opts.author, group: \"new\" }, () =>\n db\n .transaction((): SetStatusResult => {\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n if (before.status !== \"OPEN\" || before.substate !== \"triage\") {\n return {\n previousStatus: before.status,\n status: before.status,\n previousSubstate: before.substate,\n substate: before.substate,\n changed: false,\n };\n }\n const r = setTaskStatus(db, localId, \"OPEN\", { workstream: opts.workstream });\n recordEvidenceNote(db, localId, before.workstreamName, \"ACCEPT\", opts);\n return r;\n })\n .immediate(),\n );\n}\n\n/** OPEN/parked → OPEN/todo. A no-op (`changed: false`) on any other pair. */\nexport function unparkTask(\n db: Db,\n localId: string,\n opts: AttributedEvidence & { workstream: string },\n): SetStatusResult {\n return withOpContext(db, { intent: \"task.unpark\", actor: opts.author, group: \"new\" }, () =>\n db\n .transaction((): SetStatusResult => {\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n if (before.status !== \"OPEN\" || before.substate !== \"parked\") {\n return {\n previousStatus: before.status,\n status: before.status,\n previousSubstate: before.substate,\n substate: before.substate,\n changed: false,\n };\n }\n const r = setTaskStatus(db, localId, \"OPEN\", { workstream: opts.workstream });\n recordEvidenceNote(db, localId, before.workstreamName, \"UNPARK\", opts);\n return r;\n })\n .immediate(),\n );\n}\n","// mu — claim/release/resolveActorIdentity verbs.\n//\n// claimTask is the heart of mu's coordination protocol: an atomic\n// CAS via a single SQL UPDATE, with two flavours:\n//\n// \"worker claim\" : --for <name> sets owner_id to that agent (FK to agents.id)\n// \"anonymous claim\": --self keeps owner_id=NULL but flips status to\n// IN_PROGRESS and records the actor\n// in agent_logs\n//\n// resolveActorIdentity is the env-aware identity helper:\n// $MU_AGENT_NAME > pane title > $USER > 'orchestrator'. Used by --self\n// AND by the bare worker-claim path, so both flavours answer \"who am\n// I?\" identically.\n//\n// Extracted from src/tasks.ts as part of refactor_split_large_src_files.\n\nimport type { Db } from \"../db.js\";\nimport { activeMux } from \"../mux.js\";\nimport { withOpContext } from \"../op-context.js\";\nimport {\n ClaimerNotRegisteredError,\n TaskAlreadyOwnedError,\n TaskInTriageError,\n TaskNotFoundError,\n TaskParkedError,\n} from \"./errors.js\";\nimport { type AttributedEvidence, recordEvidenceNote } from \"./lifecycle.js\";\nimport { getTask } from \"./queries.js\";\nimport { DEFAULT_SUBSTATE, type TaskStatus } from \"./status.js\";\n\nexport interface ReleaseResult {\n /** The previous owner (null if the task was already unowned). */\n previousOwnerName: string | null;\n /** Status before the release. */\n previousStatus: TaskStatus;\n /** Status after the release. */\n status: TaskStatus;\n /** True iff owner OR status actually changed. */\n changed: boolean;\n}\n\nexport interface ReleaseTaskOptions extends AttributedEvidence {\n /** Workstream context for the task (v5: tasks.local_id is\n * per-workstream unique). */\n workstream: string;\n /** Force `status = OPEN` regardless of the current status. Without\n * this flag, `IN_PROGRESS` is also flipped to `OPEN` automatically\n * (so a released task isn't left structurally stranded with\n * `owner=NULL, status=IN_PROGRESS`); CLOSED is preserved.\n * `--reopen` is the override for the rarer \"un-close and hand\n * back to the pool\" workflow. */\n reopen?: boolean;\n}\n\n/**\n * Release a task: clear `tasks.owner`.\n *\n * Status side-effects (review_release_open_in_progress_inconsistency):\n * - IN_PROGRESS → OPEN automatically (without it, the task is\n * stranded: no owner to drive it forward, but `mu task next`\n * skips it because it's not OPEN).\n * - OPEN / CLOSED preserved.\n * - `--reopen` forces OPEN regardless of current status — the\n * escape hatch for un-closing a CLOSED owned task in one verb.\n *\n * Idempotent: releasing an already-unowned task with no `--reopen` and\n * no IN_PROGRESS status is a no-op (returns `changed: false`).\n * Throws TaskNotFoundError on missing.\n */\nexport function releaseTask(db: Db, localId: string, opts: ReleaseTaskOptions): ReleaseResult {\n return withOpContext(db, { intent: \"task.release\", actor: opts.author, group: \"new\" }, () =>\n releaseTaskImpl(db, localId, opts),\n );\n}\n\nfunction releaseTaskImpl(db: Db, localId: string, opts: ReleaseTaskOptions): ReleaseResult {\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n\n // Default: auto-flip IN_PROGRESS → OPEN so the released task isn't\n // left in the structurally weird owner=NULL/IN_PROGRESS limbo.\n // --reopen still wins for any status (including CLOSED).\n const newStatus: TaskStatus = opts.reopen\n ? \"OPEN\"\n : before.status === \"IN_PROGRESS\"\n ? \"OPEN\"\n : before.status;\n const ownerChanges = before.ownerName !== null;\n const statusChanges = newStatus !== before.status;\n\n if (!ownerChanges && !statusChanges) {\n return {\n previousOwnerName: before.ownerName,\n previousStatus: before.status,\n status: before.status,\n changed: false,\n };\n }\n\n // No pre-mutation snapshot: v9 dropped the `snapshots` table and\n // rollback is inverse ops over the ops log (`mu undo`).\n\n db.prepare(\n `UPDATE tasks SET owner_id = NULL, status = ?, substate = ?, updated_at = ?\n WHERE local_id = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)`,\n ).run(\n newStatus,\n statusChanges ? DEFAULT_SUBSTATE[newStatus] : before.substate,\n new Date().toISOString(),\n localId,\n before.workstreamName,\n );\n // No emitEvent: the UPDATE fired the capture trigger\n // (intent='task.release'), whose payload names owner_id and status —\n // the same facts the prose spelled out. Evidence goes to a note,\n // since the prose payload that used to carry it is gone.\n recordEvidenceNote(db, localId, before.workstreamName, \"RELEASE\", opts);\n return {\n previousOwnerName: before.ownerName,\n previousStatus: before.status,\n status: newStatus,\n changed: true,\n };\n}\n\n// ─── claimTask (verb) ──────────────────────────────────────────────────\n\nexport interface ClaimTaskOptions extends AttributedEvidence {\n /** Workstream context for both the task and the claiming agent.\n * v5: agents.name and tasks.local_id are per-workstream unique;\n * the task lookup AND the agent FK lookup scope to this\n * workstream so a same-named task or worker elsewhere can't be\n * silently picked. The CLI always passes this from the resolved\n * -w / $MU_SESSION. */\n workstream: string;\n /**\n * Override the agent name. If omitted, resolved from the ambient\n * environment via `resolveWorkerIdentity()`: `$MU_AGENT_NAME` first,\n * then the current pane's title.\n *\n * Mutually exclusive with `self: true`.\n */\n agentName?: string;\n /**\n * Workstream that the claimer agent lives in. When omitted, defaults\n * to `opts.workstream` (today's same-workstream behaviour). Set by\n * the CLI when `mu task claim X -w A --for B/worker-1` qualifies the\n * `--for` ref with a different workstream prefix\n * (`task_claim_for_cross_workstream`).\n *\n * Cross-workstream ownership is structurally allowed by the schema:\n * `tasks.owner_id` is an INTEGER FK to `agents.id` with no\n * workstream qualifier on the agent side. The per-workstream UNIQUE\n * on `agents(workstream_id, name)` is what previously made the\n * SDK's name → id lookup scope to one workstream; this option\n * widens that lookup to a different workstream when the operator\n * dispatches across a workstream boundary. The agent's own\n * workstream remains unchanged — only the task's `owner_id` points\n * out-of-workstream.\n */\n agentWorkstream?: string;\n /**\n * Anonymous claim: write `owner_id = NULL` instead of resolving an agent\n * name and checking the FK. Use when the actor is the orchestrator\n * (or a script, or a human) doing direct work in a workstream they\n * aren't a registered worker in.\n *\n * The actor name is still recorded — it ends up in `agent_logs.source`\n * for the auto-emitted `task claim` event — so provenance is preserved.\n * Just not in the FK column.\n *\n * Resolution order for the actor name (used as the log source):\n * 1. `actor` if explicitly passed.\n * 2. `$MU_AGENT_NAME`.\n * 3. The mux's current agent name (pane title on tmux).\n * 4. `$USER`.\n * 5. The literal string 'orchestrator'.\n *\n * Mutually exclusive with `agentName` (the two are alternative\n * answers to \"who's the actor for this claim?\"). Passing both is a\n * usage error.\n */\n self?: boolean;\n /**\n * Override the actor name used for the log source when `self: true`.\n * Ignored when `self: false`. Useful when the orchestrator wants to\n * attribute the work to a meaningful name rather than the pane\n * title (e.g. \"deploy-bot\" rather than \"pi-mu\").\n */\n actor?: string;\n /**\n * Claim an OPEN/parked task anyway. Without it, a parked task throws\n * TaskParkedError before any write: parking means \"keep out of the\n * scheduler\", so overriding it must be explicit.\n */\n force?: boolean;\n}\n\nexport interface ClaimResult {\n /** The agent now owning the task, or null when the claim was anonymous (--self). */\n ownerName: string | null;\n /** The actor recorded in the agent_logs event — the agent name for a\n * registered-worker claim, or the resolved actor for --self. */\n actorName: string;\n /** The previous owner (null if it was unowned). */\n previousOwnerName: string | null;\n /** The status BEFORE the claim; post-claim is IN_PROGRESS unless was CLOSED. */\n previousStatus: TaskStatus;\n /** The status AFTER the claim. */\n status: TaskStatus;\n}\n\n/**\n * Claim a task. Two modes:\n *\n * Worker claim (default):\n * Resolve an agent name from `opts.agentName`, else $MU_AGENT_NAME,\n * else the mux's current agent name (pane title on tmux). The name\n * MUST exist in the agents table (`tasks.owner_id` is an FK to\n * `agents.id`). Sets `owner_id` to that agent. This is what mu-spawned\n * workers do, and what `mu task claim --for <worker>` does for\n * orchestrator dispatch.\n *\n * Anonymous claim (--self):\n * Skip the name -> agents FK lookup entirely. Sets `owner_id = NULL`.\n * Records the actor in `agent_logs.source` instead. This is the\n * orchestrator-doing-direct-work path — the actor is logged but\n * not registered as a worker pane.\n *\n * Status side-effect: OPEN -> IN_PROGRESS; IN_PROGRESS / CLOSED unchanged.\n *\n * Concurrency: the worker-claim path uses a single-statement CAS UPDATE\n * with `WHERE owner IS NULL OR owner = ?` so two workers racing to\n * claim the same task can't both win. The anonymous path uses\n * `WHERE owner IS NULL` (anonymous claims don't 'own' the task in any\n * exclusive sense; if it's already owned by anyone, the anonymous claim\n * is a TaskAlreadyOwnedError just like a worker claim would be).\n */\nexport async function claimTask(\n db: Db,\n localId: string,\n opts: ClaimTaskOptions,\n): Promise<ClaimResult> {\n // claimTask is async (it shells out to tmux to resolve the caller's\n // identity), and withOpContext is deliberately sync — see its doc\n // comment. So set the context around the SYNCHRONOUS mutation legs\n // inside, not around the whole async function.\n return claimTaskImpl(db, localId, opts);\n}\n\nasync function claimTaskImpl(\n db: Db,\n localId: string,\n opts: ClaimTaskOptions,\n): Promise<ClaimResult> {\n if (opts.self === true && opts.agentName !== undefined) {\n throw new Error(\"claimTask: --self and --for are mutually exclusive\");\n }\n\n if (opts.self === true) {\n return claimSelf(db, localId, opts);\n }\n\n // ── Worker claim path (registered agent owns the task) ──\n const agentName = opts.agentName ?? (await resolveWorkerIdentity());\n if (!agentName) {\n throw new Error(\n \"claimTask: no agent name (pass opts.agentName, run inside an mu-spawned pane with $MU_AGENT_NAME or $TMUX_PANE set, or pass --self for an anonymous claim)\",\n );\n }\n\n // Resolve the claiming agent to its surrogate id within the agent's\n // workstream — defaults to opts.workstream (today's same-ws path),\n // or opts.agentWorkstream when the CLI dispatched across a\n // workstream boundary via a qualified `--for <ws>/<name>` ref\n // (task_claim_for_cross_workstream).\n //\n // The schema permits cross-workstream owner_id assignment (FK to\n // agents.id only); the per-workstream UNIQUE on agents.name is the\n // only reason this SELECT was scoped narrowly before. Bare-name\n // dispatch keeps that scope to honour today's behaviour; qualified\n // dispatch widens it to the named workstream so the agent resolves\n // there.\n const claimerWorkstream = opts.agentWorkstream ?? opts.workstream;\n const claimerRow = db\n .prepare(\n `SELECT a.id AS id\n FROM agents a JOIN workstreams ws ON ws.id = a.workstream_id\n WHERE a.name = ? AND ws.name = ?`,\n )\n .get(agentName, claimerWorkstream) as { id: number } | undefined;\n if (!claimerRow) {\n const paneIdFromEnv = opts.agentName === undefined ? (process.env.TMUX_PANE ?? null) : null;\n throw new ClaimerNotRegisteredError(agentName, paneIdFromEnv);\n }\n\n return withOpContext(db, { intent: \"task.claim\", actor: agentName, group: \"new\" }, () =>\n db\n .transaction(() => {\n // Resolve the task within opts.workstream. This locks the\n // (workstream, local_id) pair for the rest of the transaction.\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n assertNotParked(before, opts);\n\n const now = new Date().toISOString();\n const result = db\n .prepare(\n `UPDATE tasks\n SET owner_id = ?,\n status = CASE WHEN status = 'OPEN' THEN 'IN_PROGRESS' ELSE status END,\n -- SQLite evaluates every SET expression against the OLD\n -- row, so this CASE still sees the pre-claim status.\n substate = CASE WHEN status = 'OPEN' THEN 'active' ELSE substate END,\n updated_at = ?\n WHERE local_id = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)\n AND (owner_id IS NULL OR owner_id = ?)`,\n )\n .run(claimerRow.id, now, localId, opts.workstream, claimerRow.id);\n\n if (result.changes === 0) {\n throw new TaskAlreadyOwnedError(localId, before.ownerName ?? \"<unknown>\");\n }\n\n const after = getTask(db, localId, opts.workstream);\n if (!after) throw new Error(`claimTask: row missing after update: ${localId}`);\n // The CLAIM note's author: the dispatcher when the CLI passed one\n // (`--for`), else the claiming agent.\n recordEvidenceNote(db, localId, opts.workstream, \"CLAIM\", {\n ...opts,\n author: opts.author ?? agentName,\n });\n // No emitEvent: the UPDATE fired the capture trigger under\n // intent='task.claim' with actor=agentName (withOpContext above\n // put it in _op_ctx, and the trigger copies it into ops.actor).\n // The op payload carries the new owner_id, so the prose\n // `formatClaimEvent` breadcrumb — and the tab-delimited prefix\n // that existed only because prose had to be re-parsed — are both\n // redundant. `lastClaimActor` now reads ops.actor directly.\n return {\n ownerName: agentName,\n actorName: agentName,\n previousOwnerName: before.ownerName,\n previousStatus: before.status,\n status: after.status,\n };\n })\n .immediate(),\n );\n}\n\n/** The parked-claim guard shared by the worker and --self paths. The\n * UPDATE's `status = 'OPEN'` CASE already maps parked → active, so a\n * forced claim needs nothing else. */\nfunction assertNotParked(\n task: { name: string; status: TaskStatus; substate: string; workstreamName: string },\n opts: ClaimTaskOptions,\n): void {\n if (task.status === \"OPEN\" && task.substate === \"parked\" && opts.force !== true) {\n throw new TaskParkedError(task.name, task.workstreamName);\n }\n if (task.status === \"OPEN\" && task.substate === \"triage\" && opts.force !== true) {\n throw new TaskInTriageError(task.name, task.workstreamName);\n }\n}\n\n/**\n * Resolve the current actor's identity for attribution in task notes,\n * --self claims, and any other write that wants 'who did this?'.\n *\n * Resolution order:\n * 1. $MU_AGENT_NAME env var (set by mu spawnAgent on every managed\n * pane; surfaced from the f3d4bdd commit). Authoritative when\n * present — you're inside a mu-spawned worker, no ambiguity.\n * 2. tmux pane title (the pane-title identity step). Works\n * when running inside any pane mu manages OR adopted.\n * 3. $USER (when running outside tmux entirely).\n * 4. The literal 'orchestrator' as a last-resort default.\n *\n * Why prefer env over pane title: pane titles are a tmux-server-wide\n * resource that anything can rewrite. The env var is set per-pane at\n * spawn time and is unforgeable from outside without explicit\n * `--actor` override. Pane title is the only identity available for\n * adopted panes that didn't go through mu's spawn path.\n */\nexport async function resolveActorIdentity(): Promise<string> {\n const worker = await resolveWorkerIdentity();\n if (worker !== undefined) return worker;\n const user = process.env.USER;\n if (user !== undefined && user !== \"\") return user;\n return \"orchestrator\";\n}\n\n/**\n * The AGENT-identity half of the ladder, shared by `resolveActorIdentity`\n * and the bare worker-claim path. Returns undefined when the caller\n * isn't identifiable as a specific agent — the two callers disagree\n * about what to do then, which is why this stops short rather than\n * falling through to `$USER`:\n *\n * - `resolveActorIdentity` continues to `$USER` / 'orchestrator',\n * because an anonymous claim only needs an attribution string.\n * - the worker-claim path THROWS, because `tasks.owner_id` is a real\n * FK to `agents.id` and '$USER' is not an agent. Falling back there\n * would turn a clear \"who are you?\" error into a confusing\n * ClaimerNotRegisteredError naming the operator's unix login.\n *\n * Rungs:\n * 1. `$MU_AGENT_NAME` — injected into every pane by spawnAgent.\n * Backend-independent, and the only rung that works on a mux with\n * no mu-writable pane title.\n * 2. The mux backend's `currentAgentName()` — pane title on tmux.\n * Parses 'name · status · task' back to the name token, which\n * matters because composeAgentTitle decorates titles and the FK is\n * keyed on the bare `agents.name`.\n *\n * Why env before title: a pane title is a mux-server-wide resource that\n * any process can rewrite, while the env var is set once at spawn and\n * is unforgeable from outside the pane. The title rung survives only\n * because ADOPTED panes never went through spawn and have nothing else.\n */\nexport async function resolveWorkerIdentity(): Promise<string | undefined> {\n const muAgent = process.env.MU_AGENT_NAME;\n if (muAgent !== undefined && muAgent !== \"\") return muAgent;\n // Best-effort: identity resolution must never be the thing that fails\n // a verb. A non-interactive command on a box with no multiplexer wants an\n // attribution string, not a NoMultiplexerError — and the caller\n // already has $USER / 'orchestrator' rungs below this one.\n try {\n const paneTitle = await (await activeMux()).currentAgentName();\n if (paneTitle !== undefined && paneTitle !== \"\") return paneTitle;\n } catch {\n // No reachable mux, or it could not answer. Fall through.\n }\n return undefined;\n}\n\nasync function claimSelf(db: Db, localId: string, opts: ClaimTaskOptions): Promise<ClaimResult> {\n const actor =\n opts.actor !== undefined && opts.actor !== \"\" ? opts.actor : await resolveActorIdentity();\n return withOpContext(db, { intent: \"task.claim\", actor, group: \"new\" }, () =>\n db\n .transaction(() => {\n // Scope by the operator's workstream so a same-named task\n // elsewhere can't be self-claimed by accident.\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n assertNotParked(before, opts);\n\n // Anonymous claim: owner stays NULL, status flips OPEN -> IN_PROGRESS.\n // Gate on `owner_id IS NULL` so an in-flight worker claim can't be\n // silently overwritten.\n const now = new Date().toISOString();\n const result = db\n .prepare(\n `UPDATE tasks\n SET status = CASE WHEN status = 'OPEN' THEN 'IN_PROGRESS' ELSE status END,\n substate = CASE WHEN status = 'OPEN' THEN 'active' ELSE substate END,\n updated_at = ?\n WHERE local_id = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)\n AND owner_id IS NULL`,\n )\n .run(now, localId, before.workstreamName);\n\n if (result.changes === 0) {\n // Task exists but is already owned (by someone). Mirror the\n // worker-path error so callers can pattern-match consistently.\n throw new TaskAlreadyOwnedError(localId, before.ownerName ?? \"<unknown>\");\n }\n\n const after = getTask(db, localId, before.workstreamName);\n if (!after) throw new Error(`claimTask: row missing after update: ${localId}`);\n recordEvidenceNote(db, localId, before.workstreamName, \"CLAIM\", {\n ...opts,\n author: opts.author ?? actor,\n });\n // No emitEvent. This is the interesting case: the `--self` path\n // leaves tasks.owner_id NULL deliberately, so the op PAYLOAD\n // cannot name the actor — but ops.actor can and does, because\n // withOpContext seeded _op_ctx with it. That is precisely what\n // `lastClaimActor` needs, and reading a column beats\n // prefix-matching prose (review_code_last_claim_actor_brittle).\n return {\n ownerName: null,\n actorName: actor,\n previousOwnerName: before.ownerName,\n previousStatus: before.status,\n status: after.status,\n };\n })\n .immediate(),\n );\n}\n","// mu — waitForTasks: block until tasks reach a target status.\n//\n// The orchestrator pattern: dispatch N workers via mu task claim --for,\n// then wait until they're all done before reviewing/merging.\n//\n// Pre-existing alternatives + why this verb exists:\n//\n// awk pipe over `mu log --tail`: works for ONE event but the\n// awk script becomes stateful (tracking 'which of N have closed?')\n// for multi-task waits. Bad shape for SKILL examples.\n//\n// Implementation:\n//\n// 1. Initial check — if the wait condition is already satisfied,\n// exit immediately. No subscription needed.\n// 2. Otherwise, poll the tasks table every pollMs. Same cadence as\n// mu log --tail (default 1000ms). We don't subscribe to\n// agent_logs because (a) we'd still need to re-query tasks to\n// learn the current status, (b) some status changes happen via\n// mu sql which doesn't emit events, and (c) the polling cost is\n// one indexed SELECT every second — cheaper than parsing the\n// log stream.\n// 3. Exit on success (all/any reached) OR timeout. Caller maps\n// timeout to exit code 5.\n//\n// Extracted from src/tasks.ts as part of refactor_split_large_src_files.\n\nimport type { StateReading } from \"../agent-state.js\";\nimport type { Db } from \"../db.js\";\nimport { emitEvent } from \"../logs.js\";\nimport { StallDetectedDuringWaitError, stallWaitHint, TaskNotFoundError } from \"./errors.js\";\nimport { getTask, getTaskOwner } from \"./queries.js\";\nimport { DEFAULT_SUBSTATE, type TaskStatus, type TaskSubstate } from \"./status.js\";\n\n// ─── Test seams: poll-sleep + poll counter + stuck-warn writer ─────────\n//\n// Mirror src/tmux.ts's setSleepForTests pattern. Default sleep is a real\n// setTimeout; tests can swap in an instant + counted version to assert\n// poll cadence (the bug fixed alongside this hook silently sleeps a full\n// pollMs past the deadline when pollMs > timeoutMs — see\n// test/tasks-wait.integration.test.ts 'clamped sleep' regression case).\n//\n// The stuck-warn writer is the second seam: agent_attention_required\n// (named agent_close_discipline_gap until it was found to presume one\n// of three causes) added a per-poll \"this task is IN_PROGRESS but its\n// owner is needs_input for too long\" warning emitted to stderr; tests\n// intercept it via setWaitStuckWarnForTests so they can assert\n// exactly-once dedupe without scraping process.stderr.\n\nlet currentWaitSleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms));\nlet pollCount = 0;\n\n/** Default `--stuck-after`: how long an IN_PROGRESS task's owner may sit\n * in needs_input before the wait reports it. Short because a pi worker\n * that settles still owning a task is already nudged once to close it;\n * still waiting after that means it needs the orchestrator. */\nexport const DEFAULT_STUCK_AFTER_MS = 120_000;\n\n/** Default `--stuck-after` for an owner read over its control socket.\n * ctl's idle is exact (pi's agent_settled) and arrives only after the\n * close nudge's continuation has run (the nudge fires in\n * agent_before_settle, before the settle), so an idle owner still\n * holding the task has already declined to close it. The grace only\n * covers an orchestrator `send` racing the settle. */\nexport const DEFAULT_CTL_STUCK_AFTER_MS = 5_000;\nconst defaultStuckWarn: (msg: string) => void = (msg) => {\n process.stderr.write(msg);\n};\nlet currentStuckWarn: (msg: string) => void = defaultStuckWarn;\n\nexport function setWaitSleepForTests(\n impl: ((ms: number) => Promise<void>) | undefined,\n): (ms: number) => Promise<void> {\n const previous = currentWaitSleep;\n currentWaitSleep = impl ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n return previous;\n}\n\n/** Test seam: swap the stderr writer used by the stuck-task warning so\n * unit tests can capture warnings without spying on process.stderr. */\nexport function setWaitStuckWarnForTests(\n impl: ((msg: string) => void) | undefined,\n): (msg: string) => void {\n const previous = currentStuckWarn;\n currentStuckWarn = impl ?? defaultStuckWarn;\n return previous;\n}\n\n/** Total number of polls performed across all `waitForTasks` calls in this\n * process. Tests typically reset before exercising and read after. */\nexport function getWaitPollCount(): number {\n return pollCount;\n}\n\nexport function resetWaitPollCount(): void {\n pollCount = 0;\n}\n\n/**\n * Human-readable age for the attention warning: `45s`, `5m`, `2h`.\n *\n * Deliberately NOT `relTime` from src/cli/format.ts — that module\n * pulls picocolors and cli-table3, and this one stays dep-free so the\n * SDK can be imported without the CLI's render layer. The duplication\n * is three lines of arithmetic against a shared import that would\n * invert the layering.\n *\n * Raw milliseconds were the old wording and read badly: a worker five\n * minutes into a question got \"(>= 1000ms ...)\" because the message\n * quoted the THRESHOLD, not the age.\n */\nexport function formatStallAge(ms: number): string {\n const sec = Math.max(0, Math.round(ms / 1000));\n if (sec < 60) return `${sec}s`;\n const min = Math.floor(sec / 60);\n if (min < 60) return `${min}m`;\n return `${Math.floor(min / 60)}h`;\n}\n\n/** A single task ref the wait verb is watching. Cross-workstream\n * waits arrive as a heterogeneous list of (workstream, name) pairs;\n * the legacy single-workstream call passes the same workstream on\n * every ref. task_wait_cross_workstream. */\nexport interface TaskWaitRef {\n /** The workstream the task lives in. Each ref carries its own so\n * the SDK doesn't need a single \"the workstream\" — cross-ws waits\n * pass refs from multiple workstreams in one call. */\n workstreamName: string;\n /** The task's per-workstream-unique local id. */\n name: string;\n}\n\nexport interface TaskWaitOptions {\n /** Target status. Default 'CLOSED'. */\n status?: TaskStatus;\n /** When true, succeed as soon as ONE listed task reaches the target.\n * Default false: every listed task must reach the target. */\n any?: boolean;\n /** Maximum time to wait, in milliseconds. Default 600_000 (10 min).\n * Pass 0 to wait forever. */\n timeoutMs?: number;\n /** Polling interval. Default 1000ms; overridable for tests. */\n pollMs?: number;\n /** Workstream context applied to bare-string ids. Required when the\n * caller passes `string[]`; ignored when the caller passes\n * `TaskWaitRef[]` (each ref carries its own ws). The legacy\n * single-ws SDK call site keeps its today's shape; the cross-ws\n * callers (CLI verb) pass `TaskWaitRef[]` and omit `workstream`.\n * task_wait_cross_workstream. */\n workstream?: string;\n /** Emit a yellow STUCK warning to stderr (once per task per wait call)\n * when an IN_PROGRESS task's owner has been in `needs_input` for at\n * least this many milliseconds since the agent row's last update.\n * Default `DEFAULT_STUCK_AFTER_MS` (2 min). Pass 0 to disable.\n *\n * Surfaced by agent_attention_required: a worker sitting in\n * needs_input leaves wait blocked indefinitely. The cause may be a\n * finish-without-close, a question awaiting an answer, or a prompt\n * — the predicate cannot tell them apart, so the warning reports\n * the observation and points at `mu agent read`. Default action is\n * observation-only (wait keeps polling); `onStall: 'exit'` makes it\n * terminal. */\n stuckAfterMs?: number;\n /** `stuckAfterMs` for an owner whose reading came from a working\n * control socket (source ctl, ctl ok). Default\n * `DEFAULT_CTL_STUCK_AFTER_MS`. `stuckAfterMs: 0` disables both. */\n ctlStuckAfterMs?: number;\n /** What to do when the `--stuck-after` predicate fires on a watched\n * task. `'warn'` (default) = today's behaviour: yellow STUCK line\n * to stderr (deduped per task per wait call) + corroborating\n * `kind='event'` agent_logs row; wait keeps polling. `'exit'` =\n * same emit + persist, but THEN throw `StallDetectedDuringWaitError`\n * so the CLI wrapper exits 7 (STALL_DETECTED). The exit-action is\n * the unattended-orchestrator escape: a wrapping policy can branch\n * on 7 (idle, ambiguous — operator decides poke vs release) vs 6\n * (dead pane, unambiguous — re-dispatch).\n *\n * Carve-out (lives at the call site, not here): the CLI passes\n * `'exit'` only when the wait target is CLOSED — mirrors exit-6's\n * reaper-flip suppression. With `--status OPEN` the worker reaching\n * needs_input might BE the success path. See\n * task_wait_stall_action_flag. */\n onStall?: \"warn\" | \"exit\";\n /** Read an owner's current runtime state for attention detection.\n * Without this hook, SDK waits never infer a stall from stored data. */\n readOwnerState?: (owner: {\n name: string;\n workstreamName: string;\n }) => Promise<StateReading | null>;\n /** Optional async hook run BEFORE every snapshot (initial + each\n * poll iteration). The CLI uses this to reconcile the workstream\n * each tick (reaper flips IN_PROGRESS → OPEN for dead-pane\n * workers) and to throw a typed error when a reaper-flip on a\n * watched task should abandon the wait — see\n * task_wait_reconcile_dead_panes. Throwing from `beforePoll`\n * propagates out of `waitForTasks` unchanged.\n *\n * Kept as a generic seam (not a `--reconcile`-shaped option) so\n * the SDK module stays free of tmux/reconcile imports — that\n * layering belongs above the SDK in the CLI wrapper. */\n beforePoll?: () => Promise<void>;\n}\n\nexport interface TaskWaitTaskState {\n /** The workstream this task lives in. Cross-workstream waits\n * return a mixed list; the workstream is part of identity.\n * task_wait_cross_workstream. */\n workstreamName: string;\n /** The task's per-workstream-unique name. */\n name: string;\n /** Current status (at the moment we exit). */\n status: TaskStatus;\n /** Current substate. A CLOSED ref that is not `done` has no deliverable. */\n substate: TaskSubstate;\n /** Owner at exit time (NULL when unowned, after release, or after\n * the reaper flipped IN_PROGRESS → OPEN due to a dead pane). */\n owner: string | null;\n /** True when this task's status equals the target. */\n reachedTarget: boolean;\n /** True when the task is IN_PROGRESS, owned by a registered agent\n * whose detected status is `needs_input` for >= `stuckAfterMs`.\n * Surfaces agent_attention_required: the worker may have finished\n * without closing, be waiting on an answer, or be sitting at a\n * prompt — this flag does not distinguish them, so a consumer\n * should read the pane. Backwards-compatible signal — callers\n * ignoring it see no behaviour change. */\n stuck: boolean;\n}\n\nexport interface TaskWaitResult {\n /** Per-task state at exit time. Same length and order as the input\n * list. The caller derives all-reached / any-reached / elapsed\n * from this list (count `r.reachedTarget`) and from its own\n * startedAt clock — keeping the SDK return minimal. */\n refs: TaskWaitTaskState[];\n /** True when we exited because of the timeout, not because the wait\n * condition was met. Refs that did reach the target are still\n * reflected in `refs[i].reachedTarget` on partial-progress timeout. */\n timedOut: boolean;\n}\n\n/**\n * Block until a set of tasks reaches `opts.status` (default CLOSED).\n * Returns a result describing the final state — the caller decides\n * whether to treat partial-progress timeouts as success or failure\n * (the CLI maps a clean exit to 0, a timeout to 5).\n *\n * Pre-flight: every task in `localIds` MUST exist; missing ones throw\n * TaskNotFoundError before any waiting begins. This is loud-fail by\n * design — a typo'd id silently waiting forever is the worst-case UX.\n */\nexport async function waitForTasks(\n db: Db,\n input: readonly TaskWaitRef[] | readonly string[],\n opts: TaskWaitOptions,\n): Promise<TaskWaitResult> {\n if (input.length === 0) {\n throw new Error(\"waitForTasks: refs must be non-empty\");\n }\n // Normalise to TaskWaitRef[]. The string[] shape preserves today's\n // single-workstream SDK contract (callers pass `workstream` on\n // opts); the TaskWaitRef[] shape carries its own ws per ref so\n // cross-workstream waits don't need a sentinel.\n const refs: readonly TaskWaitRef[] = input.map((entry) => {\n if (typeof entry === \"string\") {\n if (opts.workstream === undefined)\n throw new Error(\n \"waitForTasks: string ids require opts.workstream; pass TaskWaitRef[] for cross-workstream waits\",\n );\n return { workstreamName: opts.workstream, name: entry };\n }\n return entry;\n });\n const target: TaskStatus = opts.status ?? \"CLOSED\";\n const wantAny = opts.any === true;\n const timeoutMs = opts.timeoutMs ?? 600_000;\n const pollMs = opts.pollMs ?? 1000;\n const stuckAfterMs = opts.stuckAfterMs ?? DEFAULT_STUCK_AFTER_MS;\n const ctlStuckAfterMs = opts.ctlStuckAfterMs ?? DEFAULT_CTL_STUCK_AFTER_MS;\n const onStall: \"warn\" | \"exit\" = opts.onStall ?? \"warn\";\n const deadline = timeoutMs > 0 ? Date.now() + timeoutMs : Number.POSITIVE_INFINITY;\n\n // Pre-flight: every ref must exist in its own workstream scope.\n // Cross-workstream waits validate each ref against its declared ws\n // — a typo in the workstream half of `<ws>/<name>` should land here\n // with a clear TaskNotFoundError, not silently wait forever.\n for (const ref of refs) {\n if (getTask(db, ref.name, ref.workstreamName) === undefined)\n throw new TaskNotFoundError(`${ref.workstreamName}/${ref.name}`);\n }\n\n // Per-(ws,name) dedupe: emit the STUCK warning at most ONCE per wait\n // call, not once per poll cycle. Operators don't want their stderr\n // filled with the same yellow line every second; one nudge is enough.\n const stuckWarned = new Set<string>();\n const refKey = (ref: TaskWaitRef): string => `${ref.workstreamName}/${ref.name}`;\n // Owners whose control socket is broken: first-seen time + reason.\n const ctlBrokenSince = new Map<string, number>();\n const stuckLabel = new Map<string, string>();\n // Owners whose reading carried a ctl link: they run pi, so the stall\n // error can point at `mu agent abort`.\n const ctlOwners = new Set<string>();\n // The latest ctl run count per owner, for the stall hints.\n const ownerRuns = new Map<string, number>();\n\n const stuckAgeMs = async (\n status: TaskStatus,\n owner: { name: string; workstreamName: string } | undefined,\n ): Promise<number | null> => {\n if (\n stuckAfterMs <= 0 ||\n status !== \"IN_PROGRESS\" ||\n owner === undefined ||\n opts.readOwnerState === undefined\n )\n return null;\n const reading = await opts.readOwnerState(owner);\n const ownerKey = `${owner.workstreamName}/${owner.name}`;\n if (reading?.ctl !== undefined) ctlOwners.add(ownerKey);\n if (reading?.runs !== undefined) ownerRuns.set(ownerKey, reading.runs);\n else ownerRuns.delete(ownerKey);\n // A pi owner whose control socket does not answer (pane still\n // alive) needs attention too: nothing will report it settling.\n // Its age runs from when this wait first saw the socket broken.\n if (reading?.alive === true && (reading.ctl === \"missing\" || reading.ctl === \"refused\")) {\n const firstSeen = ctlBrokenSince.get(ownerKey) ?? Date.now();\n ctlBrokenSince.set(ownerKey, firstSeen);\n stuckLabel.set(ownerKey, reading.reason ?? `ctl ${reading.ctl}`);\n const ageMs = Date.now() - firstSeen;\n return ageMs >= stuckAfterMs ? ageMs : null;\n }\n ctlBrokenSince.delete(ownerKey);\n stuckLabel.delete(ownerKey);\n if (reading?.state !== \"needs_input\" || reading.since === null) return null;\n const viaCtl = reading.source === \"ctl\" && reading.ctl === \"ok\";\n if (viaCtl && reading.pending === true) return null;\n const ageMs = Date.now() - reading.since;\n return ageMs >= (viaCtl ? ctlStuckAfterMs : stuckAfterMs) ? ageMs : null;\n };\n\n /** Read current state of all tasks; returns the result shape. */\n const snapshot = async (): Promise<TaskWaitResult> => {\n const refStates: TaskWaitTaskState[] = [];\n for (const ref of refs) {\n const row = getTask(db, ref.name, ref.workstreamName);\n // Defensive: if a task was deleted mid-wait, treat as 'never\n // reached' for EVERY target, OPEN included (reachedTarget below\n // requires the row). Not the same as TaskNotFoundError pre-flight:\n // deletion mid-wait shouldn't crash the wait; it's a legitimate\n // state change.\n const status = (row?.status ?? \"OPEN\") as TaskStatus;\n const substate = row?.substate ?? DEFAULT_SUBSTATE[status];\n // The owner's own workstream, not the task's: a cross-workstream\n // owner (`task claim --for <ws>/<agent>`) is read, keyed and\n // named under the workstream it lives in.\n const ownerRef =\n row === undefined ? undefined : getTaskOwner(db, ref.name, ref.workstreamName);\n const owner = ownerRef?.name ?? row?.ownerName ?? null;\n const ownerWs = ownerRef?.workstreamName ?? ref.workstreamName;\n const ageMs = await stuckAgeMs(status, ownerRef);\n const stuck = ageMs !== null;\n const key = refKey(ref);\n if (ageMs !== null && !stuckWarned.has(key)) {\n stuckWarned.add(key);\n const ageSecs = Math.round(ageMs / 1000);\n // State the OBSERVATION, not a cause. The predicate above only\n // knows \"needs_input for >= N\" — it cannot tell a worker that\n // finished without closing from one waiting on an answer from\n // one sitting at a prompt, and those need opposite responses.\n // The old text asserted the first (\"likely committed but\n // skipped mu task close\"), which sent at least one operator\n // hunting for a commit to merge while the worker was waiting\n // on a design decision. Workers asking questions is DESIRABLE;\n // the tooling must not frame it as negligence.\n //\n // The indented lines are the remedy: a pi owner's last answer\n // (wait --after-runs) when its socket reported runs, then\n // `mu agent read`, the next move in all three cases, because a\n // question, dialog, or crash is in the pane, not in the task row.\n //\n // Yellow ANSI escape inline (no picocolors import — keeps the\n // SDK module dep-free; the CLI layer already pulls picocolors).\n // Prefixed `mu task wait:` so log greppers can target it, and\n // cross-ws waits carry the qualified `<ws>/<name>`.\n // A pi owner with a settled run also gets its last answer\n // exactly (wait --after-runs), ahead of the pane read.\n const ownerBit = owner ?? \"<none>\";\n const runs = ownerRuns.get(`${ownerWs}/${ownerBit}`);\n const stateBit = stuckLabel.get(`${ownerWs}/${ownerBit}`) ?? \"needs_input\";\n const waitLine = stallWaitHint(ownerBit, ownerWs, runs)\n .map((s) => ` ${s.command}\\n`)\n .join(\"\");\n currentStuckWarn(\n `\\x1b[33mmu task wait: ${key} needs attention — owner=${ownerBit} has been in ` +\n `${stateBit} for ${formatStallAge(ageMs)}. It may have finished without closing, ` +\n `be waiting on an answer, or be sitting at a prompt.\\x1b[0m\\n` +\n waitLine +\n ` mu agent read ${ownerBit} -w ${ownerWs} --lines 60\\n`,\n );\n // Persist a corroborating kind='event' row so other consumers\n // (mu state, mu log --kind event, dashboards) see the same\n // signal that the new derived `idle` flag surfaces in the\n // agents table. The stderr warning stays — observation-only,\n // operator decides recovery. See idle_assigned_agent_detection.\n // Pure OBSERVATION: nothing is mutated, so no trigger could\n // ever see this. The emit is the only record.\n emitEvent(\n db,\n ref.workstreamName,\n \"agent.stall\",\n `agent stalled ${owner ?? \"<none>\"} owns ${ref.name} for ${ageSecs}s`,\n owner ?? \"system\",\n );\n // task_wait_stall_action_flag: with `--on-stall exit` (the\n // unattended-orchestrator escape), the same emit + persist\n // happens but we then throw — the CLI's classifyError maps\n // this to exit code 7 (STALL_DETECTED). The carve-out\n // (target=CLOSED only) lives at the call site: the CLI\n // wrapper passes `onStall: 'exit'` only when target=CLOSED.\n // Precedence vs exit-6 (dead pane): the reaper-flip check\n // throws inside `beforePoll` BEFORE snapshot() runs, so a\n // watched task that's both reaper-flipped AND stale never\n // reaches this branch (status would already be OPEN).\n if (onStall === \"exit\") {\n throw new StallDetectedDuringWaitError(\n ref.name,\n owner,\n ref.workstreamName,\n ageSecs,\n ctlOwners.has(`${ownerWs}/${owner ?? \"\"}`),\n runs,\n ownerWs,\n );\n }\n }\n refStates.push({\n workstreamName: ref.workstreamName,\n name: ref.name,\n status,\n substate,\n owner,\n reachedTarget: row !== undefined && status === target,\n stuck,\n });\n }\n return {\n refs: refStates,\n timedOut: false,\n };\n };\n\n /** Has the wait condition been met? */\n const isDone = (snap: TaskWaitResult): boolean => {\n const reached = snap.refs.filter((r) => r.reachedTarget).length;\n return wantAny ? reached > 0 : reached === snap.refs.length;\n };\n\n // Initial check: maybe we're already done. Run beforePoll first so\n // the CLI's per-poll reconcile (task_wait_reconcile_dead_panes) runs\n // even on the immediate-exit path — a dead-pane worker that died\n // BEFORE the operator typed `mu task wait` should still fail fast.\n if (opts.beforePoll) await opts.beforePoll();\n let snap = await snapshot();\n if (isDone(snap)) return snap;\n\n // Poll loop.\n //\n // Sleep is clamped to `min(pollMs, deadline - now)` so the function\n // returns within `timeoutMs + small slack`, never `pollMs` later.\n // Without the clamp, `pollMs=10000, timeoutMs=100` sleeps a full 10s\n // before noticing the deadline expired. When the clamp goes <= 0 we\n // skip the sleep entirely and re-snapshot before bailing on the\n // timeout — gives the wait one last chance at a winning state right\n // at the deadline boundary, and avoids passing 0 / negatives to\n // setTimeout (which has implementation-defined behaviour).\n for (;;) {\n const now = Date.now();\n if (now >= deadline) {\n return { ...snap, timedOut: true };\n }\n const sleepMs =\n deadline === Number.POSITIVE_INFINITY ? pollMs : Math.min(pollMs, deadline - now);\n if (sleepMs > 0) {\n await currentWaitSleep(sleepMs);\n }\n pollCount += 1;\n if (opts.beforePoll) await opts.beforePoll();\n snap = await snapshot();\n if (isDone(snap)) return snap;\n }\n}\n","// mu — abort: stop a pi agent's current turn through its control socket.\n//\n// status → abort → wait(afterRuns). The wait keys on the run counter the\n// extension bumps on agent_settled, so an abort that settles before the\n// wait connects still resolves at once. An agent that is not busy gets no\n// abort at all: the verb is idempotent.\n//\n// interruptAgent (`mu agent send --interrupt`) is abort + send as one\n// ctl op; against an extension that predates it, abortAgent then send.\n\nimport { getAgent } from \"../agents.js\";\nimport { CtlUnknownOpError, ctlRequest } from \"../ctl/client.js\";\nimport type { CtlState } from \"../ctl/protocol.js\";\nimport type { Db } from \"../db.js\";\nimport { AgentAbortNeedsCtlError, AgentAbortTimeoutError, AgentNotFoundError } from \"./errors.js\";\nimport {\n agentCtlSocket,\n ctlFailure,\n expectsCtl,\n recordSend,\n ctlRequestFor as request,\n} from \"./transport.js\";\n\nexport const DEFAULT_ABORT_TIMEOUT_MS = 30_000;\n\nexport type AbortResult = {\n agent: string;\n workstream: string;\n before: CtlState;\n after: CtlState;\n /** True when pi settled (or was never busy). */\n settled: boolean;\n /** False when the agent was not busy, so no abort was sent. */\n aborted: boolean;\n /** pi had queued messages before the abort (pi returns them to the editor). */\n pending: boolean;\n elapsedMs: number;\n};\n\nexport type AbortAgentOptions = {\n workstream: string;\n timeoutMs?: number;\n /** Control socket path. Default: derived from the agent's identity. */\n socket?: string;\n};\n\nexport async function abortAgent(\n db: Db,\n name: string,\n opts: AbortAgentOptions,\n): Promise<AbortResult> {\n const agent = getAgent(db, name, opts.workstream);\n if (!agent) throw new AgentNotFoundError(name, opts.workstream);\n const sock = opts.socket ?? agentCtlSocket(db, agent);\n if (!expectsCtl(agent, sock)) throw new AgentAbortNeedsCtlError(name, opts.workstream, agent.cli);\n const timeoutMs = opts.timeoutMs ?? DEFAULT_ABORT_TIMEOUT_MS;\n const started = Date.now();\n const base = { agent: name, workstream: opts.workstream };\n\n const st = await request(agent, sock, { op: \"status\" });\n if (!st.ok) throw new Error(`control socket refused status: ${st.error}`);\n const before = st.state ?? \"idle\";\n const pending = st.pending ?? false;\n if (before !== \"busy\") {\n return {\n ...base,\n before,\n after: before,\n settled: true,\n aborted: false,\n pending,\n elapsedMs: Date.now() - started,\n };\n }\n\n const ab = await request(agent, sock, { op: \"abort\" });\n if (!ab.ok) throw new Error(`control socket refused the abort: ${ab.error}`);\n\n const w = await request(agent, sock, { op: \"wait\", afterRuns: st.runs ?? 0, timeoutMs });\n if (!w.ok) {\n if (w.error === \"timeout\") throw new AgentAbortTimeoutError(name, opts.workstream, timeoutMs);\n throw new Error(`control socket refused the wait: ${w.error}`);\n }\n return {\n ...base,\n before,\n after: w.state ?? \"idle\",\n settled: true,\n aborted: true,\n pending,\n elapsedMs: Date.now() - started,\n };\n}\n\nexport type InterruptResult = {\n /** pi was busy, so its turn was aborted before the send. */\n wasBusy: boolean;\n /** pi had queued messages before the abort; pi put them back in the pane's editor. */\n pending: boolean;\n /**\n * pi's run count after the aborted run settled and before the send: the\n * `mu agent wait --after-runs` baseline for the run the text starts.\n */\n runs?: number;\n /** The extension predates the interrupt op: mu ran abort, then send. */\n fallback: boolean;\n};\n\n/**\n * Make a pi agent act on `text` now: abort a running turn, wait for pi to\n * settle (AgentAbortTimeoutError after timeoutMs, text not sent), then\n * send `text` as a new run. Never pastes: non-pi agents get\n * AgentAbortNeedsCtlError.\n */\nexport async function interruptAgent(\n db: Db,\n name: string,\n text: string,\n opts: AbortAgentOptions,\n): Promise<InterruptResult> {\n const r = await interruptAgentImpl(db, name, text, opts);\n recordSend(db, { name, workstreamName: opts.workstream }, \"interrupt\", Buffer.byteLength(text), {\n transport: \"ctl\",\n wasBusy: r.wasBusy,\n });\n return r;\n}\n\nasync function interruptAgentImpl(\n db: Db,\n name: string,\n text: string,\n opts: AbortAgentOptions,\n): Promise<InterruptResult> {\n const agent = getAgent(db, name, opts.workstream);\n if (!agent) throw new AgentNotFoundError(name, opts.workstream);\n const sock = opts.socket ?? agentCtlSocket(db, agent);\n if (!expectsCtl(agent, sock)) throw new AgentAbortNeedsCtlError(name, opts.workstream, agent.cli);\n const timeoutMs = opts.timeoutMs ?? DEFAULT_ABORT_TIMEOUT_MS;\n let r: Awaited<ReturnType<typeof ctlRequest>>;\n try {\n r = await ctlRequest(sock, { op: \"interrupt\", text, timeoutMs });\n } catch (e) {\n if (!(e instanceof CtlUnknownOpError)) throw await ctlFailure(agent, sock, e);\n // An extension loaded before the op: same steps, composed here.\n const ab = await abortAgent(db, name, { ...opts, socket: sock });\n const sent = await request(agent, sock, { op: \"send\", text });\n if (!sent.ok) throw new Error(`control socket refused the send: ${sent.error}`);\n return {\n wasBusy: ab.aborted,\n pending: ab.pending,\n ...(sent.runs !== undefined ? { runs: sent.runs } : {}),\n fallback: true,\n };\n }\n if (!r.ok) {\n if (r.error === \"timeout\") throw new AgentAbortTimeoutError(name, opts.workstream, timeoutMs);\n throw new Error(`control socket refused the interrupt: ${r.error}`);\n }\n return {\n wasBusy: r.wasBusy ?? false,\n pending: r.pending ?? false,\n ...(r.runs !== undefined ? { runs: r.runs } : {}),\n fallback: false,\n };\n}\n","// mu — adoptAgent: register an existing tmux pane as a managed agent.\n//\n// The inverse of `mu agent spawn` — instead of creating the pane,\n// adoptAgent hooks an already-existing pane into mu's registry.\n//\n// Two flavours of resolution:\n// - by paneId ('%15', 'w1:p2') : look up directly via the mux\n// - by paneTitle ('worker-1') : scan the session's panes for that title\n//\n// Common cases for adopt:\n// - operator manually started a CLI in a pane before installing mu\n// - mu agent close was followed by re-attaching the pane via tmux\n// - migrating from a previous orchestrator\n//\n// Extracted from src/agents.ts as part of refactor_split_large_src_files.\n\nimport {\n type AgentRow,\n getAgent,\n getAgentByPane,\n insertAgent,\n isValidAgentName,\n} from \"../agents.js\";\nimport { ctlProbe } from \"../ctl/client.js\";\nimport type { Db } from \"../db.js\";\nimport { emitEvent } from \"../logs.js\";\nimport { activeMux, type MuxPane, PaneNotFoundError, parseAgentNameFromTitle } from \"../mux.js\";\nimport { AgentExistsError, PaneNotInSessionError } from \"./errors.js\";\nimport { probeToSpawnCtl, type SpawnCtl } from \"./spawn.js\";\nimport { agentCtlSocket, expectsCtl } from \"./transport.js\";\n\n/** Budget for adopt's one-shot control-socket probe. */\nconst ADOPT_CTL_PROBE_MS = 1000;\n\nexport interface AdoptAgentOptions {\n /** Mux pane id (tmux '%15', herdr 'w1:p2'). Must already exist. */\n paneId: string;\n /** Workstream to adopt the pane into. The pane MUST be in the\n * matching tmux session (`mu-<workstream>`); cross-session adopt is\n * rejected. */\n workstream: string;\n /** Override the pane's title with this name. When omitted, the pane's\n * current title becomes the agent name (zero-config adoption). */\n name?: string;\n /** Defaults to 'pi' via the schema DEFAULT. */\n cli?: string;\n /** 'full-access' (default) or 'read-only'. */\n role?: string;\n /** Override the tmux session lookup. Defaults to `mu-<workstream>`. */\n tmuxSession?: string;\n}\n\nexport interface AdoptAgentResult {\n agent: AgentRow;\n /** True when the pane already had a matching agents row — the call\n * was a no-op (idempotent). */\n alreadyAdopted: boolean;\n /** The pane title before adopt, or null if the pane had no title. */\n previousTitle: string | null;\n /** The title the pane was set to (== agent.name post-adopt). Equal to\n * previousTitle when no retitle happened. */\n paneTitleSetTo: string;\n /** One probe of the derived control socket; \"skipped\" for non-pi CLIs.\n * An adopted pi pane usually lacks MU_CTL_SOCK, so this is \"missing\". */\n ctl: SpawnCtl;\n ctlSocket: string;\n}\n\nasync function probeAdopted(\n db: Db,\n agent: AgentRow,\n): Promise<Pick<AdoptAgentResult, \"ctl\" | \"ctlSocket\">> {\n const ctlSocket = agentCtlSocket(db, agent);\n if (!expectsCtl(agent, ctlSocket)) return { ctl: \"skipped\", ctlSocket };\n const probe = await ctlProbe(ctlSocket, ADOPT_CTL_PROBE_MS);\n return { ctl: probeToSpawnCtl(probe.kind), ctlSocket };\n}\n\n/**\n * Register an existing tmux pane as a managed mu agent. Inverse of the\n * 'orphan' state surfaced by `mu agent list`: a pane that looks like an\n * agent (running pi/claude/codex) but has no DB row.\n *\n * Identity contract (matches the claim protocol invariant):\n * - Post-adopt, the pane's title equals the agent's name.\n * - When `name` is omitted, the pane's existing title becomes the\n * agent name verbatim. Adopting a pane titled 'pi' would fail name\n * validation — caller must supply --name in that case.\n *\n * Idempotent: adopting the same pane twice with the same name is a\n * no-op (returns alreadyAdopted=true). Adopting a different pane under\n * an existing agent name throws AgentExistsError.\n *\n * Validation order (matches the design in note #100):\n * 1. Pane id format -> assertValidPaneId via paneExists / setPaneTitle\n * 2. Pane exists -> PaneNotFoundError\n * 3. Pane is in session -> PaneNotInSessionError (cross-session)\n * 4. Resolved name OK -> isValidAgentName / Error('agent name invalid')\n * 5. Idempotent check -> if pane already owned by an agent of this\n * name, return alreadyAdopted=true\n * 6. Name not taken -> AgentExistsError (else)\n * 7. Insert + retitle.\n *\n * The row's `status` column is the deprecated constant 'spawning' (see\n * insertAgent); runtime state is read at display time by\n * src/agent-state.ts (control socket, murmur, or herdr), never from\n * pane text. Adoption runs no state probe, so it does not depend on\n * the agent's control socket or a captured pane answering.\n */\nexport async function adoptAgent(db: Db, opts: AdoptAgentOptions): Promise<AdoptAgentResult> {\n // Load-bearing throughout: adopting a pane requires reading a pane.\n const mux = await activeMux();\n\n // Step 1+2: pane format + existence.\n if (!(await mux.paneExists(opts.paneId))) {\n throw new PaneNotFoundError(opts.paneId, mux);\n }\n\n // Step 3: pane must be in the workstream's mux session.\n const expectedSession = opts.tmuxSession ?? `mu-${opts.workstream}`;\n const panesInSession: MuxPane[] = await mux.listPanesInSession(expectedSession);\n const matchingPane = panesInSession.find((p) => p.paneId === opts.paneId);\n if (!matchingPane) {\n // Pane exists (passed step 2) but isn't in the expected session.\n throw new PaneNotInSessionError(opts.paneId, opts.workstream, expectedSession);\n }\n\n // Step 4: resolved name. Default to the pane's current title —\n // unwrapping a possibly-composed mu title ('name · task')\n // back to just the name token. Re-adoption of a pane that mu previously\n // owned must work; without parseAgentNameFromTitle the ' · <glyph>'\n // suffix would fail isValidAgentName.\n const previousTitle = matchingPane.title.length > 0 ? matchingPane.title : null;\n const candidate =\n opts.name ?? (previousTitle !== null ? parseAgentNameFromTitle(previousTitle) : \"\");\n const resolvedName = candidate;\n if (!isValidAgentName(resolvedName)) {\n if (opts.name === undefined) {\n throw new Error(\n `pane ${opts.paneId} title '${previousTitle ?? \"(empty)\"}' is not a valid agent name; pass --name <name>`,\n );\n }\n throw new Error(`agent name invalid: '${resolvedName}'`);\n }\n\n // Step 5: idempotency. If an agent already owns this pane id, check\n // whether it's the same name; same name == no-op, different name ==\n // conflict (we don't move agents between pane ids silently).\n const existingByPane = getAgentByPane(db, opts.paneId);\n if (existingByPane) {\n if (existingByPane.name === resolvedName) {\n return {\n agent: existingByPane,\n alreadyAdopted: true,\n previousTitle,\n paneTitleSetTo: resolvedName,\n ...(await probeAdopted(db, existingByPane)),\n };\n }\n throw new AgentExistsError(existingByPane.name);\n }\n\n // Step 6: name not taken in THIS workstream. v5 makes agent names\n // per-workstream unique — 'worker-1' may legally exist in another\n // workstream; only refuse when it collides in the workstream we're\n // adopting into (bug_v5_name_clash_silent_misroute).\n const existingByName = getAgent(db, resolvedName, opts.workstream);\n if (existingByName) {\n throw new AgentExistsError(resolvedName);\n }\n\n // Step 7: insert + (conditional) retitle.\n const inserted = insertAgent(db, {\n name: resolvedName,\n workstream: opts.workstream,\n paneId: opts.paneId,\n cli: opts.cli,\n role: opts.role,\n });\n if (resolvedName !== previousTitle) {\n await mux.setPaneTitle(opts.paneId, resolvedName);\n }\n // Machine-local table (`agents`): no trigger, so this emit is the\n // only record.\n emitEvent(\n db,\n opts.workstream,\n \"agent.adopt\",\n `agent adopt ${resolvedName} (pane ${opts.paneId}, was title='${previousTitle ?? \"\"}')`,\n );\n return {\n agent: inserted,\n alreadyAdopted: false,\n previousTitle,\n paneTitleSetTo: resolvedName,\n ...(await probeAdopted(db, inserted)),\n };\n}\n","// mu — kickAgent: signal a wedged worker pane's foreground process\n// group from outside the pane.\n//\n// Why this verb exists: pi/claude/codex CLIs catch SIGINT themselves\n// (Ctrl-C is a UI input, not a process signal). When a worker is\n// wedged on a long-running tool subprocess (an unbounded `find /`,\n// a busy-wait loop, ...) the orchestrator's options were:\n//\n// - `mu agent send` — queues; the message is read after the tool\n// subprocess returns, which is exactly the wait we're trying to\n// shortcut.\n// - `tmux send-keys C-c` against the pane — the wrapping CLI eats\n// the signal as TUI input; doesn't reach the tool subprocess.\n// - drop out of mu, `pgrep -af \"find /\"`, `kill <pid>` — works,\n// but breaks the orchestrator's mental model and is fiddly.\n//\n// `mu agent kick` looks up the pane's TTY via tmux, asks `ps -t <tty>`\n// for the foreground process group (the pgid whose `stat` field\n// contains a `+`), and kills(2) that pgid directly. Default signal\n// is SIGINT (graceful, matches what Ctrl-C would do if it\n// propagated); --signal SIGTERM / SIGKILL escalate.\n//\n// Source feedback: workers_commonly_attempt_unbounded_find. The\n// PART A spec.\n\nimport { type AgentRow, getAgent } from \"../agents.js\";\nimport type { Db } from \"../db.js\";\nimport { emitEvent } from \"../logs.js\";\nimport { activeMux } from \"../mux.js\";\nimport type { HasNextSteps, NextStep } from \"../output.js\";\nimport { AgentNotFoundError } from \"./errors.js\";\n\n// ─── Allowed signals ─────────────────────────────────────────────────\n\n/** The signal set kick supports. SIGINT is graceful (matches Ctrl-C\n * semantics — what the operator probably wanted in the first place);\n * SIGTERM is the polite escalation; SIGKILL is the unblockable\n * hammer. We deliberately don't expose arbitrary signals — the\n * three above are the actionable ones for \"interrupt a wedged\n * foreground tool subprocess.\" */\nexport type KickSignal = \"SIGINT\" | \"SIGTERM\" | \"SIGKILL\";\n\nconst ALLOWED_SIGNALS: readonly KickSignal[] = [\"SIGINT\", \"SIGTERM\", \"SIGKILL\"];\n\nexport function isKickSignal(s: string): s is KickSignal {\n return (ALLOWED_SIGNALS as readonly string[]).includes(s);\n}\n\n// ─── Errors ──────────────────────────────────────────────────────────\n\n/**\n * Thrown when the foreground pgid lookup on a pane's TTY yields\n * either no rows at all (the pane is sitting at an idle shell with\n * no foreground job) or only the wrapping shell itself (the LLM CLI\n * — pi/claude/codex — is the foreground; signalling it would close\n * the agent, which is what `mu agent close` is for).\n *\n * Maps to the generic exit code 1 in handle.ts (this is a\n * runtime-state condition, not a typed not-found / conflict).\n */\nexport class NoForegroundProcessError extends Error implements HasNextSteps {\n override readonly name = \"NoForegroundProcessError\";\n constructor(\n public readonly agentName: string,\n public readonly tty: string,\n public readonly reason: \"no-foreground\" | \"shell-only\",\n ) {\n const detail =\n reason === \"no-foreground\"\n ? `no foreground process group on tty ${tty} (pane is idle)`\n : `the only foreground process on tty ${tty} is the agent's wrapping CLI itself; refusing to signal it (use \\`mu agent close ${agentName}\\` to close the agent)`;\n super(`agent ${agentName}: ${detail}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Inspect what's running in the pane\",\n command: `mu agent show ${this.agentName} -n 50`,\n },\n {\n intent: \"Close the agent (kills the wrapping CLI + pane)\",\n command: `mu agent close ${this.agentName}`,\n },\n ];\n }\n}\n\n// ─── Process executor (swappable for tests) ──────────────────────────\n//\n// Mirrors the `setTmuxExecutor` pattern in src/tmux.ts so unit tests\n// can mock `ps -t <tty>` and `kill -<sig> -<pgid>` without touching\n// real processes.\n\nexport interface KickProcessExecResult {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n}\n\nexport type KickProcessExecutor = (\n cmd: string,\n args: readonly string[],\n) => Promise<KickProcessExecResult>;\n\nconst realExecutor: KickProcessExecutor = async (cmd, args) => {\n // Lazy-load execa: this module is on the cold path (kick is rare),\n // and the unit tests swap in a mock executor before any real call\n // would be made.\n const { execa } = await import(\"execa\");\n const result = await execa(cmd, [...args], { reject: false });\n return {\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? \"\",\n exitCode: result.exitCode ?? null,\n };\n};\n\nlet currentExecutor: KickProcessExecutor = realExecutor;\n\n/** Install a custom executor (for tests). Returns the previous one so\n * tests can restore cleanly. */\nexport function setKickProcessExecutor(executor: KickProcessExecutor): KickProcessExecutor {\n const previous = currentExecutor;\n currentExecutor = executor;\n return previous;\n}\n\n/** Restore the real executor. */\nexport function resetKickProcessExecutor(): void {\n currentExecutor = realExecutor;\n}\n\n// ─── Foreground pgid lookup ──────────────────────────────────────────\n\ninterface PsRow {\n pid: number;\n pgid: number;\n /** ps's `stat` (or `state`) field. The presence of `+` means\n * \"foreground process group on its controlling tty\". */\n stat: string;\n /** Process command (just the comm; truncated, used for diagnostics). */\n comm: string;\n}\n\n/**\n * Parse `ps -t <tty> -o pid=,pgid=,stat=,comm=` output. Each non-blank\n * line is one process: four whitespace-separated fields. Defensive\n * about leading whitespace and command names with embedded spaces\n * (the comm is the LAST field — join the tail).\n */\nexport function parsePsTtyOutput(output: string): PsRow[] {\n const rows: PsRow[] = [];\n for (const raw of output.split(\"\\n\")) {\n const line = raw.trim();\n if (line === \"\") continue;\n const parts = line.split(/\\s+/);\n if (parts.length < 4) continue;\n const [pidStr, pgidStr, stat, ...commParts] = parts;\n if (pidStr === undefined || pgidStr === undefined || stat === undefined) continue;\n const pid = Number.parseInt(pidStr, 10);\n const pgid = Number.parseInt(pgidStr, 10);\n if (!Number.isFinite(pid) || !Number.isFinite(pgid)) continue;\n rows.push({ pid, pgid, stat, comm: commParts.join(\" \") });\n }\n return rows;\n}\n\n/**\n * Resolve the foreground process group id for a TTY device path. The\n * canonical signal `ps`'s `stat` field uses is `+` (BSD/Darwin AND\n * Linux procps). We pick the first row whose stat contains `+`; its\n * `pgid` is the foreground pgid of that controlling terminal.\n *\n * Returns:\n * - `{ kind: \"ok\", pgid, fgRow }` on success\n * - `{ kind: \"no-foreground\" }` when no row carries `+` AND there\n * are no candidate rows at all\n * - `{ kind: \"shell-only\", pgid, fgRow }` when the foreground pgid\n * resolves to a shell whose comm is the agent's wrapping CLI\n * (caller decides whether to refuse — kick refuses)\n *\n * The wrapping-CLI guard is intentionally narrow: we only refuse\n * when the foreground process command matches one of the known\n * pi/claude/codex/zsh/bash shapes. Anything else (a `find`, a\n * `cargo build`, a `python script.py`) is exactly what we want to\n * signal — that's the unbounded-tool case the verb was built for.\n */\nexport interface ForegroundLookup {\n kind: \"ok\" | \"shell-only\" | \"no-foreground\";\n pgid?: number;\n fgRow?: PsRow;\n /** All rows ps returned for the tty, for diagnostics / tests. */\n rows: PsRow[];\n}\n\n/** Comm names we treat as the agent's wrapping CLI / shell. Signalling\n * these via kick would just terminate the agent itself; that's\n * `mu agent close`'s job. The match is loose (suffix on basename)\n * so paths like `/usr/local/bin/pi` and aliases like `pi-meta` match\n * the right family. */\nconst WRAPPER_COMM_PREFIXES: readonly string[] = [\n \"pi\",\n \"claude\",\n \"codex\",\n \"bash\",\n \"zsh\",\n \"sh\",\n \"fish\",\n \"dash\",\n];\n\nfunction isWrapperComm(comm: string): boolean {\n // ps -o comm= returns the basename of the executable, possibly with\n // a leading `-` (login shell). Strip both.\n const cleaned = comm.replace(/^-/, \"\").trim();\n if (cleaned === \"\") return false;\n // Match exactly OR with a `-suffix` (e.g. `pi-meta`, `bash-3.2`).\n for (const prefix of WRAPPER_COMM_PREFIXES) {\n if (cleaned === prefix) return true;\n if (cleaned.startsWith(`${prefix}-`)) return true;\n }\n return false;\n}\n\nexport async function foregroundPgid(tty: string): Promise<ForegroundLookup> {\n // Strip the `/dev/` prefix because some `ps` implementations want\n // the tty as e.g. `ttys012` rather than `/dev/ttys012`. macOS's\n // ps accepts both; Linux's procps wants the short form. Pass the\n // short form everywhere for portability.\n const ttyShort = tty.startsWith(\"/dev/\") ? tty.slice(\"/dev/\".length) : tty;\n const result = await currentExecutor(\"ps\", [\"-t\", ttyShort, \"-o\", \"pid=,pgid=,stat=,comm=\"]);\n // ps exits 1 with empty output when no process is attached to the\n // tty; treat as no-foreground rather than throw.\n if (result.exitCode !== 0 && result.stdout.trim() === \"\") {\n return { kind: \"no-foreground\", rows: [] };\n }\n const rows = parsePsTtyOutput(result.stdout);\n if (rows.length === 0) return { kind: \"no-foreground\", rows };\n // Find the foreground row: stat contains `+`.\n const fg = rows.find((r) => r.stat.includes(\"+\"));\n if (!fg) {\n // ps returned rows but none is foreground. Treat as no-foreground.\n return { kind: \"no-foreground\", rows };\n }\n if (isWrapperComm(fg.comm)) {\n return { kind: \"shell-only\", pgid: fg.pgid, fgRow: fg, rows };\n }\n return { kind: \"ok\", pgid: fg.pgid, fgRow: fg, rows };\n}\n\n// ─── kill(2) the process group ────────────────────────────────────────\n\n/**\n * Send `signal` to process group `pgid`. The `kill -SIG -<pgid>` form\n * (negative pid) targets the whole pgrp, which is what we want — a\n * single `find` invocation may have spawned helpers; we want to take\n * the whole tree down with one signal.\n */\nasync function killPgrp(pgid: number, signal: KickSignal): Promise<void> {\n const result = await currentExecutor(\"kill\", [`-${signal}`, `-${pgid}`]);\n if (result.exitCode !== 0) {\n // ESRCH (pgid already gone) is benign — the process completed\n // between our ps and our kill. Treat as success.\n if (/no such process/i.test(result.stderr)) return;\n throw new Error(\n `kill -${signal} -${pgid} failed (exit ${result.exitCode}): ${result.stderr.trim() || \"no stderr\"}`,\n );\n }\n}\n\n// ─── Public verb ──────────────────────────────────────────────────────\n\nexport interface KickAgentOptions {\n workstream: string;\n /** Defaults to SIGINT (matches Ctrl-C semantics). */\n signal?: KickSignal;\n}\n\nexport interface KickAgentResult {\n agentName: string;\n paneId: string;\n /** TTY device path the foreground pgid was resolved against. */\n tty: string;\n /** The pgid we signalled. */\n signaledPgid: number;\n signal: KickSignal;\n /** The comm of the foreground process at the time of signal — useful\n * diagnostic in the event log (\"we kicked a `find`, not a `cargo`\"). */\n foregroundComm: string;\n}\n\n/**\n * Send `signal` to the foreground process group of an agent's pane\n * TTY. Default signal is SIGINT.\n *\n * Errors:\n * - `AgentNotFoundError` — the agent doesn't exist in this workstream.\n * - `PaneNotFoundError` (from paneTTY) — the agent's pane has vanished.\n * - `NoForegroundProcessError` — pane has no foreground job, OR the\n * foreground is the wrapping CLI itself (refuse; use `mu agent close`).\n *\n * Emits an `agent kick <name> (signal=..., pgid=..., comm=...)` event\n * on success.\n */\nexport async function kickAgent(\n db: Db,\n name: string,\n opts: KickAgentOptions,\n): Promise<KickAgentResult> {\n const signal: KickSignal = opts.signal ?? \"SIGINT\";\n const agent: AgentRow | undefined = getAgent(db, name, opts.workstream);\n if (!agent) throw new AgentNotFoundError(name, opts.workstream);\n // Load-bearing: no pane TTY, no process to signal.\n const tty = await (await activeMux()).paneTTY(agent.paneId);\n const lookup = await foregroundPgid(tty);\n if (lookup.kind === \"no-foreground\") {\n throw new NoForegroundProcessError(name, tty, \"no-foreground\");\n }\n if (lookup.kind === \"shell-only\") {\n throw new NoForegroundProcessError(name, tty, \"shell-only\");\n }\n // kind === \"ok\"\n const pgid = lookup.pgid;\n const fgRow = lookup.fgRow;\n if (pgid === undefined || fgRow === undefined) {\n // Should be unreachable given the discriminator; defensive throw\n // satisfies noUncheckedIndexedAccess + future-refactor safety.\n throw new NoForegroundProcessError(name, tty, \"no-foreground\");\n }\n await killPgrp(pgid, signal);\n // Signals a process; mutates no table at all, so there is nothing for\n // a trigger to see and this emit is the only record.\n emitEvent(\n db,\n agent.workstreamName,\n \"agent.kick\",\n `agent kick ${name} (signal=${signal}, pgid=${pgid}, comm=${fgRow.comm})`,\n );\n return {\n agentName: name,\n paneId: agent.paneId,\n tty,\n signaledPgid: pgid,\n signal,\n foregroundComm: fgRow.comm,\n };\n}\n","// mu — self-documenting output helpers.\n//\n// Every successful verb output should answer \"what changed AND what's\n// the natural next step?\". Every error should answer \"why AND what are\n// the actionable resolutions?\". The same data shape feeds both human\n// (dim text) and JSON (`nextSteps` array) consumers.\n//\n// Why a separate module: this is shared by cli.ts (success-path\n// rendering), the typed errors in src/agents.ts / src/tasks.ts /\n// src/workstream.ts (error nextSteps), and tests. Keeping the type +\n// helpers in one place avoids circular imports.\n\nimport Table from \"cli-table3\";\nimport type { Command } from \"commander\";\nimport picocolors from \"picocolors\";\n\n/**\n * Should we emit ANSI color escapes from this process?\n *\n * picocolors ships an `isColorSupported` flag, but it bakes its env\n * inspection (NO_COLOR / FORCE_COLOR / isTTY) ONCE at module-load\n * time. That makes the function untestable without dynamic re-imports\n * — and worse, it loses colors whenever stdout is a pipe, most\n * painfully under `watch --color mu state` and `tmux display-popup -E\n * 'mu state' | cat`, where the surrounding pane is a real terminal but\n * our own stdout is a pipe.\n *\n * We therefore re-implement the decision from scratch at call time,\n * reading every signal directly from `process.env` / `process.stdout`\n * so tests can flip env vars and observe the result without the\n * vi.resetModules + vi.doMock dance (per task\n * review_test_color_enabled_no_color_module_load_caveat).\n *\n * Order of precedence (first match wins):\n * - `NO_COLOR` set (cross-tool opt-out, https://no-color.org/) →\n * OFF, even when TMUX/MU_FORCE_COLOR/FORCE_COLOR are set. We\n * treat any defined value (including \"\") as set, matching the\n * no-color.org convention and picocolors' own behavior.\n * - `MU_FORCE_COLOR` truthy → ON (mu-specific override). Values\n * \"\", \"0\", and \"false\" are explicit opt-outs, matching chalk's\n * FORCE_COLOR convention.\n * - `FORCE_COLOR` truthy → ON (the standard env var picocolors /\n * chalk consult). Values \"\", \"0\", and \"false\" opt out.\n * - `TMUX` set → ON (the load-bearing fix for `watch` inside tmux:\n * the surrounding pane is a real terminal even though our stdout\n * is a pipe).\n * - Fall back to the standard TTY heuristic: stdout is a TTY AND\n * TERM !== \"dumb\". This mirrors what picocolors itself does in\n * `isColorSupported` for the happy-path case.\n *\n * See task hud_colors_stripped_under_watch_and for the original repro.\n */\nfunction envForceTrue(key: string): boolean {\n const v = process.env[key];\n if (v === undefined) return false;\n if (v === \"\" || v === \"0\" || v.toLowerCase() === \"false\") return false;\n return true;\n}\n\nexport function colorEnabled(): boolean {\n if (process.env.NO_COLOR !== undefined) return false;\n if (envForceTrue(\"MU_FORCE_COLOR\")) return true;\n if (envForceTrue(\"FORCE_COLOR\")) return true;\n if (process.env.TMUX !== undefined) return true;\n return Boolean(process.stdout.isTTY) && process.env.TERM !== \"dumb\";\n}\n\n/**\n * The single picocolors instance the rest of the codebase imports.\n * Built once at module load with `colorEnabled()` baked in, so every\n * caller (cli.ts, src/cli/*.ts) renders consistently regardless of\n * isTTY heuristics. Any other module that needs `pc` should import\n * this one rather than reaching for `picocolors` directly.\n */\n// picocolors is still used as the renderer (createColors honors the\n// flag we pass), but the *decision* of whether to render is ours.\nexport const pc = picocolors.createColors(colorEnabled());\n\n/**\n * One actionable next step. The `intent` is human-prose (\"Drop notes\n * as you work\"); the `command` is a literal shell command the user (or\n * an LLM) can copy-paste or `eval` directly.\n *\n * Used both for success-path hints (post-verb) and for typed-error\n * resolutions (in the error message + JSON output).\n */\nexport interface NextStep {\n /** Short human-prose label, e.g. \"Drop notes as you work\". */\n intent: string;\n /** Literal shell command, e.g. `mu task note foo \"...\"`. */\n command: string;\n}\n\n/**\n * Print a block of next-step hints to stdout, dimmed so humans can\n * skim past them but agents reading the captured output still get\n * them. No-op when the array is empty.\n *\n * Format:\n *\n * Next:\n * <intent padded>: <command>\n * <intent padded>: <command>\n *\n * The padding aligns the colons so visual scanning is easy.\n */\nexport function printNextSteps(steps: readonly NextStep[]): void {\n printNextStepsTo(steps, \"stdout\");\n}\n\n/** Same as `printNextSteps` but routes to either stdout or stderr.\n * Errors emit nextSteps to stderr (so success vs failure paths\n * capture cleanly when scripts redirect them separately); success\n * paths emit to stdout. Single source of truth for the formatting\n * (review_code_print_next_steps_duplicated). */\nexport function printNextStepsTo(steps: readonly NextStep[], sink: \"stdout\" | \"stderr\"): void {\n if (steps.length === 0) return;\n const labelWidth = Math.max(...steps.map((s) => s.intent.length));\n const out = sink === \"stderr\" ? console.error : console.log;\n out(pc.dim(\"Next:\"));\n for (const step of steps) {\n const label = step.intent.padEnd(labelWidth);\n out(pc.dim(` ${label} : ${step.command}`));\n }\n}\n\n/**\n * Build a cli-table3 Table with the mu-standard safety belt:\n * `wordWrap: false` (cells wider than their column truncate with `…`\n * instead of wrapping to a second visual row), per-column max widths\n * applied only where the caller asks (`null` = auto), and a default\n * borderless style mirroring the state/workspace/workstream tables.\n *\n * Callers should pre-truncate values they care about via the\n * `truncate()` / `truncateFront()` helpers in cli.ts (the proactive\n * path); `wordWrap: false` is the safety belt for the cells they\n * miss. This is load-bearing for renderers with fixed row budgets:\n * a single wrapped cell silently blows out the promised section height.\n *\n * Surfaced live by `mu workspace list` blowing the terminal width on\n * the `path` column (tables_truncate_long_cols_audit). Don't try to\n * cap every column — apply `colWidths` only on the column(s) the\n * operator is least likely to read in full and most likely to be\n * long. Don't add a `--full` / `--no-truncate` flag per verb either;\n * `--json` already emits the full value.\n */\nexport function muTable(opts: {\n head: string[];\n /** Per-column max widths in cells (`null` = auto width). When\n * supplied, the array length must match `head`. cli-table3\n * truncates with `…` because we set `wordWrap: false`. */\n colWidths?: (number | null)[];\n /** Style override; defaults to `{ head: [], border: [] }` (mu's\n * borderless look). Pass `{ head: [] }` to keep cli-table3's\n * default border styling. */\n style?: { head?: string[]; border?: string[] };\n}): InstanceType<typeof Table> {\n return new Table({\n head: opts.head,\n ...(opts.colWidths !== undefined ? { colWidths: opts.colWidths } : {}),\n style: opts.style ?? { head: [], border: [] },\n wordWrap: false,\n });\n}\n\n/**\n * The typed-error wire format for `--json` output. Errors that carry\n * actionable resolutions (most of them) implement `errorNextSteps()`;\n * the handler in cli.ts wraps them in this shape and emits to stderr.\n */\nexport interface ErrorJson {\n /** Class name (e.g. \"ClaimerNotRegisteredError\"). */\n error: string;\n /** Human-readable message (the same one printed to stderr in non-JSON mode). */\n message: string;\n /** Actionable resolutions. May be empty. */\n nextSteps: NextStep[];\n /** Process exit code that will follow. */\n exitCode: number;\n}\n\n/**\n * Marker interface for typed errors that carry actionable resolutions.\n * The handler checks this with a duck-typed `typeof err.errorNextSteps\n * === \"function\"` rather than instanceof so cross-realm errors (e.g.\n * thrown from a different module instance after a hot-reload) still\n * surface their nextSteps.\n */\nexport interface HasNextSteps {\n errorNextSteps(): NextStep[];\n}\n\n/**\n * Detect whether the current invocation requested `--json`. Used by the\n * error handler to decide between human-prose stderr and structured\n * JSON stderr. Reads `process.argv` directly because commander has\n * already consumed it by the time the handler runs, and threading it\n * through every verb wrapper would be invasive.\n *\n * Tolerates `--json=...` form (commander supports both) but mu's verbs\n * only use the bare `--json` flag.\n */\nexport function isJsonMode(): boolean {\n return process.argv.some((a) => a === \"--json\" || a.startsWith(\"--json=\"));\n}\n\n/**\n * Has the current err object produced its own actionable nextSteps?\n * Encapsulates the duck-type check so the handler stays readable.\n */\nexport function hasNextSteps(err: unknown): err is HasNextSteps {\n return (\n typeof err === \"object\" &&\n err !== null &&\n typeof (err as { errorNextSteps?: unknown }).errorNextSteps === \"function\"\n );\n}\n\n// ─── Usage rendering for the validation-error contract ──────────────\n//\n// Every operator-error path (commander-thrown CommanderError, handler-\n// thrown UsageError, typed *Invalid* errors) gets the same surface:\n// (1) the error line, (2) the failing subcommand's --help. The human\n// path prints commander's own helpInformation() (so future commander\n// version bumps automatically pick up any rendering improvements);\n// the --json path renders a structured shape so a script orchestrator\n// can introspect the verb without re-shelling for --help.\n//\n// Why structured-not-string for JSON: the entire point of --json is\n// that consumers never have to free-text-parse mu output. Embedding a\n// multi-kilobyte rendered help blob in the JSON envelope defeats that\n// — every option is already structured on the Command object.\n\n/** Structured rendition of a verb's --help, for JSON error envelopes. */\nexport interface UsageJson {\n /** Full canonical name including parent commands (e.g. \"mu task add\"). */\n command: string;\n /** The single-line synopsis (e.g. \"mu task add [options] [id]\"). */\n synopsis: string;\n /** Verb description (the one-paragraph prose under the synopsis). */\n description: string;\n /** Positional arguments in declared order. */\n args: Array<{ name: string; required: boolean; variadic: boolean; description: string }>;\n /** Options in declared order. `mandatory: true` iff the option was\n * declared via `.requiredOption()` (i.e. the operator MUST pass it).\n * `valueRequired: true` for `<value>`-style options whose value is\n * required when the flag IS passed. The two are independent. */\n options: Array<{\n flags: string;\n description: string;\n mandatory: boolean;\n valueRequired: boolean;\n }>;\n}\n\n/** Walk parent chain so subcommand renderings include the full path\n * (\"mu task add\" not just \"add\"). */\nfunction commandFullName(cmd: Command): string {\n const parts: string[] = [];\n let cur: Command | null = cmd;\n while (cur) {\n parts.unshift(cur.name());\n cur = cur.parent;\n }\n return parts.join(\" \");\n}\n\n/** Extract the structured usage shape for `--json` error envelopes. */\nexport function renderUsageJson(cmd: Command): UsageJson {\n return {\n command: commandFullName(cmd),\n synopsis: `${commandFullName(cmd)} ${cmd.usage()}`,\n description: cmd.description(),\n args: cmd.registeredArguments.map((a) => ({\n name: a.name(),\n required: a.required,\n variadic: a.variadic,\n description: a.description ?? \"\",\n })),\n options: cmd.options.map((o) => ({\n flags: o.flags,\n description: o.description ?? \"\",\n mandatory: o.mandatory ?? false,\n valueRequired: o.required ?? false,\n })),\n };\n}\n\n/** Render the human --help block (commander's own `helpInformation()`)\n * to stderr. Single source of truth for the post-error help dump. */\nexport function printUsageHuman(cmd: Command): void {\n // helpInformation() returns the full \"Usage: ...\\n\\n<desc>\\n\\nOptions:\\n ...\" block.\n // Print to stderr (errors only) so success-path stdout is never polluted.\n process.stderr.write(`\\n${cmd.helpInformation()}`);\n}\n","// mu — tmux substrate (back-compat re-export hub).\n//\n// The tmux implementation moved to src/mux/tmux.ts when the MuxBackend\n// interface was extracted, so mu could grow a second multiplexer. This\n// file preserves the historical `import { ... } from \"./tmux.js\"`\n// surface — 12 src modules and 47 test files import it directly, and\n// churning them in the same commit as the extraction would have made\n// the diff unreviewable.\n//\n// NEW CODE SHOULD IMPORT `./mux.js` and go through `activeMux()`.\n// Importing this module pins you to tmux specifically, which is correct\n// only for genuinely tmux-only concerns (the `MU_TMUX_SOCKET` test\n// isolation seam, `mu doctor`'s tmux version probe).\n//\n// Migrating the call sites is tracked by mux-callsite-migration.\n\nexport * from \"./mux/tmux.js\";\n","// Cross-process advisory lock for the tmux-topology critical section of\n// a spawn.\n//\n// Why this exists: every `mu` invocation is a separate short-lived\n// process, so there is no in-process mutex to lean on. When an operator\n// (or the model) fires several spawns in parallel —\n//\n// for n in 1 2 3; do mu agent spawn scout-$n -w scratch & done; wait\n//\n// — each process runs the check-then-act in `createOrReusePane`:\n// `sessionExists(mu-scratch)` → all see \"no\" → all call\n// `tmux new-session -d -s mu-scratch`. Exactly one wins; the losers get\n// \"duplicate session\", throw, and `rollbackSpawn` removes their agent\n// row. Net effect: agents silently dropped (and sometimes duplicated as\n// windows race). bug_parallel_spawn_races_drop_agents.\n//\n// The fix is a filesystem advisory lock keyed on the tmux SESSION name\n// (the shared resource — two spawns into different sessions never\n// contend). It wraps ONLY the fast topology + DB-insert section, NOT the\n// slow liveness/readiness wait, so genuine parallelism (the whole point\n// of `&`) is preserved: process A creates the session and inserts its\n// row, releases, then waits for liveness while process B is already\n// creating its own window.\n//\n// Mechanism: `fs.mkdir` is atomic and fails with EEXIST if the directory\n// exists — the classic lockfile primitive, no extra dependency (honors\n// the ROADMAP anti-feature pledge). A `meta.json` inside records pid +\n// acquisition time for stale-lock diagnostics and breaking.\n\nimport { join } from \"node:path\";\nimport { type FileLockOptions, locksDir, withFileLock } from \"../file-lock.js\";\n\n/** Lock directory path for a given tmux session name. The session name\n * is already validated ([a-z0-9_-] plus the `mu-` prefix), so it is a\n * safe path segment. Exported for tests that need to read the lock's\n * metadata directly via readFileLockMeta. */\nexport function lockPathForSession(session: string): string {\n return join(locksDir(), `spawn-${session}.lock`);\n}\n\n/** Max time to wait to acquire the lock before giving up (ms). A spawn's\n * critical section is sub-second; this is generous headroom for a deep\n * parallel fan-out queued behind one slow tmux server. */\nconst DEFAULT_ACQUIRE_TIMEOUT_MS = 15_000;\n\n/** A held lock older than this is presumed abandoned (a crashed `mu`\n * process that never released) and force-broken. Must comfortably\n * exceed the critical section's real duration. */\nconst STALE_LOCK_MS = 30_000;\n\nexport interface SpawnLockOptions {\n acquireTimeoutMs?: number;\n staleLockMs?: number;\n}\n\n/**\n * Run `fn` while holding the per-session spawn lock.\n *\n * The mechanism (atomic mkdir, stale-lock breaking, release-in-finally,\n * proceed-unlocked-on-non-contention) lives in src/file-lock.ts so\n * segments can reuse it without a second copy. This wrapper owns the\n * session-keyed path and the spawn-specific timeouts.\n */\nexport async function withSpawnLock<T>(\n session: string,\n fn: () => Promise<T>,\n opts?: SpawnLockOptions,\n): Promise<T> {\n const lockOpts: FileLockOptions = {\n acquireTimeoutMs: opts?.acquireTimeoutMs ?? DEFAULT_ACQUIRE_TIMEOUT_MS,\n staleLockMs: opts?.staleLockMs ?? STALE_LOCK_MS,\n timeoutEnvVar: \"MU_SPAWN_LOCK_TIMEOUT_MS\",\n };\n return withFileLock(lockPathForSession(session), session, fn, lockOpts);\n}\n","// mu — generic cross-process advisory lock, via atomic `fs.mkdir`.\n//\n// Extracted from src/agents/spawn-lock.ts, which had exactly this logic\n// keyed to a tmux session name. Segments need the same primitive keyed to\n// a sync directory, and two copies of a lock implementation is how they\n// drift — so the mechanism lives here once and both callers name their own\n// resource. spawn-lock.ts keeps its session-specific wrapper and its\n// tests; only the body moved.\n//\n// Why a lockfile at all: every `mu` invocation is a separate short-lived\n// process, so there is no in-process mutex to lean on. `fs.mkdir` is\n// atomic and fails EEXIST when the directory exists — the classic\n// primitive, no dependency (honours the ROADMAP anti-feature pledge).\n//\n// BEST-EFFORT BY DESIGN. A lock-acquire failure that is not contention (a\n// read-only state dir, say) falls through to running the body UNLOCKED\n// rather than failing the command. The lock narrows a race; it is not a\n// correctness gate. For segments specifically, correctness comes from\n// single-writer-per-file plus `UNIQUE (machine_id, hlc)` on ingest — the\n// lock only stops two concurrent local flushes from interleaving lines in\n// the same file.\n\nimport { mkdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { defaultStateDir } from \"./db.js\";\n\n/** Sub-directory of the state dir holding lock directories. */\nexport function locksDir(): string {\n return join(defaultStateDir(), \"locks\");\n}\n\n/** Default: max time to wait to acquire before proceeding unlocked. */\nconst DEFAULT_ACQUIRE_TIMEOUT_MS = 15_000;\n/** A held lock older than this is presumed abandoned (crashed process). */\nconst DEFAULT_STALE_LOCK_MS = 30_000;\n/** Poll interval while spinning. */\nconst RETRY_INTERVAL_MS = 25;\n\nexport interface FileLockOptions {\n acquireTimeoutMs?: number;\n staleLockMs?: number;\n /** Env var consulted for the acquire timeout, if any. */\n timeoutEnvVar?: string;\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\nfunction resolveTimeout(opts?: FileLockOptions): number {\n const envVar = opts?.timeoutEnvVar;\n if (envVar !== undefined) {\n const raw = process.env[envVar];\n if (raw !== undefined && raw !== \"\") {\n const n = Number.parseInt(raw, 10);\n if (Number.isFinite(n) && n >= 0) return n;\n }\n }\n return opts?.acquireTimeoutMs ?? DEFAULT_ACQUIRE_TIMEOUT_MS;\n}\n\n/** Best-effort staleness probe. A vanished lock returns false so the\n * caller simply retries the mkdir. */\nasync function isStale(path: string, staleMs: number): Promise<boolean> {\n try {\n const s = await stat(path);\n return Date.now() - s.mtimeMs > staleMs;\n } catch {\n return false;\n }\n}\n\n/**\n * Run `fn` while holding the lock directory at `lockPath`.\n *\n * Releases in a `finally`, so a throwing `fn` never leaks the lock.\n * Records pid + acquisition time in `meta.json` for stale-lock\n * diagnostics.\n */\nexport async function withFileLock<T>(\n lockPath: string,\n label: string,\n fn: () => Promise<T>,\n opts?: FileLockOptions,\n): Promise<T> {\n const staleMs = opts?.staleLockMs ?? DEFAULT_STALE_LOCK_MS;\n const deadline = Date.now() + resolveTimeout(opts);\n let held = false;\n\n try {\n await mkdir(locksDir(), { recursive: true });\n } catch {\n // Cannot even create the locks dir: run unlocked rather than refuse.\n return await fn();\n }\n\n while (!held) {\n try {\n await mkdir(lockPath); // atomic; EEXIST if already held\n held = true;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"EEXIST\") return await fn();\n if (await isStale(lockPath, staleMs)) {\n await rm(lockPath, { recursive: true, force: true }).catch(() => {});\n continue;\n }\n if (Date.now() >= deadline) return await fn();\n await sleep(RETRY_INTERVAL_MS);\n }\n }\n\n await writeFile(\n join(lockPath, \"meta.json\"),\n JSON.stringify({ pid: process.pid, label, acquiredAt: new Date().toISOString() }),\n ).catch(() => {});\n\n try {\n return await fn();\n } finally {\n await rm(lockPath, { recursive: true, force: true }).catch(() => {});\n }\n}\n\n/** Read a held lock's metadata, or null. Exposed for tests. */\nexport async function readFileLockMeta(\n lockPath: string,\n): Promise<{ pid: number; label: string; acquiredAt: string } | null> {\n try {\n const raw = await readFile(join(lockPath, \"meta.json\"), \"utf8\");\n return JSON.parse(raw) as { pid: number; label: string; acquiredAt: string };\n } catch {\n return null;\n }\n}\n","// mu — agent error classes.\n//\n// Every agent verb that can fail in a typed way has its own error class\n// here. The CLI's classifyError() (src/cli/handle.ts) maps them to exit\n// codes:\n// usage → 2 (AgentAbortNeedsCtlError, AgentFreshNeedsCtlError,\n// AgentSlashCommandUnsupportedError)\n// not found → 3 (AgentNotFoundError)\n// conflict → 4 (AgentExistsError, AgentNotInWorkstreamError,\n// PaneNotInSessionError, WorkspacePreservedError,\n// AgentExtensionOutdatedError, AgentBusyError)\n// timeout → 5 (AgentAbortTimeoutError)\n// failure → 1 (AgentSpawnCliNotFoundError, AgentDiedOnSpawnError,\n// AgentSpawnStartupError, AgentCtlUnreachableError)\n//\n// AgentDiedOnSpawnError + AgentSpawnStartupError reach into spawn.ts for\n// defaultSpawnLivenessMs — a single, narrow cross-cluster import that\n// documents itself in the error message (\"agent died within Nms of\n// spawn\" / \"agent reported a startup error within Nms of spawn\") —\n// and AgentSpawnStartupError for isExecFailureLine, to pick hints that\n// fit the matched line.\n//\n// AgentSpawnCliNotFoundError is the pre-flight cousin of the two\n// post-spawn-detect errors above: thrown BEFORE prestageWorkspace when\n// the resolved `--cli` command's first token doesn't exist on PATH.\n// Distinct from AgentSpawnStartupError so the operator can tell\n// 'I never had a working CLI' from 'CLI started but parked at an error'.\n//\n// Extracted from src/agents.ts as part of refactor_split_large_src_files.\n\nimport type { HasNextSteps, NextStep } from \"../output.js\";\nimport { defaultSpawnLivenessMs, isExecFailureLine } from \"./spawn.js\";\n\n/**\n * Pre-flight failure: the command mu would have spawned in the new\n * pane doesn't resolve to a binary on PATH (and isn't an absolute /\n * relative path that exists + is executable). Thrown by `spawnAgent`\n * BEFORE `prestageWorkspace` so a typo in `--cli` never leaves an\n * orphan workspace dir behind.\n *\n * Source: feedback ws task `fb_agent_spawn_no_validation`. Live\n * dogfood report: `mu agent spawn worker-1 --cli pi-meta` on a host\n * where the `pi-meta` binary wasn't on PATH printed `Spawned worker-1\n * (pi-meta)` and the pane immediately died with `command not found`;\n * the existing 1.5s liveness check sometimes missed it (the shell\n * stays alive after the failed exec). Pre-flighting the PATH lookup\n * surfaces the typo before any side effects (workspace, pane, DB row).\n *\n * Distinct from `AgentSpawnStartupError` (pane alive but parked at an\n * error prompt) and `AgentDiedOnSpawnError` (pane vanished within the\n * liveness window). All three carry different remediation hints, so\n * they're separate types.\n */\nexport class AgentSpawnCliNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"AgentSpawnCliNotFoundError\";\n constructor(\n public readonly cli: string,\n /** First whitespace-separated token of the resolved command — the\n * thing actually missing on PATH. Surfaced verbatim in the\n * message so the operator sees what mu searched for (which may\n * differ from `cli` when `$MU_<UPPER_CLI>_COMMAND` rewrites it). */\n public readonly binary: string,\n /** Name of the env var that mu consulted before falling back to\n * the bare `cli` value (e.g. `MU_PI_META_COMMAND`). Always set\n * to the conventional name so the nextSteps hint can recommend\n * exporting it. */\n public readonly envVarChecked: string,\n ) {\n super(\n `--cli ${cli} resolved to binary \"${binary}\" which is not on PATH (and not an executable absolute/relative path). Refusing to spawn — would create a pane that dies immediately on \"command not found\".`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Try the default CLI (the one mu's substrate ships against)\",\n command: \"mu agent spawn <name> --cli pi\",\n },\n {\n intent: \"If you meant a custom alias, set the env var to its real path\",\n command: `export ${this.envVarChecked}=\"<absolute-path-to-binary> [args...]\"`,\n },\n {\n intent: \"List installed CLIs typically supported by mu\",\n command: \"which pi pi-meta claude codex\",\n },\n ];\n }\n}\n\nexport class AgentExistsError extends Error implements HasNextSteps {\n override readonly name = \"AgentExistsError\";\n constructor(public readonly agentName: string) {\n // v5: agent names are UNIQUE per (workstream, name) — the same\n // name can legitimately exist in two different workstreams. The\n // pre-v5 message claimed global uniqueness, which (a) lied about\n // the schema and (b) misled operators into closing the existing\n // agent when the actual fix is `-w <other-ws>`.\n super(`agent already exists in this workstream: ${agentName}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n // v5: agents.workstream_id → workstreams.id; there is no\n // `agents.workstream` column. Use the join so this hint\n // actually runs against a v5 DB.\n intent: \"List which workstream(s) already have an agent by this name\",\n command: `mu sql \"SELECT a.name, ws.name AS workstream FROM agents a JOIN workstreams ws ON ws.id = a.workstream_id WHERE a.name = '${this.agentName}'\"`,\n },\n {\n intent: \"Spawn it in a different workstream (per-workstream unique → no clash)\",\n command: `mu agent spawn ${this.agentName} -w <other-workstream>`,\n },\n {\n intent: \"Or close the existing agent in this workstream and re-spawn\",\n command: `mu agent close ${this.agentName} && mu agent spawn ${this.agentName}`,\n },\n { intent: \"Or pick a different name\", command: \"mu agent spawn <new-name>\" },\n ];\n }\n}\n\nexport class AgentNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"AgentNotFoundError\";\n constructor(\n public readonly agentName: string,\n /** Optional workstream context. When set, the message is enriched\n * with `(in workstream <ws>)` so the verb that hit the miss\n * (e.g. `mu workspace path <agent> -w <ws>`) doesn't leave the\n * operator guessing which scope was searched. Optional so existing\n * call sites that only know the agent name keep their original\n * one-line message. */\n public readonly workstream?: string,\n ) {\n super(\n workstream === undefined\n ? `no such agent: ${agentName}`\n : `no such agent: ${agentName} (in workstream ${workstream})`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List agents in current workstream\", command: \"mu agent list\" },\n { intent: \"List workstreams to choose the right scope\", command: \"mu workstream list\" },\n {\n intent: \"Spawn it now\",\n command: `mu agent spawn ${this.agentName} -w <workstream>`,\n },\n ];\n }\n}\n\n/**\n * Thrown when an entity-targeted verb is invoked with `-w/--workstream\n * <name>` but the named agent lives in a different workstream.\n * Mirrors `TaskNotInWorkstreamError`. Maps to exit code 4 (conflict /\n * wrong scope). Distinguishes \"the user typo'd the workstream\" from\n * \"the agent doesn't exist anywhere\" (which surfaces as\n * `AgentNotFoundError`).\n */\nexport class AgentNotInWorkstreamError extends Error implements HasNextSteps {\n override readonly name = \"AgentNotInWorkstreamError\";\n constructor(\n public readonly agentName: string,\n public readonly expectedWorkstream: string,\n public readonly actualWorkstream: string,\n ) {\n super(`agent ${agentName} is in workstream ${actualWorkstream}, not ${expectedWorkstream}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Use the agent's actual workstream\",\n command: `mu agent show ${this.agentName} -w ${this.actualWorkstream}`,\n },\n {\n intent: \"List agents in the requested workstream\",\n command: `mu agent list -w ${this.expectedWorkstream}`,\n },\n ];\n }\n}\n\n/**\n * `mu agent adopt` of a pane that exists but is not in the workstream's\n * mux session. Distinct from AgentNotInWorkstreamError: the subject is a\n * pane, not an agent, and its owning session is not known without\n * another mux query. Maps to exit code 4 (conflict).\n */\nexport class PaneNotInSessionError extends Error implements HasNextSteps {\n override readonly name = \"PaneNotInSessionError\";\n constructor(\n public readonly paneId: string,\n public readonly workstream: string,\n public readonly expectedSession: string,\n ) {\n super(\n `pane ${paneId} is not in session ${expectedSession}; adopt only takes panes from workstream ${workstream}'s own session`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"List the workstream's agents and orphan panes to pick one\",\n command: `mu agent list -w ${this.workstream}`,\n },\n {\n intent: \"Or adopt it into the workstream whose session holds the pane\",\n command: `mu agent adopt ${this.paneId} -w <workstream>`,\n },\n ];\n }\n}\n\n/**\n * Thrown when an agent's pane is created and titled successfully but the\n * spawned process exits within the liveness window (default 1500ms;\n * configurable via `MU_SPAWN_LIVENESS_MS`). The most common cause is the\n * underlying CLI failing fast: a wrapper CLI blocking on a single-instance\n * lock, `claude` rejecting an invalid API key, etc. The agent's last\n * scrollback (when capturable) is attached to help diagnose.\n */\nexport class AgentDiedOnSpawnError extends Error implements HasNextSteps {\n override readonly name = \"AgentDiedOnSpawnError\";\n /** The spawned command is an ssh hop: remote causes, not wrapper locks. */\n readonly remote: boolean;\n constructor(\n public readonly agentName: string,\n public readonly paneId: string,\n public readonly scrollback: string | undefined,\n /** The command the pane ran; an `ssh ...` command gets remote hints. */\n public readonly command?: string,\n /** When it died; default `within <MU_SPAWN_LIVENESS_MS>ms of spawn`. */\n when = `within ${defaultSpawnLivenessMs()}ms of spawn`,\n ) {\n const tail = scrollback?.trim();\n const detail = tail ? `\\n\\n--- pane scrollback ---\\n${tail}\\n--- end scrollback ---` : \"\";\n const remote = /^\\s*ssh\\s/.test(command ?? \"\");\n const cause = remote\n ? 'The command is an ssh hop, so the likely causes are remote: the remote binary is missing, or its env (PATH, provider keys) is absent because ssh runs a non-interactive shell that skips ~/.zshrc / ~/.bashrc (wrap it: $SHELL -ilc \"pi --approve\"); or sshd refused the socket forward (ExitOnForwardFailure=yes kills the connection; check AllowStreamLocalForwarding).'\n : \"Most common cause: the spawned CLI exited immediately (e.g. a wrapper CLI blocking on its instance lock; set MU_<UPPER_CLI>_COMMAND to a non-blocking variant to bypass).\";\n super(`agent ${agentName} died ${when} (pane ${paneId}). ${cause}${detail}`);\n this.remote = remote;\n }\n errorNextSteps(): NextStep[] {\n if (this.remote) {\n return [\n {\n intent: \"See the ssh pieces and the interactive-login-shell recipe\",\n command: \"mu agent remote-env --help\",\n },\n {\n intent: \"Check the remote binary resolves in a login shell\",\n command: \"ssh <host> '$SHELL -ilc \\\"command -v pi\\\"'\",\n },\n {\n intent: \"Keep the pane open to read the remote error (one-off)\",\n command: \"export MU_SPAWN_LIVENESS_MS=0\",\n },\n { intent: \"Run health check\", command: \"mu doctor\" },\n ];\n }\n return [\n {\n intent: \"Inspect the dead pane's scrollback for the underlying error\",\n command: `mu agent read ${this.agentName} -n 100`,\n },\n {\n // agent_spawn_liveness_check_trips_on: per-spawn override is\n // the right scope for one-offs (e.g. wrapper CLIs blocking\n // on a per-project solo lock). Listed first so operators\n // reach for it before exporting an env var that leaks into\n // every subsequent spawn in the shell.\n intent: \"Override per spawn (one-off; no env-var leak)\",\n command: `mu agent spawn ${this.agentName} --command \"<cli> <bypass-flag>\" (e.g. pi-meta --no-solo)`,\n },\n {\n intent:\n \"Make the override the default for this CLI (applies to every subsequent spawn in this shell)\",\n command: 'export MU_PI_COMMAND=\"pi-alt --some-flag\"',\n },\n {\n intent: \"Disable the liveness check (CI / known long-lived sh subprocess)\",\n command: \"export MU_SPAWN_LIVENESS_MS=0\",\n },\n { intent: \"Run health check\", command: \"mu doctor\" },\n ];\n }\n}\n\n/**\n * Thrown when an agent's pane is alive AND staying alive after the\n * liveness window, but its first burst of output matches a known\n * provider-startup-failure pattern (missing API key, auth rejected, …).\n * Source: feedback ws task `agent_spawn_model_auth_failure_counts_as_live`.\n * Live dogfood report: `pi-meta --no-solo --model sonnet:high` printed\n * `Error: No API key found for amazon-bedrock` and parked at a prompt.\n * The pane stayed alive (1.5s liveness check passed) but the worker\n * could never do work — the orchestrator only discovered this when\n * `mu task wait` stalled minutes later.\n *\n * Distinct from `AgentDiedOnSpawnError`:\n * - `AgentDiedOnSpawnError` → pane vanished within the liveness window\n * (CLI exited fast).\n * - `AgentSpawnStartupError` → pane alive, but the captured scrollback\n * tail contains a curated provider-auth-failure pattern.\n * The two carry different remediation hints (CLI override vs. fix the\n * env var), so they're separate types instead of one with a flag.\n *\n * The pattern list is curated and short to keep false-positive risk low\n * — the scan only looks at the last ~30 lines of the 50-line capture\n * taken right after the liveness sleep, so matches naturally come from\n * the CLI's first ~1.5s of output (not arbitrary later prompts the\n * agent might type into).\n */\nexport class AgentSpawnStartupError extends Error implements HasNextSteps {\n override readonly name = \"AgentSpawnStartupError\";\n constructor(\n public readonly agentName: string,\n public readonly paneId: string,\n /** The single scrollback line that matched a known startup-error\n * pattern. Surfaced verbatim in the message so the operator sees\n * what mu saw. */\n public readonly matchedLine: string,\n /** Full captured scrollback (tail-trimmed already by\n * awaitSpawnLiveness). Attached to the message for context. */\n public readonly scrollback: string,\n /** When it was seen; default `within <MU_SPAWN_LIVENESS_MS>ms of spawn`. */\n when = `within ${defaultSpawnLivenessMs()}ms of spawn`,\n ) {\n super(\n `agent ${agentName} reported a startup error ${when} (pane ${paneId}). The pane is alive but the spawned CLI parked at an error prompt instead of becoming a working agent.\\n\\nMatched line: ${matchedLine.trim()}\\n\\n--- pane scrollback ---\\n${scrollback.trim()}\\n--- end scrollback ---`,\n );\n }\n errorNextSteps(): NextStep[] {\n const inspect: NextStep = {\n intent: \"Inspect the parked pane's scrollback for the full error\",\n command: `mu agent read ${this.agentName} -n 100`,\n };\n const disable: NextStep = {\n intent:\n \"Disable the startup-error scan if you actually wanted that prompt (CI / scripted recovery)\",\n command: \"export MU_SPAWN_LIVENESS_MS=0\",\n };\n if (isExecFailureLine(this.matchedLine.trim())) {\n // A shell exec failure, not a provider error: the API-key hints\n // below would point the operator at the wrong problem.\n return [\n inspect,\n {\n intent: \"Check the spawned command resolves in the pane's shell (PATH, typo)\",\n command: `mu agent spawn ${this.agentName} --command \"<full path to the CLI>\"`,\n },\n disable,\n ];\n }\n return [\n inspect,\n {\n // Most common today: the operator picked a model whose\n // provider has no credentials in this env. Default Anthropic\n // is the safe fallback for pi-meta.\n intent: \"Re-spawn with a CLI command whose provider credentials are present\",\n command: `mu agent spawn ${this.agentName} --command \"pi-meta --no-solo\" # default Anthropic`,\n },\n {\n intent: \"Or set the missing API key env var for the provider you wanted, then re-spawn\",\n command:\n \"export ANTHROPIC_API_KEY=... # or AWS_BEARER_TOKEN_BEDROCK, OPENAI_API_KEY, ...\",\n },\n disable,\n ];\n }\n}\n\n/**\n * Thrown when `closeAgent` is called on an agent that has an associated\n * workspace AND the caller didn't explicitly opt into discarding it.\n *\n * Background: the FK on `vcs_workspaces.agent` cascades on agent\n * delete, so a naive `closeAgent` drops the workspace registry row\n * but leaves the on-disk dir orphaned (mu can't see it via\n * `mu workspace list / free / path` afterwards). Surfaced during\n * the multi-agent dogfood teardown when three workspaces went\n * orphaned silently.\n *\n * The fix: refuse close if a workspace exists; force the caller to\n * decide. Two actionable resolutions:\n * - `mu workspace free <agent>` first, then close cleanly.\n * - `mu agent close <agent> --discard-workspace` to free the\n * workspace AND close the agent in one shot (lossy: pending\n * changes in the workspace are gone).\n *\n * Maps to exit code 4 (conflict) via the cli.ts handler.\n */\nexport class WorkspacePreservedError extends Error implements HasNextSteps {\n override readonly name = \"WorkspacePreservedError\";\n constructor(\n public readonly agentName: string,\n public readonly workspacePath: string,\n ) {\n super(\n `agent ${agentName} has a workspace at ${workspacePath}; refusing to close (would orphan the on-disk dir)`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Free the workspace first (preserves agent for next step)\",\n command: `mu workspace free ${this.agentName} (--commit to commit pending changes first)`,\n },\n {\n intent: \"Or close + discard the workspace in one shot (lossy)\",\n command: `mu agent close ${this.agentName} --discard-workspace`,\n },\n {\n intent: \"Or just inspect what's in the workspace\",\n command: `cd ${this.workspacePath}`,\n },\n ];\n }\n}\n\n/**\n * The agent's pane and pi are up, but nothing answers on its control\n * socket (`MU_CTL_SOCK`): the mu pi extension is not loaded, or, for a\n * remote agent, the ssh command lacks the `-L` forward. Spawn does not\n * throw it — the agent is usable by hand — but prints it as a warning;\n * verbs that need the socket throw it.\n */\nexport class AgentCtlUnreachableError extends Error implements HasNextSteps {\n override readonly name = \"AgentCtlUnreachableError\";\n constructor(\n public readonly agentName: string,\n public readonly workstream: string,\n public readonly socket: string,\n public readonly kind: \"missing\" | \"refused\",\n ) {\n super(\n `agent ${agentName} has no control socket (ctl: ${kind}) at ${socket}; mu cannot send to or wait on it exactly`,\n );\n }\n errorNextSteps(): NextStep[] {\n // A pi at its trust prompt has not bound the socket yet, so it reads\n // \"missing\"; a \"refused\" socket exists, so trust is not the cause.\n const trust: NextStep[] =\n this.kind === \"missing\"\n ? [\n {\n intent:\n \"Just spawned? pi may sit at its project trust prompt: answer /trust in the pane, or respawn with --command 'pi --approve'\",\n command: `mu agent read ${this.agentName} -w ${this.workstream}`,\n },\n ]\n : [];\n return [\n ...trust,\n { intent: \"Check whether the mu pi extension is linked\", command: \"mu doctor\" },\n {\n intent: \"Stop a runaway tool without the socket\",\n command: `mu agent kick ${this.agentName} -w ${this.workstream}`,\n },\n { intent: \"Link the extension, then re-spawn\", command: \"mu link pi\" },\n {\n intent: \"Remote agent: forward the socket in your ssh command\",\n command: `ssh -L ${this.socket}:<remote sock> ... (see mu agent remote-env)`,\n },\n ];\n }\n}\n\n/**\n * `mu agent abort` on an agent that does not run pi: there is no control\n * socket to carry the abort, and mu does not silently signal the pane.\n */\nexport class AgentAbortNeedsCtlError extends Error implements HasNextSteps {\n override readonly name = \"AgentAbortNeedsCtlError\";\n constructor(\n public readonly agentName: string,\n public readonly workstream: string,\n public readonly cli: string,\n ) {\n super(\n `agent ${agentName} runs ${cli}, not pi: abort needs the mu pi extension; use mu agent kick`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Signal the pane's foreground process instead\",\n command: `mu agent kick ${this.agentName} -w ${this.workstream}`,\n },\n ];\n }\n}\n\n/** The abort was delivered but pi did not settle within the timeout. */\nexport class AgentAbortTimeoutError extends Error implements HasNextSteps {\n override readonly name = \"AgentAbortTimeoutError\";\n constructor(\n public readonly agentName: string,\n public readonly workstream: string,\n public readonly timeoutMs: number,\n ) {\n super(`agent ${agentName} was still busy ${timeoutMs}ms after the abort`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Signal the pane's foreground process group\",\n command: `mu agent kick ${this.agentName} -w ${this.workstream}`,\n },\n {\n intent: \"Read the pane to see what it is stuck on\",\n command: `mu agent read ${this.agentName} -n 30 -w ${this.workstream}`,\n },\n ];\n }\n}\n\n/**\n * `mu agent send --fresh` on an agent without a control socket (non-pi\n * CLI, or forced `--via mux`): there is no one-operation new session.\n */\nexport class AgentFreshNeedsCtlError extends Error implements HasNextSteps {\n override readonly name = \"AgentFreshNeedsCtlError\";\n constructor(\n public readonly agentName: string,\n public readonly workstream: string,\n public readonly cli: string,\n ) {\n super(\n `agent ${agentName} runs ${cli} without a control socket: --fresh needs the mu pi extension`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Reset the session through the pane, then send (verify it landed)\",\n command: `mu agent send ${this.agentName} '/new' -w ${this.workstream}`,\n },\n ];\n }\n}\n\n/**\n * The agent's pi serves the control socket with an extension it loaded\n * before mu learned `op` (e.g. `fresh`): running pi processes keep the\n * extension code they started with. pi's `/reload` re-imports the\n * extension through the `mu link pi` shim and keeps the socket; a\n * respawn always works. An extension that serves op `command` takes\n * `/reload` over ctl; an older one needs it typed (`--via mux`).\n */\nexport class AgentExtensionOutdatedError extends Error implements HasNextSteps {\n override readonly name = \"AgentExtensionOutdatedError\";\n constructor(\n public readonly agentName: string,\n public readonly workstream: string,\n public readonly op: string,\n public readonly extVersion?: string,\n /** Ops the extension serves (from its \"unknown op\" reply); absent from old extensions. */\n public readonly ops?: readonly string[],\n ) {\n super(\n `agent ${agentName}'s mu pi extension (${extVersion ?? \"version unknown\"}) predates op ${op}: its pi loaded an older mu build; reload or respawn it`,\n );\n }\n errorNextSteps(): NextStep[] {\n const w = `-w ${this.workstream}`;\n // An extension that serves op command runs /reload over ctl itself;\n // an older one only reloads when /reload is typed into the pane.\n const via = this.ops?.includes(\"command\") ? \"\" : \" --via mux\";\n return [\n {\n intent: \"Reload pi's extensions (keeps the session and context)\",\n command: `mu agent send ${this.agentName} '/reload'${via} ${w}`,\n },\n {\n intent: \"Or respawn the agent (loses its context)\",\n command: `mu agent close ${this.agentName} ${w} && mu agent spawn ${this.agentName} ${w} ...`,\n },\n ];\n }\n}\n\n/**\n * `send --fresh` or a session command (`/new`, `/reload`, `/compact`)\n * refused: pi is mid-turn, and running it would abandon the turn.\n */\nexport class AgentBusyError extends Error implements HasNextSteps {\n override readonly name = \"AgentBusyError\";\n constructor(\n public readonly agentName: string,\n public readonly workstream: string,\n /** The session command refused, e.g. \"/new\". Absent: `--fresh`. */\n public readonly command?: string,\n ) {\n super(`agent ${agentName} is busy: ${command ?? \"--fresh\"} would abandon its running turn`);\n }\n errorNextSteps(): NextStep[] {\n const retry =\n this.command === undefined\n ? `mu agent send ${this.agentName} --fresh --force '...' -w ${this.workstream}`\n : `mu agent send ${this.agentName} '${this.command}' --force -w ${this.workstream}`;\n return [\n {\n intent: \"Stop the turn first, then retry\",\n command: `mu agent abort ${this.agentName} -w ${this.workstream}`,\n },\n { intent: \"Or abandon the turn in one step\", command: retry },\n ];\n }\n}\n\n/**\n * A slash command other than `/new`, `/reload`, `/compact` sent to a pi\n * agent. The control socket cannot run it (pi.sendUserMessage would\n * hand it to the model as text), and mu does not silently paste into a\n * pi pane: `--via mux` is the explicit way to type it there.\n */\nexport class AgentSlashCommandUnsupportedError extends Error implements HasNextSteps {\n override readonly name = \"AgentSlashCommandUnsupportedError\";\n constructor(\n public readonly agentName: string,\n public readonly workstream: string,\n public readonly text: string,\n public readonly supported: readonly string[],\n ) {\n super(\n `agent ${agentName} runs pi: over the control socket mu runs only ${supported.join(\", \")}; ${JSON.stringify(text.split(\"\\n\")[0])} is not one of them. Pass --via mux to type it into the pane`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Type it into the pane on purpose (tmux paste; unconfirmed)\",\n command: `mu agent send ${this.agentName} '${this.text.split(\"\\n\")[0]}' --via mux -w ${this.workstream}`,\n },\n ];\n }\n}\n","// mu — the APPLY path: how one op lands in the portable tables.\n//\n// The counterpart to src/capture.ts. Capture answers \"a row changed,\n// what op records it\"; this answers \"here is an op, local or from a\n// peer, what does the table look like afterwards\". v2-sync will call\n// `applyOp` once per op it ingests; this module knows nothing about\n// segments, files or watermarks.\n//\n// THE MERGE RULES (docs/VOCABULARY.md; v2-merge-rules)\n// ---------------------------------------------------\n// note / message GROW-ONLY SET. Insert-if-absent by content\n// identity (task, author, content, created_at).\n// Never updated, so never in conflict.\n// task / workstream PER-FIELD LWW by HLC. Not row-level.\n// edge LWW-ELEMENT-SET: add/remove, each carrying an\n// HLC, so a remove and a re-add order correctly.\n// anything else NOT SYNCED. Rejected loudly.\n//\n// WHY PER-FIELD AND NOT ROW-LEVEL\n// -------------------------------\n// The original design note argued row-level LWW sufficed, because \"the\n// operating rule is already no concurrent edits to one workstream on two\n// machines\". That assumption is weak in mu specifically: mu runs\n// autonomous agent crews, so a crew on the devserver closing a task\n// while the operator edits that same task's impact on a laptop is\n// concurrent multi-machine writing BY CONSTRUCTION, not user error.\n// Row-level LWW would let the crew's status=CLOSED clobber the\n// operator's impact=80, silently, with no conflict reported.\n//\n// Per-field costs nothing extra here because capture already emits\n// SEMANTIC PARTIAL UPDATES: a task.close op carries {\"status\":\"CLOSED\"},\n// not a row snapshot. So \"apply each op's keys in HLC order\" IS\n// per-field LWW. No column version vectors, no per-column metadata, none\n// of the cr-sqlite machinery we rejected.\n//\n// PROVENANCE: DERIVED FROM THE OPS LOG, NOT STORED\n// ------------------------------------------------\n// Per-field LWW needs to know, per field, the HLC that last wrote it.\n// The obvious shape is a side table keyed (entity, key, field) -> hlc.\n// This module does NOT do that, and the reason is worth stating because\n// the brief explicitly asked for a justification.\n//\n// The ops log ALREADY IS the per-field provenance. Every op carries its\n// HLC and names exactly the fields it touched, and `idx_ops_entity_key`\n// indexes (entity, key). So \"the HLC that last wrote field F of key K\"\n// is a query over the ~handful of ops for K, not a fact needing separate\n// storage. Deriving it instead of storing it means:\n//\n// * ONE source of truth. A side table is a denormalisation that can\n// disagree with the log, and when it does, every downstream\n// projection (undo, sync) inherits the disagreement. There\n// is no reconciliation procedure that could fix it, because nothing\n// would know which side was right.\n// * Idempotence for free. Re-applying an op compares HLCs against the\n// log, which already contains that op, so a second apply is a no-op\n// by construction rather than by careful bookkeeping. That is what\n// makes \"re-read a peer's segment from zero\" a safe universal\n// repair.\n// * Resurrection survives deletion for free. See below.\n// * Storage cost: ZERO additional bytes. A side table would cost one\n// row per (key, field) — for the 8 captured columns of `tasks`, 8\n// rows per task, each ~60 bytes of key text plus a ~55-byte HLC, so\n// roughly 1KB per task, unbounded in the number of tasks and\n// duplicating data already on disk in `ops`.\n//\n// Measured cost of deriving: ~10us per key against a 16000-op / 2000-task\n// log (index seek over that key's ~8 ops). Ingest is already doing a\n// write per op, so this is not the bottleneck, and it buys the absence of\n// a whole class of drift bug. If a profile ever says otherwise, the fix\n// is a materialised cache keyed off `ops.seq` that can be rebuilt from\n// the log — which is exactly the kind of thing to add when measured, not\n// before.\n//\n// RESURRECTION vs A STALE PUT\n// ---------------------------\n// After a `del`, a LATER `put` must recreate the row (a legitimate\n// re-add), while an EARLIER `put` arriving late must not. Distinguishing\n// them requires provenance that outlives the row — and derived\n// provenance does, automatically, because `ops` rows are never deleted\n// when a table row is. A stored side table would have to be deliberately\n// NOT cleaned up on delete, which is a rule someone eventually\n// \"optimises\" away.\n//\n// TOMBSTONES ARE JUST OPS (v2-tombstones)\n// ---------------------------------------\n// There is no tombstone table. `op='del'` rows ARE the tombstones,\n// ordinary ops carrying an HLC, so out-of-order arrival is just \"compare\n// HLCs\" — the same comparison the update path does. A late `put` older\n// than a seen `del` loses; a `del` older than a seen `put` loses. One\n// code path, no special casing.\n\nimport { type Db, MACHINE_LOCAL_ENTITIES, SYNCED_ENTITIES, type SyncedEntity } from \"./db.js\";\nimport { compareHlc } from \"./hlc.js\";\nimport { LEGACY_LOG_ONLY_SQL_EXCLUSION } from \"./legacy-ops.js\";\nimport { withCaptureSuppressed } from \"./op-context.js\";\nimport { DEFAULT_SUBSTATE, isTaskStatus, mapLegacyStatus, resolvePair } from \"./tasks/status.js\";\n\n/** An op as applied. Mirrors the `ops` row shape, minus the local-only\n * `seq` (meaningless on a peer) and the advisory `created_at`. */\nexport interface Op {\n /** The ordering key. See docs/VOCABULARY.md § HLC. */\n hlc: string;\n /** Which peer minted it. Half of the (machine_id, hlc) identity. */\n machineId: string;\n /** One user-visible action, for `mu undo`. */\n groupId: string;\n /** Free text: agent name, \"user\", \"system\". */\n actor?: string | null;\n /** Semantic label, e.g. \"task.close\". */\n intent?: string | null;\n /** Which kind of thing this op addresses. */\n entity: string;\n /** The NATURAL key. Never a surrogate id. */\n key: string;\n /** 'put' = semantic partial update, 'del' = tombstone. */\n op: \"put\" | \"del\";\n /** JSON object of ONLY the fields this op touched. */\n payload: string;\n}\n\n/** Thrown when an op names an entity this build KNOWS is machine-local\n * (see `MACHINE_LOCAL_ENTITIES`). Loud by design: a peer shipping a\n * pane id or an absolute path is a real bug we want reported.\n *\n * NOT thrown for an entity we merely do not recognise. That is a\n * reader-behind-writer vocabulary skew in a mixed fleet, and treating\n * it as a defect froze one peer's watermark permanently — see\n * `MACHINE_LOCAL_ENTITIES` in src/db.ts for the incident. */\nexport class OpEntityNotSyncedError extends Error {\n constructor(readonly entity: string) {\n super(\n `op entity ${JSON.stringify(entity)} is machine-local and must never arrive from a peer ` +\n `(synced entities: ${SYNCED_ENTITIES.join(\", \")})`,\n );\n this.name = \"OpEntityNotSyncedError\";\n }\n}\n\n/** Thrown when an op's natural key does not parse for its entity. */\nexport class OpKeyMalformedError extends Error {\n constructor(\n readonly entity: string,\n readonly key: string,\n ) {\n super(`malformed ${entity} key: ${JSON.stringify(key)}`);\n this.name = \"OpKeyMalformedError\";\n }\n}\n\n/** What `applyOp` did, so callers (and tests) can assert on outcomes\n * rather than re-querying. */\nexport interface ApplyResult {\n /** True iff the DB changed. False for a losing op, a duplicate, or a\n * no-op — all three are legitimate and non-exceptional. */\n changed: boolean;\n /** Fields actually written, for a 'put' on task / workstream. Empty\n * when every field in the payload lost its HLC comparison. */\n appliedFields: string[];\n /** Why nothing happened, when `changed` is false. */\n skipped?: \"older-than-tombstone\" | \"older-than-current\" | \"already-present\" | \"absent\";\n}\n\nfunction isSyncedEntity(entity: string): entity is SyncedEntity {\n return (SYNCED_ENTITIES as readonly string[]).includes(entity);\n}\n\n/** Entities this build knows must never travel. Distinct from \"unknown\":\n * only these are a defect. */\nfunction isKnownMachineLocalEntity(entity: string): boolean {\n return (MACHINE_LOCAL_ENTITIES as readonly string[]).includes(entity);\n}\n\n// ─── Derived provenance ───────────────────────────────────────────────\n\n/**\n * The newest HLC among ops for `key` that touched `field`, or null if no\n * op ever has. Ops are the provenance (see the module comment), so this\n * is a plain indexed query rather than a lookup in a side table.\n *\n * `json_type(payload, '$.field') IS NOT NULL` is the presence test, NOT\n * `json_extract(...) IS NOT NULL`. json_extract returns SQL NULL both\n * for an absent key and for a key whose value IS null, which would make\n * a set-to-NULL op invisible to its own provenance — the field would\n * then be permanently rewritable by any older op. json_type returns the\n * string 'null' for a present-but-null member and SQL NULL only when\n * the member is genuinely absent, so it distinguishes the two.\n *\n * The `hlc <> @self` exclusion makes re-application idempotent: an op\n * already in the log must not count itself as a competing writer, or a\n * second apply would compare its HLC against itself and lose.\n */\nfunction fieldHlc(db: Db, op: Op, field: string): string | null {\n const row = db\n .prepare(\n `SELECT MAX(hlc) AS hlc\n FROM ops\n WHERE entity = @entity\n AND key = @key\n AND op = 'put'\n AND hlc <> @self\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n AND json_type(payload, '$.' || @field) IS NOT NULL`,\n )\n .get({ entity: op.entity, key: op.key, self: op.hlc, field }) as\n | { hlc: string | null }\n | undefined;\n return row?.hlc ?? null;\n}\n\n/**\n * The newest tombstone HLC for this key, or null if it was never\n * deleted. This is the whole of tombstone handling: a `del` is an\n * ordinary op, so \"has this been deleted, and when\" is one MAX().\n */\nfunction tombstoneHlc(db: Db, op: Op): string | null {\n const row = db\n .prepare(\n `SELECT MAX(hlc) AS hlc\n FROM ops\n WHERE entity = @entity AND key = @key AND op = 'del' AND hlc <> @self`,\n )\n .get({ entity: op.entity, key: op.key, self: op.hlc }) as { hlc: string | null } | undefined;\n return row?.hlc ?? null;\n}\n\n/** The newest HLC of any op for this key, whatever kind. Used by the\n * grow-only and element-set paths, which care about the row's\n * existence rather than individual fields. */\nfunction anyHlc(db: Db, op: Op, kind: \"put\" | \"del\"): string | null {\n const row = db\n .prepare(\n `SELECT MAX(hlc) AS hlc\n FROM ops\n WHERE entity = @entity AND key = @key AND op = @kind AND hlc <> @self`,\n )\n .get({ entity: op.entity, key: op.key, kind, self: op.hlc }) as\n | { hlc: string | null }\n | undefined;\n return row?.hlc ?? null;\n}\n\n/** True iff `candidate` is strictly newer than `incumbent`. A null\n * incumbent means \"nothing has written this yet\", so anything wins. */\nfunction wins(candidate: string, incumbent: string | null): boolean {\n if (incumbent === null) return true;\n return compareHlc(candidate, incumbent) > 0;\n}\n\n// ─── Payload decoding ─────────────────────────────────────────────────\n\n/** A payload field and its value. `value` may legitimately be null —\n * that is a set-to-NULL, not an absent field. */\ntype PayloadEntry = readonly [field: string, value: string | number | null];\n\n/**\n * Decode a payload into ordered entries.\n *\n * Deliberately hand-rolled over `Object.entries(JSON.parse(...))`\n * only in that it validates shape and narrows types; the important part\n * is what it does NOT do: it never uses `json_patch` to merge a payload\n * into a row. json_patch implements RFC 7396, where a null member means\n * DELETE THIS KEY, so `json_patch('{\"owner_id\":7}', '{\"owner_id\":null}')`\n * returns `{}` — the set-to-NULL is silently discarded rather than\n * applied. Since capture emits exactly `{\"owner_id\":null}` when a claim\n * is released, using json_patch anywhere on the apply path would drop\n * every set-to-NULL transition in the system. Fields are applied one at\n * a time with explicit parameter binding instead.\n */\nfunction decodePayload(payload: string): PayloadEntry[] {\n const parsed: unknown = JSON.parse(payload);\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new SyntaxError(`op payload must be a JSON object, got ${payload.slice(0, 40)}`);\n }\n const entries: PayloadEntry[] = [];\n for (const [field, value] of Object.entries(parsed)) {\n if (value === null || typeof value === \"string\" || typeof value === \"number\") {\n entries.push([field, value]);\n continue;\n }\n if (typeof value === \"boolean\") {\n // SQLite has no boolean type; capture never emits one, but a\n // hand-written or future op might.\n entries.push([field, value ? 1 : 0]);\n continue;\n }\n throw new SyntaxError(`op payload field ${JSON.stringify(field)} has unsupported type`);\n }\n return entries;\n}\n\n// ─── Natural key parsing ──────────────────────────────────────────────\n//\n// The inverse of src/capture.ts's key builders. Kept here rather than\n// shared with capture because capture composes keys in SQL and this\n// decomposes them in TS; a \"shared\" helper would be two functions in one\n// file pretending to be one thing.\n\n/** `<workstream>/<local_id>` -> its parts. Task ids match\n * `[a-z][a-z0-9_-]*`, so they never contain '/', making the FIRST '/'\n * the unambiguous separator. */\nfunction parseTaskKey(key: string): { workstream: string; localId: string } {\n const slash = key.indexOf(\"/\");\n if (slash <= 0 || slash === key.length - 1) throw new OpKeyMalformedError(\"task\", key);\n const workstream = key.slice(0, slash);\n const localId = key.slice(slash + 1);\n if (workstream.length === 0 || localId.length === 0) {\n throw new OpKeyMalformedError(\"task\", key);\n }\n return { workstream, localId };\n}\n\n/** `<task-key>#<origin-id>` -> the task key it hangs off. Task ids\n * cannot contain '#', so the LAST '#' separates. The origin id is\n * validated as present (a key without one is malformed) but not\n * returned: it identifies the note on its ORIGIN machine only, and\n * `applyNotePut` uses content identity instead. See its comment. */\nfunction parseNoteKey(key: string): { taskKey: string } {\n const hash = key.lastIndexOf(\"#\");\n if (hash <= 0 || hash === key.length - 1) throw new OpKeyMalformedError(\"note\", key);\n return { taskKey: key.slice(0, hash) };\n}\n\n/** `<blocker>-><blocked>` -> its parts. Task ids MAY end in '-', so\n * `demo/a-->demo/b` is a valid key for blocker `demo/a-`; splitting on\n * the FIRST '->' recovers that correctly, whereas a naive split('->')\n * would produce three fragments. */\nfunction parseEdgeKey(key: string): { blocker: string; blocked: string } {\n const arrow = key.indexOf(\"->\");\n if (arrow <= 0) throw new OpKeyMalformedError(\"edge\", key);\n const blocker = key.slice(0, arrow);\n const blocked = key.slice(arrow + 2);\n if (blocker.length === 0 || blocked.length === 0) throw new OpKeyMalformedError(\"edge\", key);\n return { blocker, blocked };\n}\n\n// ─── Row helpers ──────────────────────────────────────────────────────\n\n/**\n * Columns that must never be written from an op.\n *\n * owner_id is an FK into the machine-local `agents` table, so\n * OWNERSHIP DOES NOT SYNC (src/db.ts § MACHINE_LOCAL_TABLES\n * spells out why). It is CAPTURED, for local history and\n * undo, but applying a peer's owner_id would at best point\n * at an unrelated local agent and at worst violate the FK\n * outright — verified: inserting a task with an owner_id\n * absent from `agents` fails with 'FOREIGN KEY constraint\n * failed'. Stripping it structurally is the only correct\n * move, and it makes the FK a backstop rather than a trap.\n * local_id / name\n * are encoded in the natural key. Taking them from the\n * payload would let a op rename the row out from under its\n * own key.\n */\nconst NEVER_APPLY = new Set([\"owner_id\", \"local_id\", \"name\", \"id\", \"workstream_id\", \"task_id\"]);\n\n/** Ensure the workstream row exists, returning its surrogate id. Rows\n * arrive out of order across entities (a task op can precede its\n * workstream op), so the apply path creates the parent on demand\n * rather than rejecting. The subsequent workstream op then fills in\n * its real fields by ordinary per-field LWW. */\nfunction ensureWorkstreamRow(db: Db, name: string): number {\n const existing = db.prepare(\"SELECT id FROM workstreams WHERE name = ?\").get(name) as\n | { id: number }\n | undefined;\n if (existing) return existing.id;\n db.prepare(\"INSERT INTO workstreams (name, created_at) VALUES (?, ?)\").run(\n name,\n new Date().toISOString(),\n );\n const created = db.prepare(\"SELECT id FROM workstreams WHERE name = ?\").get(name) as\n | { id: number }\n | undefined;\n if (!created) throw new Error(`failed to create workstream row for ${name}`);\n return created.id;\n}\n\n/** Surrogate id for a task natural key, or null when absent. */\nfunction taskRowId(db: Db, key: string): number | null {\n const { workstream, localId } = parseTaskKey(key);\n const row = db\n .prepare(\n `SELECT t.id AS id\n FROM tasks t\n JOIN workstreams w ON w.id = t.workstream_id\n WHERE w.name = ? AND t.local_id = ?`,\n )\n .get(workstream, localId) as { id: number } | undefined;\n return row?.id ?? null;\n}\n\n// ─── Entity appliers ──────────────────────────────────────────────────\n\n/** PER-FIELD LWW for one row of a table with a natural key.\n *\n * For each field in the payload, apply it only if this op's HLC beats\n * the newest HLC that previously wrote THAT FIELD. Fields are\n * independent, so one op can win on `status` and lose on `impact` in\n * the same call — which is exactly the concurrent-crew case. */\nfunction applyFieldLww(\n db: Db,\n op: Op,\n table: \"tasks\" | \"workstreams\",\n rowId: number,\n entries: readonly PayloadEntry[],\n): string[] {\n const applied: string[] = [];\n for (const [field, value] of entries) {\n if (NEVER_APPLY.has(field)) continue;\n if (!wins(op.hlc, fieldHlc(db, op, field))) continue;\n\n // The HLC says this op may write the field. Before doing so, check\n // whether the field ALREADY holds this value.\n //\n // This is what makes re-application a true no-op rather than a\n // redundant rewrite that merely happens to be value-stable. The HLC\n // comparison alone cannot tell \"first apply\" from \"second apply\",\n // because provenance deliberately excludes the op's own HLC (so that\n // record-then-apply and apply-then-record behave identically). Value\n // equality is the honest signal: nothing changed, so report nothing\n // changed. Callers use `changed` to decide whether to log or refresh,\n // and a spurious true would make `mu sync` claim work it did not do.\n //\n // Identifier is not user input: `field` is filtered against the\n // table's real column list by `filterAppliable` before we get here.\n const current = db.prepare(`SELECT ${field} AS v FROM ${table} WHERE id = ?`).get(rowId) as\n | { v: string | number | null }\n | undefined;\n if (current !== undefined && current.v === value) continue;\n\n db.prepare(`UPDATE ${table} SET ${field} = ? WHERE id = ?`).run(value, rowId);\n applied.push(field);\n }\n return applied;\n}\n\n/** The columns each portable table will accept from an op. Anything\n * else in a payload is ignored rather than fatal: a peer on a newer mu\n * may legitimately send fields we do not know yet, and dropping them\n * is strictly better than refusing the whole op. */\nconst APPLIABLE_COLUMNS: Record<\"tasks\" | \"workstreams\", readonly string[]> = {\n tasks: [\"title\", \"status\", \"substate\", \"impact\", \"effort_days\", \"created_at\", \"updated_at\"],\n workstreams: [\"created_at\"],\n};\n\nfunction filterAppliable(\n table: \"tasks\" | \"workstreams\",\n entries: readonly PayloadEntry[],\n): PayloadEntry[] {\n const allowed = APPLIABLE_COLUMNS[table];\n return entries.filter(([field]) => allowed.includes(field));\n}\n\nfunction applyTaskPut(db: Db, op: Op): ApplyResult {\n const { workstream, localId } = parseTaskKey(op.key);\n // Decode the pair out of the payload. `substate` is not applied by\n // per-field LWW: repairTaskPair derives it from the log (see there),\n // with this op passed in because the caller may not have recorded it\n // yet. A legacy status (v9 peers, retained history) maps onto a pair;\n // only the decoded projection changes, the payload is recorded as-is.\n // A status this build does not know is dropped like an unknown field.\n const entries: PayloadEntry[] = [];\n for (const entry of filterAppliable(\"tasks\", decodePayload(op.payload))) {\n const [field, value] = entry;\n if (field === \"substate\") continue;\n if (field === \"status\") {\n if (typeof value !== \"string\") continue;\n const legacy = mapLegacyStatus(value);\n if (legacy) entries.push([\"status\", legacy.status]);\n else if (isTaskStatus(value)) entries.push(entry);\n continue;\n }\n entries.push(entry);\n }\n const pending = { hlc: op.hlc, substate: carriedSubstate(JSON.parse(op.payload)) };\n\n const existing = taskRowId(db, op.key);\n if (existing === null) {\n // Row absent. Either it was never created here, or it was deleted.\n // A tombstone NEWER than this put means the delete wins and the row\n // stays gone; an OLDER tombstone means this is a legitimate\n // resurrection. Identical comparison either way — that is the point\n // of tombstones being ordinary ops.\n const tomb = tombstoneHlc(db, op);\n if (tomb !== null && !wins(op.hlc, tomb)) {\n return { changed: false, appliedFields: [], skipped: \"older-than-tombstone\" };\n }\n // Create the row, then let per-field LWW fill it in. The insert uses\n // placeholder values for NOT NULL columns so a partial payload\n // cannot fail the insert; every field the op actually carries is\n // written by applyFieldLww below, and any it does not carry will be\n // filled by whichever op does.\n const wsId = ensureWorkstreamRow(db, workstream);\n const now = new Date().toISOString();\n db.prepare(\n `INSERT INTO tasks (workstream_id, local_id, title, status, substate, impact, effort_days,\n owner_id, created_at, updated_at)\n VALUES (?, ?, ?, 'OPEN', 'todo', 50, 1, NULL, ?, ?)`,\n ).run(wsId, localId, localId, now, now);\n const rowId = taskRowId(db, op.key);\n if (rowId === null) throw new Error(`failed to create task row for ${op.key}`);\n // Force every carried field on: the row was just created, so there\n // is no incumbent value worth preserving, but the HLC comparison\n // still runs so a stale op cannot overwrite a newer one that\n // arrived first and created the row.\n const applied = applyFieldLww(db, op, \"tasks\", rowId, entries);\n if (repairTaskPair(db, rowId, pending)) applied.push(\"substate\");\n return { changed: true, appliedFields: applied };\n }\n\n const applied = applyFieldLww(db, op, \"tasks\", existing, entries);\n if (repairTaskPair(db, existing, pending)) applied.push(\"substate\");\n return applied.length > 0\n ? { changed: true, appliedFields: applied }\n : { changed: false, appliedFields: [], skipped: \"older-than-current\" };\n}\n\n/** The substate an op payload writes, or null if it writes none.\n *\n * - A legacy status writes the substate it maps to, overriding any\n * substate beside it (mirrors resolvePair).\n * - A known status with no substate writes the status's default.\n * Capture records only changed columns, and the substate sets of the\n * statuses are disjoint, so a v11 status change always carries its\n * substate. A status-only op therefore comes from a v10 peer or\n * pre-v11 history, where every status had exactly its default.\n * - Otherwise the explicit substate, or null. */\nfunction carriedSubstate(payload: unknown): string | null {\n if (payload === null || typeof payload !== \"object\") return null;\n const { status, substate } = payload as Record<string, unknown>;\n if (typeof status === \"string\") {\n const legacy = mapLegacyStatus(status);\n if (legacy) return legacy.substate;\n if (typeof substate !== \"string\" && isTaskStatus(status)) return DEFAULT_SUBSTATE[status];\n }\n return typeof substate === \"string\" ? substate : null;\n}\n\n/**\n * Set a task's substate to the one its op log implies, and keep the\n * (status, substate) pair legal for the deferred FK. Returns true iff it\n * rewrote the row.\n *\n * The substate comes from the newest (by HLC) op that writes one — see\n * carriedSubstate for what counts — resolved against the row's CURRENT\n * status. An unknown substate (newer peer) or one illegal for the\n * status (a concurrent close vs park, where status and substate come\n * from different ops) falls back to DEFAULT_SUBSTATE[status].\n *\n * It reads the LOG, not the row, on purpose. A repair that only looked\n * at the row would bake a fallback into it, and a later op that changes\n * status back would keep the fallback on one arrival order and the real\n * substate on another. Reading the log makes the result a pure function\n * of the set of ops plus the row's status (itself plain LWW), so every\n * peer that applies the same ops reaches the same row. That is why the\n * repair records no op: on the apply path it runs inside\n * withCaptureSuppressed, and every peer computes the same repair on its\n * own. On the undo path capture is live, and the write is recorded as\n * part of the undo group.\n *\n * `pending` is the op being applied, which the caller may not have\n * recorded yet (ingest applies before recording).\n */\nexport function repairTaskPair(\n db: Db,\n rowId: number,\n pending?: { hlc: string; substate: string | null },\n): boolean {\n const row = db\n .prepare(\n `SELECT t.status, t.substate, w.name || '/' || t.local_id AS key\n FROM tasks t JOIN workstreams w ON w.id = t.workstream_id\n WHERE t.id = ?`,\n )\n .get(rowId) as { status: string; substate: string; key: string } | undefined;\n if (!row) return false;\n\n let winner: { hlc: string; substate: string } | null = null;\n const writers = db\n .prepare(\n `SELECT hlc, payload FROM ops\n WHERE entity = 'task' AND key = ? AND op = 'put' AND hlc <> ?\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n AND (json_type(payload, '$.substate') IS NOT NULL\n OR json_type(payload, '$.status') IS NOT NULL)\n ORDER BY hlc DESC`,\n )\n .iterate(row.key, pending?.hlc ?? \"\") as Iterable<{ hlc: string; payload: string }>;\n for (const w of writers) {\n const substate = carriedSubstate(JSON.parse(w.payload));\n if (substate === null) continue;\n winner = { hlc: w.hlc, substate };\n break;\n }\n if (pending && pending.substate !== null && wins(pending.hlc, winner?.hlc ?? null)) {\n winner = { hlc: pending.hlc, substate: pending.substate };\n }\n\n const target = resolvePair(row.status, winner?.substate ?? row.substate);\n if (target === null || target.substate === row.substate) return false;\n db.prepare(\"UPDATE tasks SET substate = ? WHERE id = ?\").run(target.substate, rowId);\n return true;\n}\n\nfunction applyWorkstreamPut(db: Db, op: Op): ApplyResult {\n const name = op.key;\n if (name.length === 0) throw new OpKeyMalformedError(\"workstream\", name);\n const entries = filterAppliable(\"workstreams\", decodePayload(op.payload));\n\n const existing = db.prepare(\"SELECT id FROM workstreams WHERE name = ?\").get(name) as\n | { id: number }\n | undefined;\n if (!existing) {\n const tomb = tombstoneHlc(db, op);\n if (tomb !== null && !wins(op.hlc, tomb)) {\n return { changed: false, appliedFields: [], skipped: \"older-than-tombstone\" };\n }\n const id = ensureWorkstreamRow(db, name);\n const applied = applyFieldLww(db, op, \"workstreams\", id, entries);\n return { changed: true, appliedFields: applied };\n }\n const applied = applyFieldLww(db, op, \"workstreams\", existing.id, entries);\n return applied.length > 0\n ? { changed: true, appliedFields: applied }\n : { changed: false, appliedFields: [], skipped: \"older-than-current\" };\n}\n\n/** GROW-ONLY SET. A note is written once and never updated, so there is\n * no conflict to resolve — only \"is it already here\".\n *\n * IDENTITY IS THE NOTE'S CONTENT, not a surrogate id. A note's local\n * `task_notes.id` is assigned by whichever machine inserted it, so the\n * same logical note has different ids on different machines and the id\n * cannot be the identity. The op key embeds the ORIGIN id\n * (`demo/a#12`), which identifies it on its origin machine, but a peer\n * cannot map that onto a local row without storing a mapping table.\n *\n * So identity is `(task, author, content, created_at)`. Notes are\n * immutable, and every copy of one carries the created_at its origin\n * stamped, so applying a note that is already here is a no-op.\n *\n * `created_at` is in the identity because text alone is not unique.\n * Machine-written notes repeat: the reaper writes the same \"previous\n * owner gone\" line each time it reaps a task, and dispatch writes\n * \"CLAIM: dispatch\" on every claim. Keyed on text alone, a repeat\n * collapsed into the first copy on every peer while the origin kept\n * both, so note counts drifted apart one repeat at a time (a real\n * fleet: 6359 vs 6360). Two notes identical to the millisecond still\n * converge to one row, which is a dedupe rather than a loss.\n *\n * Undo, migrations and reprojection re-emit a note with the created_at\n * it already had, so they still land on the existing row. */\nfunction applyNotePut(db: Db, op: Op): ApplyResult {\n const { taskKey } = parseNoteKey(op.key);\n const taskId = taskRowId(db, taskKey);\n if (taskId === null) {\n // Parent task not here — either its op has not arrived yet, or it\n // was deleted. Notes are FK-bound to a task, so there is nowhere to\n // put this. Skipped rather than fatal: re-reading the peer's segment\n // replays the task op first and this lands then.\n return { changed: false, appliedFields: [], skipped: \"absent\" };\n }\n\n const entries = decodePayload(op.payload);\n const author = entries.find(([f]) => f === \"author\")?.[1] ?? null;\n const content = entries.find(([f]) => f === \"content\")?.[1] ?? \"\";\n const stamped = entries.find(([f]) => f === \"created_at\")?.[1];\n const createdAt = stamped ?? new Date().toISOString();\n const authorText = author === null ? null : String(author);\n\n // A put without created_at (capture always writes one) is matched on\n // text alone: stamping it with \"now\" and then matching on that would\n // make every re-delivery look new.\n const already = db\n .prepare(\n `SELECT 1 AS present FROM task_notes\n WHERE task_id = @taskId AND content = @content\n AND COALESCE(author, '') = COALESCE(@author, '')\n AND (@stamped IS NULL OR created_at = @stamped)\n LIMIT 1`,\n )\n .get({\n taskId,\n content: String(content),\n author: authorText,\n stamped: stamped === undefined || stamped === null ? null : String(stamped),\n }) as { present: number } | undefined;\n if (already) return { changed: false, appliedFields: [], skipped: \"already-present\" };\n\n // A note deleted by a NEWER tombstone must not be re-inserted by a\n // late-arriving put. Same comparison as every other entity.\n const tomb = tombstoneHlc(db, op);\n if (tomb !== null && !wins(op.hlc, tomb)) {\n return { changed: false, appliedFields: [], skipped: \"older-than-tombstone\" };\n }\n\n db.prepare(\n \"INSERT INTO task_notes (task_id, author, content, created_at) VALUES (?, ?, ?, ?)\",\n ).run(taskId, authorText, String(content), String(createdAt));\n return { changed: true, appliedFields: [\"content\"] };\n}\n\n/** LWW-ELEMENT-SET. An edge is a set member: present or absent, with no\n * fields worth merging. So add and remove each carry an HLC and the\n * newer one wins — a remove followed by a re-add converges to present,\n * and the reverse converges to absent, in either arrival order. */\nfunction applyEdgePut(db: Db, op: Op): ApplyResult {\n const { blocker, blocked } = parseEdgeKey(op.key);\n const fromId = taskRowId(db, blocker);\n const toId = taskRowId(db, blocked);\n if (fromId === null || toId === null) {\n return { changed: false, appliedFields: [], skipped: \"absent\" };\n }\n const removedAt = anyHlc(db, op, \"del\");\n if (removedAt !== null && !wins(op.hlc, removedAt)) {\n return { changed: false, appliedFields: [], skipped: \"older-than-tombstone\" };\n }\n const existing = db\n .prepare(\"SELECT 1 AS present FROM task_edges WHERE from_task_id = ? AND to_task_id = ?\")\n .get(fromId, toId) as { present: number } | undefined;\n if (existing) return { changed: false, appliedFields: [], skipped: \"already-present\" };\n\n const entries = decodePayload(op.payload);\n const createdAt = entries.find(([f]) => f === \"created_at\")?.[1] ?? new Date().toISOString();\n db.prepare(\"INSERT INTO task_edges (from_task_id, to_task_id, created_at) VALUES (?, ?, ?)\").run(\n fromId,\n toId,\n String(createdAt),\n );\n return { changed: true, appliedFields: [\"created_at\"] };\n}\n\n/** A tombstone. Applies iff it is newer than every put for this key —\n * the mirror image of the put path's tombstone check, which is why\n * there is no special casing between them. */\nfunction applyDel(db: Db, op: Op): ApplyResult {\n const newestPut = anyHlc(db, op, \"put\");\n if (newestPut !== null && !wins(op.hlc, newestPut)) {\n // A put NEWER than this delete already happened: the row is alive\n // and must stay so. The tombstone is still recorded in `ops` (by\n // the caller), so it participates in later comparisons.\n return { changed: false, appliedFields: [], skipped: \"older-than-current\" };\n }\n\n switch (op.entity) {\n case \"workstream\": {\n // FK CASCADE removes the tasks, notes and edges beneath it.\n const r = db.prepare(\"DELETE FROM workstreams WHERE name = ?\").run(op.key);\n return {\n changed: r.changes > 0,\n appliedFields: [],\n ...(r.changes > 0 ? {} : { skipped: \"absent\" as const }),\n };\n }\n case \"task\": {\n const rowId = taskRowId(db, op.key);\n if (rowId === null) return { changed: false, appliedFields: [], skipped: \"absent\" };\n db.prepare(\"DELETE FROM tasks WHERE id = ?\").run(rowId);\n return { changed: true, appliedFields: [] };\n }\n case \"note\": {\n const { taskKey } = parseNoteKey(op.key);\n const taskId = taskRowId(db, taskKey);\n if (taskId === null) return { changed: false, appliedFields: [], skipped: \"absent\" };\n // A note's local surrogate id is not the origin's, so delete by\n // the content identity the grow-only insert used. A self-describing\n // tombstone (capture writes the full row when no put shares its\n // key, drift-641) carries that identity itself; otherwise read it\n // from the put under the same key.\n let entries = decodePayload(op.payload);\n if (!entries.some(([f, v]) => f === \"content\" && v !== null)) {\n const src = db\n .prepare(\n `SELECT payload FROM ops\n WHERE entity = 'note' AND key = @key AND op = 'put'\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n ORDER BY hlc DESC LIMIT 1`,\n )\n .get({ key: op.key }) as { payload: string } | undefined;\n if (!src) return { changed: false, appliedFields: [], skipped: \"absent\" };\n entries = decodePayload(src.payload);\n }\n const content = entries.find(([f]) => f === \"content\")?.[1] ?? \"\";\n const author = entries.find(([f]) => f === \"author\")?.[1] ?? null;\n const createdAt = entries.find(([f]) => f === \"created_at\")?.[1] ?? null;\n // created_at narrows the delete to this note, not every repeat of\n // its text. A put without one (never written by capture) falls\n // back to text alone.\n const r = db\n .prepare(\n `DELETE FROM task_notes\n WHERE task_id = @taskId AND content = @content\n AND COALESCE(author, '') = COALESCE(@author, '')\n AND (@createdAt IS NULL OR created_at = @createdAt)`,\n )\n .run({\n taskId,\n content: String(content),\n author: author === null ? null : String(author),\n createdAt: createdAt === null ? null : String(createdAt),\n });\n return {\n changed: r.changes > 0,\n appliedFields: [],\n ...(r.changes > 0 ? {} : { skipped: \"absent\" as const }),\n };\n }\n case \"edge\": {\n const { blocker, blocked } = parseEdgeKey(op.key);\n const fromId = taskRowId(db, blocker);\n const toId = taskRowId(db, blocked);\n if (fromId === null || toId === null) {\n return { changed: false, appliedFields: [], skipped: \"absent\" };\n }\n const r = db\n .prepare(\"DELETE FROM task_edges WHERE from_task_id = ? AND to_task_id = ?\")\n .run(fromId, toId);\n return {\n changed: r.changes > 0,\n appliedFields: [],\n ...(r.changes > 0 ? {} : { skipped: \"absent\" as const }),\n };\n }\n default:\n // 'message' has no portable table to delete from.\n return { changed: false, appliedFields: [], skipped: \"absent\" };\n }\n}\n\n// ─── The seam ─────────────────────────────────────────────────────────\n\n/**\n * Apply one op to the portable tables.\n *\n * SYNCHRONOUS, for the same reason `withOpContext` is: the op context is\n * a per-connection temp table, so two interleaved async apply scopes\n * would clobber each other's `applying` flag with no way to tell whose\n * suppression was in force. Keeping this sync makes that\n * unrepresentable rather than merely discouraged.\n *\n * Runs inside `withCaptureSuppressed`, which is THE echo guard: without\n * it, writing a peer's op to `tasks` would fire the capture trigger,\n * mint a fresh local op, flush that back to the peer, and loop forever.\n *\n * Does NOT insert the op into `ops` — that is the caller's job (v2-sync,\n * which owns segment bookkeeping and the (machine_id, hlc) dedupe). This\n * function is deliberately only \"make the tables reflect this op\", so it\n * can be called on a replay of ops already in the log without\n * double-recording them. Provenance queries exclude the op's own HLC\n * precisely so the order of those two steps does not matter.\n *\n * Idempotent: applying the same op twice makes no second change, which\n * is what lets `mu sync --repair` be nothing more than \"re-read that\n * peer's segment from zero\".\n */\nexport function applyOp(db: Db, op: Op): ApplyResult {\n if (isKnownMachineLocalEntity(op.entity)) throw new OpEntityNotSyncedError(op.entity);\n // An entity this build does not recognise is a FORWARD-COMPATIBLE\n // no-op, projected nowhere, exactly like 'message'. Recording the op\n // is the caller's step and is what preserves it for a later build\n // (and for `mu rebuild`) without blocking every op behind it.\n if (!isSyncedEntity(op.entity)) return { changed: false, appliedFields: [] };\n\n return withCaptureSuppressed(db, () => {\n if (op.op === \"del\") return applyDel(db, op);\n\n switch (op.entity) {\n case \"workstream\":\n return applyWorkstreamPut(db, op);\n case \"task\":\n // Own transaction (a savepoint when nested): the per-field\n // UPDATEs pass through an invalid (status, substate) pair, and\n // the deferred FK only tolerates that inside a transaction. In\n // autocommit it would check each UPDATE on its own.\n return db.transaction(() => applyTaskPut(db, op)).immediate();\n case \"note\":\n return applyNotePut(db, op);\n case \"edge\":\n return applyEdgePut(db, op);\n case \"message\":\n // Log lines live in `ops` only; there is no portable table to\n // project them into. Recording the op IS applying it, and that\n // is the caller's step.\n return { changed: false, appliedFields: [] };\n default:\n // Unreachable: the union is closed and every member is handled\n // above. Kept as a total return rather than a throw so an\n // unrecognised entity can never wedge an ingest.\n return { changed: false, appliedFields: [] };\n }\n });\n}\n\n// ─── Deferred projection: the out-of-order arrival repair ─────────────\n\n/**\n * Re-project note and edge ops that could not land when they arrived.\n *\n * THE BUG THIS EXISTS TO FIX (v2-sync-workflow-integration)\n * ---------------------------------------------------------\n * `applyNotePut` / `applyEdgePut` return `skipped:\"absent\"` when the\n * task they hang off is not here YET. That is the right answer at that\n * moment — there is genuinely nowhere to put the row — and their\n * comments promised \"re-reading the peer's segment replays the task op\n * first and this lands then\". It does not. `ingestSegment` counts an\n * `absent` skip as a successful apply and ADVANCES THE WATERMARK past\n * the line, so the segment is never re-read and the note or edge is\n * never projected. The op IS recorded in local `ops`, so the data is\n * not lost — but the live tables silently diverge, permanently.\n *\n * It is not a corner case. `flushSegment` ships only LOCAL ops, so a\n * machine's segment routinely holds `edge`/`note` ops referring to\n * tasks created in ANOTHER machine's segment, and `syncPass` reads\n * peers in `discoverPeers` order — `localeCompare` over random UUID\n * filenames. Whether the parent arrives first is a coin flip. Measured\n * on 8 fresh fleets: 5 dropped the edge, 3 kept it, correlating exactly\n * with segment filename sort order.\n *\n * WHY REPAIR FROM THE LOG RATHER THAN A RETRY QUEUE\n * -------------------------------------------------\n * A queue of \"ops to try again\" would be a second source of truth that\n * can disagree with `ops` — the same denormalisation this module's\n * header rejects for provenance, and it would be lost across processes\n * (mu is one short-lived process per invocation, so an in-memory list\n * only ever fixes ops deferred within a SINGLE pass; the parent often\n * arrives the next day). The ops log already knows everything needed,\n * so this asks it directly: which note/edge puts are resolvable NOW but\n * are not projected?\n *\n * \"Resolvable now\" is doing real work in both queries. Restricting to\n * ops whose parent task rows EXIST means an op orphaned for good (its\n * task was deleted) is not retried on every pass forever, and excluding\n * keys with a NEWER `del` means a legitimately removed edge or note is\n * not resurrected. What is left is exactly the bug's footprint, which\n * in a healthy DB is the empty set — so the normal cost is two indexed\n * queries returning zero rows.\n *\n * Returns the number of ops that changed something, so callers can\n * report it and tests can assert the repair actually fired.\n */\nexport function reprojectDeferredOps(db: Db): number {\n installOpKeyFunctions(db);\n const rows = [\n ...(db\n .prepare(\n `SELECT o.hlc, o.machine_id, o.group_id, o.actor, o.intent, o.entity,\n o.key, o.op, o.payload\n FROM ops o\n JOIN tasks ft ON ft.local_id = edge_local(o.key, 0)\n JOIN workstreams fw ON fw.id = ft.workstream_id AND fw.name = edge_ws(o.key, 0)\n JOIN tasks tt ON tt.local_id = edge_local(o.key, 1)\n JOIN workstreams tw ON tw.id = tt.workstream_id AND tw.name = edge_ws(o.key, 1)\n WHERE o.entity = 'edge' AND o.op = 'put'\n AND NOT EXISTS (SELECT 1 FROM task_edges e\n WHERE e.from_task_id = ft.id AND e.to_task_id = tt.id)\n AND NOT EXISTS (SELECT 1 FROM ops d\n WHERE d.entity = 'edge' AND d.key = o.key\n AND d.op = 'del' AND d.hlc > o.hlc)`,\n )\n .all() as OpRow[]),\n ...(db\n .prepare(\n `SELECT o.hlc, o.machine_id, o.group_id, o.actor, o.intent, o.entity,\n o.key, o.op, o.payload\n FROM ops o\n JOIN tasks t ON t.local_id = note_local(o.key)\n JOIN workstreams w ON w.id = t.workstream_id AND w.name = note_ws(o.key)\n WHERE o.entity = 'note' AND o.op = 'put'\n AND NOT EXISTS (SELECT 1 FROM task_notes n\n WHERE n.task_id = t.id\n AND n.content = COALESCE(json_extract(o.payload, '$.content'), '')\n AND COALESCE(n.author, '')\n = COALESCE(json_extract(o.payload, '$.author'), '')\n AND n.created_at = json_extract(o.payload, '$.created_at'))\n AND NOT EXISTS (SELECT 1 FROM ops d\n WHERE d.entity = 'note' AND d.key = o.key\n AND d.op = 'del' AND d.hlc > o.hlc)`,\n )\n .all() as OpRow[]),\n ];\n if (rows.length === 0) return 0;\n\n let changed = 0;\n for (const row of rows.sort((a, b) => compareHlc(a.hlc, b.hlc))) {\n if (applyOp(db, rowToOp(row)).changed) changed += 1;\n }\n return changed;\n}\n\n/** Raw `ops` row shape, as the two repair queries above return it. */\ninterface OpRow {\n hlc: string;\n machine_id: string;\n group_id: string;\n actor: string | null;\n intent: string | null;\n entity: string;\n key: string;\n op: string;\n payload: string;\n}\n\nfunction rowToOp(row: OpRow): Op {\n return {\n hlc: row.hlc,\n machineId: row.machine_id,\n groupId: row.group_id,\n actor: row.actor,\n intent: row.intent,\n entity: row.entity,\n key: row.key,\n op: row.op === \"del\" ? \"del\" : \"put\",\n payload: row.payload,\n };\n}\n\n/**\n * Register the four key-splitting SQL functions the repair queries use.\n *\n * Natural keys are structured text (`<ws>/<local_id>`,\n * `<blocker>-><blocked>`, `<taskKey>#<originId>`) and the parsers here\n * already handle the awkward cases — a local_id containing `/`, an id\n * ending in `-` so `a-->b` splits correctly. Rather than reimplement\n * that splitting in SQL string functions (where it would be a second,\n * subtly different parser), the SAME TypeScript parsers are exposed to\n * SQLite. Deterministic, so SQLite may cache and index against them.\n *\n * Registered lazily by `reprojectDeferredOps` rather than by `openDb`,\n * because `db.ts` imports `capture.ts` and `apply.ts` imports `db.ts` —\n * so an `openDb`-side registration would need db.ts to import this\n * module and close a runtime import cycle. (capture.ts's header records\n * what that costs: `node dist/cli.js` printing nothing at all.)\n * Re-registration is a documented no-op-ish overwrite in better-sqlite3,\n * verified, so calling it per repair pass is safe and cheap.\n */\nfunction installOpKeyFunctions(db: Db): void {\n const safe = <T>(f: () => T): T | null => {\n try {\n return f();\n } catch {\n // A malformed key is not this query's problem: return NULL so the\n // JOIN drops the row rather than failing the whole ingest.\n return null;\n }\n };\n const opts = { deterministic: true, varargs: false } as const;\n db.function(\"edge_ws\", opts, (key: unknown, side: unknown) =>\n safe(() => {\n const parts = parseEdgeKey(String(key));\n return parseTaskKey(Number(side) === 0 ? parts.blocker : parts.blocked).workstream;\n }),\n );\n db.function(\"edge_local\", opts, (key: unknown, side: unknown) =>\n safe(() => {\n const parts = parseEdgeKey(String(key));\n return parseTaskKey(Number(side) === 0 ? parts.blocker : parts.blocked).localId;\n }),\n );\n db.function(\"note_ws\", opts, (key: unknown) =>\n safe(() => parseTaskKey(parseNoteKey(String(key)).taskKey).workstream),\n );\n db.function(\"note_local\", opts, (key: unknown) =>\n safe(() => parseTaskKey(parseNoteKey(String(key)).taskKey).localId),\n );\n}\n\n/**\n * Apply many ops in HLC order, inside one transaction.\n *\n * HLC order is what makes per-field LWW correct without any extra\n * bookkeeping: process oldest-first and each field simply ends up\n * holding the newest write. Callers may pass ops in arrival order.\n *\n * Grow-only entities are order-insensitive by construction, so a single\n * sort is enough for every rule here.\n */\nexport function applyOps(db: Db, ops: readonly Op[]): ApplyResult[] {\n const ordered = [...ops].sort((a, b) => compareHlc(a.hlc, b.hlc));\n const run = db.transaction(() => ordered.map((op) => applyOp(db, op)));\n return run.immediate();\n}\n","// mu — task-DAG read + ASCII forest rendering helpers.\n//\n// Shared by the static `mu task tree` command and the read-only TUI DAG\n// popup. Pure rendering lives here so the box-drawing characters and\n// diamond-collapse semantics have one implementation.\n\nimport pc from \"picocolors\";\nimport type { Db } from \"./db.js\";\nimport type { TaskStatus } from \"./tasks/status.js\";\nimport { listTasks, type TaskRow } from \"./tasks.js\";\n\n// One-line marker appended to a tree node when its subtree was already\n// rendered earlier in the forest (DAG diamond collapse). Symbol-only\n// + dimmed: the ↻ glyph carries the recurrence semantics, the dim\n// keeps it from competing with the task title for the eye.\nconst RECURRENCE_MARKER = ` ${pc.dim(\"(↻)\")}`;\n\nexport interface FullDag {\n /** Root tasks: no incoming `blocks` edge (no blockers). */\n roots: TaskRow[];\n /** Edges map parent task name → child task names (what parent blocks). */\n edges: Map<string, string[]>;\n /** All tasks in the workstream, keyed by operator-facing name. */\n tasks: Map<string, TaskRow>;\n}\n\nexport type TaskStatusLabelFn = (task: TaskRow) => string;\n\nexport interface RenderTreeOptions {\n /** Include the task title after the name + status label. Default: true. */\n includeTitle?: boolean;\n}\n\nexport interface LoadFullDagOptions {\n /** Optional visible-status filter. Omitted = every task status. */\n statuses?: ReadonlySet<TaskStatus>;\n /** Optional row predicate, applied after `statuses` (e.g. the TUI's\n * substate toggles). Omitted = keep every row. */\n include?: (task: TaskRow) => boolean;\n}\n\nexport function loadFullDag(db: Db, workstream: string, opts: LoadFullDagOptions = {}): FullDag {\n const tasks = listTasks(db, workstream).filter(\n (t) =>\n (opts.statuses === undefined || opts.statuses.has(t.status)) &&\n (opts.include === undefined || opts.include(t)),\n );\n const byName = new Map(tasks.map((t) => [t.name, t]));\n const incoming = new Set<string>();\n const edges = new Map<string, string[]>();\n\n for (const task of tasks) {\n edges.set(task.name, []);\n }\n\n // Start from task_edges deliberately. With ordinary JOINs plus the\n // same-workstream guard below, SQLite chose tasks(src) × tasks(dst)\n // before probing the edge PK — quadratic in workstream size. CROSS JOIN\n // fixes the loop order at edges → two INTEGER-PK lookups while retaining\n // the guard against malformed cross-workstream rows.\n const rows = db\n .prepare(\n `SELECT src.local_id AS parent, dst.local_id AS child\n FROM task_edges e\n CROSS JOIN tasks src\n CROSS JOIN tasks dst\n WHERE src.id = e.from_task_id\n AND dst.id = e.to_task_id\n AND src.workstream_id = (SELECT id FROM workstreams WHERE name = ?)\n AND dst.workstream_id = src.workstream_id\n ORDER BY src.local_id, dst.local_id`,\n )\n .all(workstream) as { parent: string; child: string }[];\n\n for (const row of rows) {\n if (!byName.has(row.parent) || !byName.has(row.child)) continue;\n incoming.add(row.child);\n const children = edges.get(row.parent) ?? [];\n children.push(row.child);\n edges.set(row.parent, children);\n }\n\n const roots = tasks.filter((t) => !incoming.has(t.name));\n return { roots, edges, tasks: byName };\n}\n\n/**\n * Render a DAG forest in the same ASCII shape as `mu task tree --down`:\n * each root is printed as a header node, dependents are below it, and\n * DAG diamonds collapse after the first full subtree render with a\n * one-line recurrence marker.\n */\nexport function renderForest(\n roots: readonly TaskRow[],\n edges: ReadonlyMap<string, readonly string[]>,\n statusFn: TaskStatusLabelFn,\n tasksByName?: ReadonlyMap<string, TaskRow>,\n opts: RenderTreeOptions = {},\n): string {\n const byName = new Map(tasksByName ?? roots.map((t) => [t.name, t]));\n const seen = new Set<string>();\n const sections: string[] = [];\n\n for (const root of roots) {\n if (!byName.has(root.name)) byName.set(root.name, root);\n const lines = [formatTreeNodeLabel(root, statusFn, opts)];\n if (seen.has(root.name)) {\n lines[0] = `${lines[0]}${RECURRENCE_MARKER}`;\n } else {\n seen.add(root.name);\n renderForestChildren(root.name, \"\", edges, byName, statusFn, seen, lines, opts);\n }\n sections.push(lines.join(\"\\n\"));\n }\n\n return sections.join(\"\\n\\n\");\n}\n\nexport function renderTaskTree(\n db: Db,\n workstream: string,\n root: TaskRow,\n direction: \"blockers\" | \"dependents\",\n statusFn: TaskStatusLabelFn,\n opts: RenderTreeOptions = {},\n): string {\n const dag = loadFullDag(db, workstream);\n if (direction === \"dependents\") {\n return renderForest([root], dag.edges, statusFn, dag.tasks, opts);\n }\n\n const blockers = new Map<string, string[]>([...dag.tasks.keys()].map((name) => [name, []]));\n for (const [blocker, dependents] of dag.edges) {\n for (const dependent of dependents) blockers.get(dependent)?.push(blocker);\n }\n for (const names of blockers.values()) names.sort();\n return renderForest([root], blockers, statusFn, dag.tasks, opts);\n}\n\nfunction renderForestChildren(\n taskName: string,\n prefix: string,\n edges: ReadonlyMap<string, readonly string[]>,\n byName: Map<string, TaskRow>,\n statusFn: TaskStatusLabelFn,\n seen: Set<string>,\n lines: string[],\n opts: RenderTreeOptions,\n): void {\n const children = edges.get(taskName) ?? [];\n for (let i = 0; i < children.length; i++) {\n const childName = children[i];\n if (childName === undefined) continue;\n const isLast = i === children.length - 1;\n const branch = isLast ? \"└── \" : \"├── \";\n const childPrefix = prefix + (isLast ? \" \" : \"│ \");\n const child = byName.get(childName);\n\n if (!child) {\n lines.push(`${prefix}${branch}${childName} (missing!)`);\n continue;\n }\n\n if (seen.has(childName)) {\n lines.push(\n `${prefix}${branch}${formatTreeNodeLabel(child, statusFn, opts)}${RECURRENCE_MARKER}`,\n );\n continue;\n }\n\n lines.push(`${prefix}${branch}${formatTreeNodeLabel(child, statusFn, opts)}`);\n seen.add(childName);\n renderForestChildren(childName, childPrefix, edges, byName, statusFn, seen, lines, opts);\n }\n}\n\nexport function formatTreeNodeLabel(\n t: TaskRow,\n statusFn: TaskStatusLabelFn,\n opts: RenderTreeOptions = {},\n): string {\n const base = `${t.name} ${statusFn(t)}`;\n if (opts.includeTitle === false) return base;\n return `${base} ${t.title}`;\n}\n","// Doctor summary — a TUI-friendly slice of `mu doctor`'s checks.\n//\n// The full `mu doctor` verb (src/cli/doctor.ts) renders a textual card\n// with: tmux/env presence, DB schema integrity, schema_version,\n// journal_mode, foreign_keys, per-workstream agent/task/log counts,\n// and reconcile drift (would-be-pruned ghosts + orphan panes). The\n// TUI's slot-9 Doctor card needs the same SIGNAL but at poll-tick\n// cost: an array of checks the card can render row-by-row.\n//\n// Per feat_card_9_doctor (workstream `tui-impl`), promoted from the\n// last reserved slot in design_global_keymap.\n//\n// CHEAPNESS RULES (so this is safe to run on every snapshot tick):\n// - Synchronous DB pragmas + COUNT(*)-shape SELECTs only.\n// - Reads `view.report.prunedGhosts` and `view.orphans` straight\n// out of the WorkstreamSnapshot — `loadWorkstreamSnapshot`\n// already runs `listLiveAgents(...)` once per tick, which populates\n// `report.prunedGhosts`.\n// - Reads `workspaceOrphans` straight out of the snapshot too.\n// - NO subprocess shellouts. The `tmux -V` check from the textual\n// `mu doctor` is intentionally OMITTED here — the TUI is\n// already running inside a terminal so tmux's binary presence\n// is implicit, and adding a per-tick subprocess fork to a\n// polled dashboard is the wrong tradeoff. If a future task\n// wants tmux-presence on the dashboard, add it as a\n// once-per-session probe in <App> (mirrors probeClipboardBackend),\n// not as a per-tick check here.\n//\n// CHECK SHAPE (matches the textual doctor's per-row vocabulary):\n// { name, status: \"ok\" | \"warn\" | \"fail\", detail }\n//\n// The card filters to non-OK rows for its body; subtitle counts\n// warn+fail. When everything is OK the card renders a quiet\n// \"all healthy\" line so the operator's eye learns to read the\n// presence of rows as \"something needs attention.\"\n\nimport { accessSync, constants, existsSync, readFileSync, realpathSync } from \"node:fs\";\nimport { delimiter, dirname, join, parse } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { murmurAvailable, UNKNOWN_REASON } from \"./agent-state.js\";\nimport { type AgentRow, expectsCtl } from \"./agents.js\";\nimport { type CtlProbe, ctlProbe } from \"./ctl/client.js\";\nimport { ctlSocketPath } from \"./ctl/path.js\";\nimport { CTL_OPS } from \"./ctl/protocol.js\";\nimport { CURRENT_SCHEMA_VERSION, type Db, defaultDbPath, EXPECTED_TABLES } from \"./db.js\";\nimport { checkCheapDriftInvariant } from \"./drift.js\";\nimport { checkFleetHazards } from \"./fleet-hazards.js\";\nimport { inspectLinks, type LinkOptions } from \"./link.js\";\nimport type { WorkstreamSnapshot } from \"./state.js\";\n\nexport type DoctorStatus = \"ok\" | \"warn\" | \"fail\";\n\nexport interface DoctorCheck {\n /** Short, stable identifier — used as the row label. Lowercase\n * one-word tokens so the column-aligned card layout looks tidy. */\n name: string;\n status: DoctorStatus;\n /** Free-form prose for the row's right-hand column. Kept short so\n * the card's CLIP column doesn't truncate it on common widths. */\n detail: string;\n}\n\nexport interface DoctorSummary {\n /** Every check that ran, in stable display order. The card filters\n * to non-OK rows for its body but keeps the OK rows so the popup\n * (when it ships under feat_more_cards_umbrella) can render the\n * full list. */\n checks: readonly DoctorCheck[];\n /** Convenience: how many rows are warn or fail. Card subtitle\n * reads this directly. Pure derivation from `checks`. */\n problemCount: number;\n /** Where the murmur row read agent state from. The slot-9 popup\n * re-derives the full check list and passes this back, so its\n * murmur row matches the card's (herdr on herdr). */\n agentStateSource?: \"murmur\" | \"herdr\";\n}\n\n/**\n * Compute the doctor summary for a workstream. Pure-ish: runs cheap\n * synchronous DB queries + reads from the supplied snapshot. Callers\n * that don't want to compute a snapshot first (or are running inside\n * `loadWorkstreamSnapshot` mid-build) can omit `snapshot` — the\n * snapshot-derived checks (ghosts / orphans / workspace-orphans) are\n * skipped in that case.\n */\nexport function loadDoctorSummary(\n db: Db,\n snapshot: WorkstreamSnapshot | null,\n agentStateSource: \"murmur\" | \"herdr\" = \"murmur\",\n): DoctorSummary {\n const checks: DoctorCheck[] = [];\n\n // ─ schema (table presence) ──────────────────────────────────────\n try {\n const tables = (\n db\n .prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name\",\n )\n .all() as { name: string }[]\n ).map((r) => r.name);\n const missing = EXPECTED_TABLES.filter((t) => !tables.includes(t));\n if (missing.length === 0) {\n checks.push({\n name: \"schema\",\n status: \"ok\",\n detail: `${EXPECTED_TABLES.length} tables`,\n });\n } else {\n checks.push({\n name: \"schema\",\n status: \"fail\",\n detail: `missing: ${missing.join(\", \")}`,\n });\n }\n } catch (err) {\n checks.push({\n name: \"schema\",\n status: \"fail\",\n detail: err instanceof Error ? err.message : String(err),\n });\n }\n\n // ─ schema_version ────────────────────────────────────────────────\n try {\n const row = db.prepare(\"SELECT version FROM schema_version WHERE id = 1\").get() as\n | { version: number }\n | undefined;\n const v = row?.version;\n if (v === undefined) {\n checks.push({\n name: \"schema_version\",\n status: \"fail\",\n detail: `missing row (expected ${CURRENT_SCHEMA_VERSION})`,\n });\n } else if (v === CURRENT_SCHEMA_VERSION) {\n checks.push({ name: \"schema_version\", status: \"ok\", detail: String(v) });\n } else if (v < CURRENT_SCHEMA_VERSION) {\n checks.push({\n name: \"schema_version\",\n status: \"warn\",\n detail: `${v} (code expects ${CURRENT_SCHEMA_VERSION})`,\n });\n } else {\n checks.push({\n name: \"schema_version\",\n status: \"fail\",\n detail: `${v} (code expects ${CURRENT_SCHEMA_VERSION}; downgrade?)`,\n });\n }\n } catch {\n checks.push({\n name: \"schema_version\",\n status: \"fail\",\n detail: \"unreadable\",\n });\n }\n\n // ─ journal_mode ─────────────────────────────────────────────────\n try {\n const journal = db.pragma(\"journal_mode\", { simple: true });\n if (journal === \"wal\") {\n checks.push({ name: \"journal_mode\", status: \"ok\", detail: String(journal) });\n } else {\n checks.push({\n name: \"journal_mode\",\n status: \"warn\",\n detail: `${journal} (expected wal)`,\n });\n }\n } catch (err) {\n checks.push({\n name: \"journal_mode\",\n status: \"warn\",\n detail: err instanceof Error ? err.message : String(err),\n });\n }\n\n // ─ foreign_keys ─────────────────────────────────────────────────\n try {\n const fk = db.pragma(\"foreign_keys\", { simple: true });\n if (fk === 1) {\n checks.push({ name: \"foreign_keys\", status: \"ok\", detail: \"on\" });\n } else {\n checks.push({\n name: \"foreign_keys\",\n status: \"fail\",\n detail: `off (${fk})`,\n });\n }\n } catch (err) {\n checks.push({\n name: \"foreign_keys\",\n status: \"fail\",\n detail: err instanceof Error ? err.message : String(err),\n });\n }\n\n // ─ snapshot-derived checks (ghosts / orphans) ───────────────────\n if (snapshot !== null) {\n const ghosts = snapshot.view.report.prunedGhosts;\n if (ghosts === 0) {\n checks.push({ name: \"agents\", status: \"ok\", detail: \"no ghost panes\" });\n } else {\n checks.push({\n name: \"agents\",\n status: \"warn\",\n detail: `${ghosts} ghost pane${ghosts === 1 ? \"\" : \"s\"}; run \\`mu state\\` or \\`mu agent list\\` to reap`,\n });\n }\n const orphanPanes = snapshot.view.orphans.length;\n if (orphanPanes === 0) {\n checks.push({ name: \"panes\", status: \"ok\", detail: \"no orphan panes\" });\n } else {\n checks.push({\n name: \"panes\",\n status: \"warn\",\n detail: `${orphanPanes} orphan pane${orphanPanes === 1 ? \"\" : \"s\"}; run \\`mu agent adopt\\``,\n });\n }\n const orphanWorkspaces = snapshot.workspaceOrphans.length;\n if (orphanWorkspaces === 0) {\n checks.push({ name: \"workspaces\", status: \"ok\", detail: \"no orphan dirs\" });\n } else {\n checks.push({\n name: \"workspaces\",\n status: \"warn\",\n detail: `${orphanWorkspaces} orphan dir${orphanWorkspaces === 1 ? \"\" : \"s\"}; run \\`mu workspace orphans\\``,\n });\n }\n }\n\n const muExt = ctlExtensionDoctorCheck();\n checks.push(murmurDoctorCheck(agentStateSource, { muExtOk: muExt.status === \"ok\" }));\n checks.push(muExt, skillDoctorCheck());\n\n // ─ Mixed-fleet hazards + the SHALLOW drift invariant.\n //\n // Both obey this file's cheapness rules: the fleet checks are two path\n // comparisons, one statfs and one indexed scan; the drift invariant is\n // four indexed NOT EXISTS queries (~1ms on a 200-task DB). NO rebuild\n // here — `checkDrift` is ~0.6ms per op and would make the TUI's poll\n // tick take seconds. The deep check belongs to `mu doctor --deep`, and\n // the row below says so when it is clean, so the operator knows the\n // dashboard is showing a smoke alarm rather than a proof.\n for (const hazard of checkFleetHazards(db, { dbPath: defaultDbPath() })) {\n // Skip the inert \"no sync configured\" row: on a single-machine setup\n // it would be permanent noise in a dashboard whose whole design is\n // \"rows appearing means something needs attention\".\n if (hazard.severity === \"ok\" && hazard.name === \"db-vs-sync\") continue;\n checks.push({ name: hazard.name, status: hazard.severity, detail: hazard.detail });\n }\n\n const cheapDrift = checkCheapDriftInvariant(db);\n checks.push(\n cheapDrift.clean\n ? {\n name: \"drift\",\n status: \"ok\",\n detail: \"every row has ops; `mu doctor --deep` for full diff\",\n }\n : {\n name: \"drift\",\n status: \"fail\",\n detail: `${cheapDrift.totalUnexplained} row(s) with no ops; run \\`mu doctor --deep\\``,\n },\n );\n\n return { checks, problemCount: countProblems(checks), agentStateSource };\n}\n\nfunction resolveExecutable(name: string): string | null {\n for (const dir of (process.env.PATH ?? \"\").split(delimiter).filter(Boolean)) {\n const candidate = join(dir, name);\n try {\n accessSync(candidate, constants.X_OK);\n return realpathSync(candidate);\n } catch {\n // Keep scanning PATH.\n }\n }\n return null;\n}\n\nfunction murmurVersion(executable: string): string | null {\n let dir = dirname(executable);\n const root = parse(dir).root;\n while (dir !== root) {\n const packagePath = join(dir, \"package.json\");\n if (existsSync(packagePath)) {\n try {\n const pkg: unknown = JSON.parse(readFileSync(packagePath, \"utf8\"));\n if (\n typeof pkg === \"object\" &&\n pkg !== null &&\n \"name\" in pkg &&\n pkg.name === \"@mu-crew/murmur\" &&\n \"version\" in pkg &&\n typeof pkg.version === \"string\"\n ) {\n return pkg.version;\n }\n } catch {\n // An unrelated or unreadable package.json does not end the walk.\n }\n }\n dir = dirname(dir);\n }\n return null;\n}\n\nfunction olderThanMurmurOne(version: string): boolean {\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(version);\n return match !== null && Number(match[1]) < 1;\n}\n\n/** Detail of the murmur row when murmur is absent but pi agents are\n * covered by the mu extension's control socket. */\nexport const MURMUR_NOT_NEEDED_DETAIL =\n \"not needed for pi agents (mu extension); used for other CLIs\";\n\n/**\n * The agent-state source row. Shared by the TUI card and `mu doctor`.\n * `muExtOk`: the mu pi extension is linked, so pi agents report state\n * over their control socket and a missing murmur only affects other CLIs.\n */\nexport function murmurDoctorCheck(\n agentStateSource: \"murmur\" | \"herdr\",\n opts: { muExtOk?: boolean } = {},\n): DoctorCheck {\n if (agentStateSource === \"herdr\") {\n return { name: \"murmur\", status: \"ok\", detail: \"agent state from herdr\" };\n }\n\n const executable = resolveExecutable(\"murmur\");\n if (opts.muExtOk === true && (executable === null || !murmurAvailable())) {\n return { name: \"murmur\", status: \"ok\", detail: MURMUR_NOT_NEEDED_DETAIL };\n }\n if (executable === null) {\n return {\n name: \"murmur\",\n status: \"warn\",\n detail: `${UNKNOWN_REASON.murmurMissing}: agent state shows unknown`,\n };\n }\n if (!murmurAvailable()) {\n return {\n name: \"murmur\",\n status: \"warn\",\n detail: `${UNKNOWN_REASON.extensionMissing}: run murmur link pi`,\n };\n }\n\n const version = murmurVersion(executable);\n if (version !== null && olderThanMurmurOne(version)) {\n return {\n name: \"murmur\",\n status: \"warn\",\n detail:\n opts.muExtOk === true\n ? `murmur ${version} is older than 1.0.0; idle/stall timing unknown for non-pi CLIs (pi agents use ctl)`\n : `murmur ${version} is older than 1.0.0; @murmur_pane_since missing, idle/stall timing unknown`,\n };\n }\n return {\n name: \"murmur\",\n status: \"ok\",\n detail: version === null ? \"agent state from murmur\" : `agent state from murmur ${version}`,\n };\n}\n\nconst RELINK = \"run mu link pi\";\n\n/** The \"mu ext\" row's feature list; a feature an env var switched off is named with it. */\nexport function extFeatures(env: NodeJS.ProcessEnv): string {\n const features: [string, string][] = [\n [\"mu_delegate\", \"MU_DELEGATE\"],\n [\"nudge\", \"MU_NUDGE\"],\n ];\n const on = [\"ctl\", ...features.filter(([, v]) => env[v] !== \"0\").map(([f]) => f)];\n const off = features.filter(([, v]) => env[v] === \"0\").map(([f, v]) => `${f} disabled by ${v}=0`);\n return `(${on.join(\", \")}${off.length ? `; ${off.join(\"; \")}` : \"\"})`;\n}\n\n/** The \"mu ext\" row: is the mu pi extension shim installed and live? */\nexport function ctlExtensionDoctorCheck(opts?: LinkOptions): DoctorCheck {\n const { extension } = inspectLinks(opts);\n switch (extension.state) {\n case \"ok\":\n return {\n name: \"mu ext\",\n status: \"ok\",\n detail: extFeatures(process.env),\n };\n case \"stale-copy\":\n return { name: \"mu ext\", status: \"warn\", detail: `stale copy: ${RELINK}` };\n case \"dangling\":\n return { name: \"mu ext\", status: \"warn\", detail: `dangling: ${RELINK}` };\n default:\n return { name: \"mu ext\", status: \"warn\", detail: `not linked: ${RELINK}` };\n }\n}\n\n/** The \"mu skill\" row: is `~/.agents/skills/mu` this package's skill? */\nexport function skillDoctorCheck(opts?: LinkOptions): DoctorCheck {\n const { skill } = inspectLinks(opts);\n switch (skill.state) {\n case \"ok\":\n return { name: \"mu skill\", status: \"ok\", detail: skill.target ?? skill.path };\n case \"foreign\":\n return {\n name: \"mu skill\",\n status: \"warn\",\n detail: `foreign skill at ~/.agents/skills/mu (${skill.target ?? \"not a symlink\"})`,\n };\n case \"dangling\":\n return { name: \"mu skill\", status: \"warn\", detail: `dangling: ${RELINK}` };\n default:\n return { name: \"mu skill\", status: \"warn\", detail: `not linked: ${RELINK}` };\n }\n}\n\n/** Per-agent probe budget for the doctor \"ctl\" row. */\nexport const DOCTOR_CTL_TIMEOUT_MS = 500;\n\nexport interface AgentCtlReport {\n workstream: string;\n agent: string;\n socket: string;\n probe: CtlProbe[\"kind\"];\n /** ok probes: the mu version the agent's extension was built from. */\n extVersion?: string;\n /** ok probes whose extension is older than the installed mu, or lacks ops. */\n outdated?: boolean;\n /** ok probes: protocol ops (CTL_OPS) the extension's hello does not list. */\n missingOps?: string[];\n /** Outdated probes: how `/reload` reaches it. \"ctl\" when the extension\n * serves op command, else \"mux\" (`--via mux`, typed into the pane). */\n reloadVia?: \"ctl\" | \"mux\";\n}\n\n/**\n * The installed mu version, from the package.json one directory above\n * this module (src/ in source mode, dist/ when bundled). Null if unreadable.\n */\nexport function installedMuVersion(): string | null {\n try {\n const pkgPath = join(dirname(fileURLToPath(import.meta.url)), \"..\", \"package.json\");\n const pkg: unknown = JSON.parse(readFileSync(pkgPath, \"utf8\"));\n const v =\n typeof pkg === \"object\" && pkg !== null ? (pkg as { version?: unknown }).version : null;\n return typeof v === \"string\" ? v : null;\n } catch {\n return null;\n }\n}\n\n/** a < b on the numeric major.minor.patch prefix; false when either is unparsable. */\nexport function versionOlder(a: string, b: string): boolean {\n const pa = /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(a);\n const pb = /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(b);\n if (!pa || !pb) return false;\n for (let i = 1; i <= 3; i++) {\n const d = Number(pa[i]) - Number(pb[i]);\n if (d !== 0) return d < 0;\n }\n return false;\n}\n\n/**\n * Is an ok probe's extension older than the installed mu? An extension\n * without `ops` in hello predates version reporting, so it is older. One\n * with ops but no extVersion runs from source (tests, dev): not flagged.\n * Missing ops are checked separately (`probeMissingOps`), because a pi that\n * loaded the extension before an op landed reports the SAME version.\n */\nfunction probeOutdated(probe: CtlProbe, installed: string | null): boolean {\n if (probe.kind !== \"ok\") return false;\n if (probe.ops === undefined) return true;\n if (probe.extVersion === undefined || installed === null) return false;\n return versionOlder(probe.extVersion, installed);\n}\n\n/** CTL_OPS entries an ok probe's hello does not list; [] when it predates ops. */\nfunction probeMissingOps(probe: CtlProbe): string[] {\n if (probe.kind !== \"ok\" || probe.ops === undefined) return [];\n const served = new Set<string>(probe.ops);\n return CTL_OPS.filter((op) => !served.has(op));\n}\n\n/**\n * The \"ctl\" row: probe the control socket of every agent that runs pi\n * (per `expectsCtl`, the rule send, state and abort use). `stateDir`\n * roots the derived socket path like spawn's (the DB's directory); it\n * defaults to ctlSocketPath's env-derived base. Probes run in\n * parallel, each bounded by `timeoutMs`. Async, so `mu doctor` only:\n * the TUI's per-tick summary stays synchronous and socket-free.\n */\nexport async function ctlSocketsDoctorCheck(\n agents: readonly AgentRow[],\n opts: { timeoutMs?: number; installedVersion?: string | null; stateDir?: string } = {},\n): Promise<{ check: DoctorCheck; agents: AgentCtlReport[] }> {\n const timeoutMs = opts.timeoutMs ?? DOCTOR_CTL_TIMEOUT_MS;\n const installed =\n opts.installedVersion !== undefined ? opts.installedVersion : installedMuVersion();\n const piAgents = agents\n .map((a) => ({ a, socket: ctlSocketPath(a.workstreamName, a.name, opts.stateDir) }))\n .filter(({ a, socket }) => expectsCtl(a, socket));\n const reports = await Promise.all(\n piAgents.map(async ({ a, socket }): Promise<AgentCtlReport> => {\n const probe = await ctlProbe(socket, timeoutMs);\n const older = probeOutdated(probe, installed);\n const missingOps = probeMissingOps(probe);\n return {\n workstream: a.workstreamName,\n agent: a.name,\n socket,\n probe: probe.kind,\n ...(probe.kind === \"ok\" && probe.extVersion !== undefined\n ? { extVersion: probe.extVersion }\n : {}),\n ...(older || missingOps.length > 0\n ? {\n outdated: true,\n reloadVia: probe.kind === \"ok\" && probe.ops?.includes(\"command\") ? \"ctl\" : \"mux\",\n }\n : {}),\n ...(missingOps.length > 0 ? { missingOps } : {}),\n };\n }),\n );\n const bad = reports.filter((r) => r.probe !== \"ok\" || r.outdated);\n const describe = (r: AgentCtlReport): string => {\n const who = `${r.workstream}/${r.agent}`;\n if (!r.outdated) return `${who}: ${r.probe}`;\n if (r.missingOps && !(r.extVersion && installed && versionOlder(r.extVersion, installed)))\n return `${who}: extension lacks ops: ${r.missingOps.join(\", \")}`;\n return `${who}: extension ${r.extVersion ?? \"(unknown)\"} older than installed ${installed ?? \"mu\"}`;\n };\n let check: DoctorCheck;\n if (reports.length === 0) check = { name: \"ctl\", status: \"ok\", detail: \"no pi agents\" };\n else if (bad.length === 0) {\n check = {\n name: \"ctl\",\n status: \"ok\",\n detail: `${reports.length}/${reports.length} pi agents reachable`,\n };\n } else {\n check = {\n name: \"ctl\",\n status: \"warn\",\n detail: bad.map(describe).join(\", \"),\n };\n }\n return { check, agents: reports };\n}\n\n/** Count of warn + fail rows. Pure; exported for unit tests. */\nexport function countProblems(checks: readonly DoctorCheck[]): number {\n let n = 0;\n for (const c of checks) if (c.status !== \"ok\") n++;\n return n;\n}\n\n/**\n * Return the full check array (OK + warn + fail) in stable display\n * order. Used by the TUI's slot-9 Doctor popup\n * (feat_popup_9_doctor, workstream `tui-impl`) which renders every\n * row — not just the non-OK subset Card 9 surfaces.\n *\n * Thin wrapper over `loadDoctorSummary` so the SDK seam stays\n * single: `loadDoctorSummary` is the source of truth for the\n * check vocabulary, and the popup's `loadDoctorChecks` view is\n * just `.checks`. Pure-ish (same cheap synchronous DB reads as\n * `loadDoctorSummary`).\n */\nexport function loadDoctorChecks(\n db: Db,\n snapshot: WorkstreamSnapshot | null,\n agentStateSource: \"murmur\" | \"herdr\" = \"murmur\",\n): readonly DoctorCheck[] {\n return loadDoctorSummary(db, snapshot, agentStateSource).checks;\n}\n\n// ─── pure helpers (per-check remediation hints) ────────────────────\n//\n// These map a `DoctorCheck.name` to either (a) a single-line\n// informational shell command suitable for yank/paste, or (b) a\n// short multi-line paragraph explaining the failure shape. They\n// originally lived next to the slot-9 Doctor popup\n// (src/cli/tui/popups/doctor.tsx) but moved here per\n// review_tui_doctor_remediation_lives_in_popup: neither function\n// has any rendering concern (both return plain strings) and the\n// popup's `renderDrillBody` was the only render-layer caller.\n// Living here keeps every per-check fact in one file so adding a\n// new check is a single touchpoint, and lets future SDK consumers\n// (e.g. a `mu doctor --remediation` flag) reach the same data\n// without depending on the TUI render layer.\n//\n// READ-ONLY by construction. Even when a check is `fail`, the yank\n// recipe is the diagnostic verb the operator should RUN MANUALLY,\n// never a mutating fix. Schema-shape checks (no actionable\n// mutation) yank a `# ...` comment line so the muscle-memory of\n// `y` still gives feedback.\n\n/**\n * Map a check row to the most useful informational command the\n * operator might paste. Read-only by construction: `mu agent list`,\n * `mu workspace orphans`, `mu doctor` are all SELECT-shape verbs;\n * `# ...` lines are visibly inert. Per the slot-9 popup spec KEY\n * MAP block this is INFORMATIONAL, never a mutating recipe — so\n * even when the check is `fail`, we yank the diagnostic verb the\n * operator should RUN MANUALLY, not a fix command.\n *\n * Pure; exported for unit tests + SDK reuse.\n */\nexport function yankCommandForCheck(check: Pick<DoctorCheck, \"name\" | \"status\">): string {\n switch (check.name) {\n case \"murmur\":\n return \"murmur link pi\";\n case \"mu ext\":\n case \"mu skill\":\n return \"mu link pi\";\n case \"ctl\":\n return \"mu agent list\";\n case \"agents\":\n // Diagnostic/reaping read: `mu state` and `mu agent list` both\n // reconcile missing panes. Prefer the canonical state card; the\n // detailed agent list remains in the doctor detail text.\n return \"mu state\";\n case \"panes\":\n // Orphan tmux panes — the standard adoption recipe.\n return \"mu agent adopt\";\n case \"workspaces\":\n // Diagnostic: list orphan workspace dirs with their cleanup\n // command. An orphan has no row, so `mu workspace free` cannot\n // remove it; the operator deletes it by hand.\n return \"mu workspace orphans\";\n case \"drift\":\n // The full rebuild-and-diff. Read-only: it writes only to a temp\n // DB and never touches live state.\n return \"mu doctor --deep\";\n case \"db-vs-sync\":\n case \"db-filesystem\":\n // Environment hazards: the fix is an env-var change the operator\n // must make deliberately, so yank the inspection, not a mutation.\n return \"# check MU_DB_PATH / MU_SYNC_DIR; see `mu doctor` for the full explanation\";\n case \"name-case\":\n return 'mu sql \"SELECT name FROM workstreams ORDER BY LOWER(name)\"';\n case \"schema\":\n case \"schema_version\":\n case \"journal_mode\":\n case \"foreign_keys\":\n // No actionable mutation an operator should yank for\n // schema-shape checks (the migration runner is the SDK seam,\n // not a CLI verb). Yank a no-op comment so the muscle memory\n // of `y` still gives feedback. When fail, the textual\n // `mu doctor` carries the full diagnostic.\n return `# ${check.name}: see \\`mu doctor\\` for full diagnostic`;\n default:\n // Forward-compat: any future check name falls back to the\n // textual `mu doctor` verb. Read-only.\n return \"mu doctor\";\n }\n}\n\n/**\n * A short paragraph (one paragraph per check name) explaining the\n * shape of the failure / warning. Returned as a `readonly string[]`\n * so the popup's drill body can interleave the lines with other\n * content; CLI consumers can `.join(\"\\n\")` themselves.\n *\n * Pure; exported for unit tests + SDK reuse.\n */\nexport function remediationParagraph(check: DoctorCheck): readonly string[] {\n switch (check.name) {\n case \"murmur\":\n return [\n \"Install murmur with `npm i -g @mu-crew/murmur`, then run\",\n \"`murmur init` and `murmur link pi`. Restart running pi sessions.\",\n ];\n case \"mu ext\":\n case \"mu skill\":\n return [\n \"Run `mu link pi` to install the mu pi extension shim and the mu\",\n \"skill symlink, then restart running pi sessions so they load it.\",\n ];\n case \"ctl\":\n return [\n \"mu talks to pi agents over a per-agent control socket served by\",\n \"the mu pi extension. `missing`: no socket (extension not loaded,\",\n \"the agent predates `mu link pi`, or a new pi still waits at its\",\n \"project trust prompt: answer /trust in the pane, or spawn with\",\n \"--command 'pi --approve'). `refused`: socket present but not\",\n \"answering. `version`: protocol mismatch; upgrade mu or pi.\",\n \"Run `mu link pi`, then respawn the agent. `extension X older than\",\n \"installed Y` or `lacks ops`: the agent's pi loaded an older mu\",\n \"build; reload it with `mu agent send <a> '/reload'` (add\",\n \"`--via mux` when the extension lacks op command) or respawn it.\",\n ];\n case \"agents\":\n return [\n \"A 'ghost pane' is a registered agent whose tmux pane is gone.\",\n \"Doctor only reports the count. Run `mu state` or `mu agent list`\",\n \"to reap ghost agents and return their IN_PROGRESS tasks to OPEN.\",\n \"The TUI is read-only, but its slow tick uses the same state reap.\",\n ];\n case \"panes\":\n return [\n \"An 'orphan pane' is a live tmux pane in the workstream's\",\n \"session that mu doesn't know about. Adopt it via\",\n \"`mu agent adopt <pane-id>` to register it as a managed agent,\",\n \"or kill it manually if it's not yours.\",\n ];\n case \"workspaces\":\n return [\n \"An 'orphan workspace dir' is a per-agent VCS workspace dir under\",\n \"the workstream with no vcs_workspaces row, so `mu workspace free`\",\n \"cannot remove it. mu never deletes it for you: it may hold the only\",\n \"copy of uncommitted work. Run `mu workspace orphans -w <ws>` to list\",\n \"them with a cleanup command, inspect each, then remove it by hand\",\n \"(`git worktree remove --force <path>` from the project root for a\",\n \"git checkout, else `rm -rf <path>`).\",\n ];\n case \"drift\":\n return [\n \"Drift means the ops log and the live tables disagree, and since\",\n \"undo/sync/history are ALL projections of that one log, a\",\n \"capture bug breaks all of them at once. This row is the shallow\",\n \"check (every live row must have at least one op naming its key);\",\n \"it cannot see an uncaptured UPDATE. Run `mu doctor --deep` for the\",\n \"full rebuild-and-diff, which names the exact table/key/field.\",\n \"Back up before acting: if capture missed a mutation the LIVE rows\",\n \"hold real work and rebuilding would discard it.\",\n ];\n case \"db-vs-sync\":\n return [\n \"MU_DB_PATH is inside MU_SYNC_DIR. A live WAL-mode SQLite DB is\",\n \"three files whose mutual consistency IS its durability; a file\",\n \"syncer copying them out of order, or resurrecting a peer's stale\",\n \"-wal, silently corrupts the DB. Move the DB out of the synced\",\n \"folder — mu syncs append-only per-machine segments precisely so\",\n \"the database file itself never has to travel.\",\n ];\n case \"db-filesystem\":\n return [\n \"The DB appears to be on a network mount (NFS/SMB/FUSE). SQLite's\",\n \"WAL mode needs working POSIX advisory locks and a shared-memory\",\n \"file, and network filesystems provide neither dependably: expect\",\n \"'database is locked' without contention, or corruption with it.\",\n \"Keep the DB on local disk and sync segments instead.\",\n ];\n case \"name-case\":\n return [\n \"Two workstream names differ only by letter case. They coexist on\",\n \"Linux but collide on macOS (APFS) and Windows (NTFS), and a\",\n \"workstream name IS a tmux session name and seeds workspace paths.\",\n \"A Mac in the fleet would see one session where Linux sees two.\",\n \"Rename one side before adding a Mac: export, re-init under a new\",\n \"name, tear down the old one.\",\n ];\n case \"schema\":\n return [\n \"Missing tables mean something dropped them after mu created\",\n \"them: every mu process runs CREATE TABLE IF NOT EXISTS for the\",\n \"whole schema at open, so re-running any mu command restores an\",\n \"empty table. If a table that held data is gone, back up the DB\",\n \"and rebuild from the ops log: `mu rebuild <new-file>`.\",\n ];\n case \"schema_version\":\n return [\n \"mu opens only a DB at exactly its own schema version: older\",\n \"DBs are refused with SchemaTooOldError and newer ones with\",\n \"SchemaTooNewError (exit 4), and there is no in-place migration.\",\n \"A mismatch seen here means the row changed after open (an\",\n \"external SQLite client). If `<` the expected version, migrate a\",\n \"copy with scripts/migrate.ts; if `>`, use the newer mu binary.\",\n ];\n case \"journal_mode\":\n return [\n \"WAL is the default journal mode for SQLite under mu. A\",\n \"different mode (e.g. `delete`) means an external tool\",\n \"rewrote the DB pragma. Re-open the DB with mu to restore.\",\n ];\n case \"foreign_keys\":\n return [\n \"Foreign keys must be ON for mu's CASCADE deletes to work.\",\n \"An OFF value usually means an external SQLite client opened\",\n \"the DB without `PRAGMA foreign_keys = ON`. Re-open with mu.\",\n ];\n default:\n return [\"See `mu doctor` for the canonical textual diagnostic.\"];\n }\n}\n","// mu — drift detection: is the projection still faithful to the ops log?\n//\n// WHY THIS IS LOAD-BEARING, NOT DIAGNOSTICS GARNISH\n// ------------------------------------------------\n// mu collapsed four independent change-recording mechanisms into ONE\n// ops log. The upside is coherence. The cost, stated plainly in\n// docs/VISION.md § 2b, is that a capture bug is no longer \"sync is\n// broken\" — it is undo AND sync AND history broken,\n// simultaneously and silently, because all four are projections of the\n// same table.\n//\n// This module is what converts that from a hazard into a DETECTABLE\n// CONDITION. It is the reason the consolidation was defensible at all,\n// so it is production code: it must name what diverged precisely enough\n// to act on at 3am, and it must not cry wolf on shapes that are\n// legitimately asymmetric.\n//\n// HOW IT WORKS\n// ------------\n// Rebuild the log into a throwaway DB (src/rebuild.ts, whose target path\n// is a parameter precisely so this can point it at a temp file), then\n// diff the rebuilt tables against the live ones. The log is canonical by\n// definition, so any divergence means either capture missed a mutation\n// or apply is lossy. Both are bugs; neither is recoverable by guessing.\n//\n// TWO TIERS, AND WHY\n// ------------------\n// Measured on this machine (better-sqlite3 3.49, NVMe):\n//\n// tasks ops rebuild\n// 100 353 258ms\n// 500 1742 1146ms\n// 1000 3461 2178ms\n//\n// Linear in ops, ~0.6ms per op. `mu doctor` is expected to be fast and\n// is run reflexively, so a 2s+ hit on a realistic DB is not acceptable\n// as a default. Hence:\n//\n// DEFAULT (`mu doctor`) — `checkCheapDriftInvariant`, ~1ms: every\n// live row must have at least one op naming\n// its key. Catches an uncaptured INSERT or a\n// row that exists with no history at all,\n// which is the most common shape of a\n// capture bug (a new mutation path that\n// bypassed the triggers).\n// DEEP (`mu doctor --deep`) — full rebuild + field-level diff. Catches\n// everything, including an uncaptured UPDATE,\n// which the cheap check is structurally blind\n// to because the key still has ops.\n//\n// That blindness is real and measured, not assumed: an uncaptured UPDATE\n// leaves the cheap invariant reporting zero orphans. So the cheap check\n// is a smoke alarm, not a proof, and the default run says so by pointing\n// at --deep.\n\nimport { mkdtempSync, rmSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { type Db, openDb } from \"./db.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\nimport { rebuildInto } from \"./rebuild.js\";\n\n/** One field-level divergence between the live tables and the rebuild. */\nexport interface DriftRecord {\n /** Portable table the divergence is in. */\n table: string;\n /** NATURAL key of the row (never a surrogate id), so the report is\n * meaningful to an operator and stable across machines. */\n key: string;\n /** Field that differs, or a marker for whole-row presence:\n * '<row>' means the row exists on one side only. */\n field: string;\n /** Value in the LIVE tables — what mu is currently showing. */\n live: string | null;\n /** Value the ops log says it should be. The log is canonical. */\n expected: string | null;\n /** Which side is missing the row entirely, when field is '<row>'. */\n presence?: \"missing-in-live\" | \"missing-in-rebuild\";\n}\n\nexport interface DriftReport {\n /** True iff the projection matches the log exactly. */\n clean: boolean;\n /** Every divergence found, capped (see DRIFT_REPORT_CAP). */\n records: readonly DriftRecord[];\n /** Total divergences found, which may exceed records.length. */\n totalDrift: number;\n /** Rows compared, per table. Context for \"clean\" — a clean report on\n * an empty DB proves less than one on 900 rows. */\n rowsCompared: Record<string, number>;\n /** Wall-clock cost, so `mu doctor --deep` can be honest about it. */\n elapsedMs: number;\n}\n\n/** Cap on reported records. A systematic capture bug can diverge every\n * row; printing 900 lines buries the signal. The count is always exact\n * (`totalDrift`) even when the list is truncated. */\nexport const DRIFT_REPORT_CAP = 20;\n\n/** Result of the cheap invariant that runs in the DEFAULT doctor. */\nexport interface CheapDriftReport {\n clean: boolean;\n /** Live rows with NO op naming their key — i.e. rows whose existence\n * the log cannot explain. */\n unexplainedRows: readonly { table: string; key: string }[];\n totalUnexplained: number;\n elapsedMs: number;\n}\n\n// ─── the cheap invariant (default doctor) ─────────────────────────────\n\n/**\n * Every live row must have at least one op naming its natural key.\n *\n * ~1ms on a 200-task DB: four indexed NOT EXISTS scans, no rebuild, no\n * temp file. Cheap enough to run on every `mu doctor`.\n *\n * WHAT IT CATCHES: a row that exists with no history — an uncaptured\n * INSERT, or a mutation path that bypassed the triggers entirely.\n *\n * WHAT IT CANNOT CATCH, by construction: an uncaptured UPDATE. The key\n * still has ops from the original insert, so the invariant holds while\n * the field value has silently diverged. Verified empirically. Only the\n * full rebuild-diff finds that, which is exactly why `--deep` exists and\n * why the default run points at it rather than claiming to be a proof.\n */\nexport function checkCheapDriftInvariant(db: Db): CheapDriftReport {\n const started = Date.now();\n const unexplained: { table: string; key: string }[] = [];\n\n // One query per portable table, each resolving the row to its natural\n // key so the report is actionable.\n const probes: Array<{ table: string; sql: string }> = [\n {\n table: \"workstreams\",\n sql: `SELECT w.name AS key FROM workstreams w\n WHERE NOT EXISTS (\n SELECT 1 FROM ops o WHERE o.entity = 'workstream' AND o.key = w.name)`,\n },\n {\n table: \"tasks\",\n sql: `SELECT w.name || '/' || t.local_id AS key\n FROM tasks t JOIN workstreams w ON w.id = t.workstream_id\n WHERE NOT EXISTS (\n SELECT 1 FROM ops o\n WHERE o.entity = 'task' AND o.key = w.name || '/' || t.local_id)`,\n },\n {\n table: \"task_edges\",\n sql: `SELECT wf.name || '/' || f.local_id || '->' || wt.name || '/' || t.local_id AS key\n FROM task_edges e\n JOIN tasks f ON f.id = e.from_task_id\n JOIN tasks t ON t.id = e.to_task_id\n JOIN workstreams wf ON wf.id = f.workstream_id\n JOIN workstreams wt ON wt.id = t.workstream_id\n WHERE NOT EXISTS (\n SELECT 1 FROM ops o\n WHERE o.entity = 'edge'\n AND o.key = wf.name || '/' || f.local_id || '->' || wt.name || '/' || t.local_id)`,\n },\n {\n // Notes are keyed <task-key>#<origin-id> and the origin id is the\n // AUTHORING machine's surrogate id, which need not match ours (see\n // src/apply.ts § applyNotePut). So match on the task-key PREFIX\n // rather than the exact key: the question here is \"does the log\n // know about a note on this task at all\", not \"which note\".\n //\n // Express the prefix as an indexed half-open range. A correlated\n // `LIKE task_key || '#%'` made SQLite scan every note op for every\n // live note (seconds on the dogfood DB); '#'..'$' contains exactly\n // the strings beginning with `task_key || '#'` because task ids\n // cannot contain either delimiter.\n table: \"task_notes\",\n sql: `SELECT w.name || '/' || t.local_id || '#' || n.id AS key\n FROM task_notes n\n JOIN tasks t ON t.id = n.task_id\n JOIN workstreams w ON w.id = t.workstream_id\n WHERE NOT EXISTS (\n SELECT 1 FROM ops o\n WHERE o.entity = 'note'\n AND o.key >= w.name || '/' || t.local_id || '#'\n AND o.key < w.name || '/' || t.local_id || '$')`,\n },\n ];\n\n for (const probe of probes) {\n const rows = db.prepare(probe.sql).all() as { key: string }[];\n for (const row of rows) unexplained.push({ table: probe.table, key: row.key });\n }\n\n return {\n clean: unexplained.length === 0,\n unexplainedRows: unexplained.slice(0, DRIFT_REPORT_CAP),\n totalUnexplained: unexplained.length,\n elapsedMs: Date.now() - started,\n };\n}\n\n// ─── the deep check (rebuild + diff) ──────────────────────────────────\n\n/** Columns compared per table, keyed by natural key.\n *\n * DELIBERATE OMISSIONS, each of which would otherwise be a false\n * positive:\n *\n * surrogate ids (`tasks.id`, `task_notes.id`) — assigned by\n * AUTOINCREMENT in replay order, which need not\n * match the original insert order. The natural key\n * is the identity; the rowid is an implementation\n * detail.\n * owner_id — an FK into machine-local `agents`, so it is\n * captured but never APPLIED (src/apply.ts strips\n * it: a peer's value would violate the FK). A\n * rebuild therefore legitimately has NULL owners\n * while the live DB has claims. Comparing it would\n * make every claimed task report drift.\n * created_at on\n * task_notes/edges — kept, since capture records it and apply\n * replays it verbatim.\n */\nconst COMPARED: Record<string, readonly string[]> = {\n workstreams: [\"created_at\"],\n tasks: [\"title\", \"status\", \"substate\", \"impact\", \"effort_days\", \"created_at\", \"updated_at\"],\n task_notes: [\"author\", \"content\", \"created_at\"],\n task_edges: [\"created_at\"],\n};\n\n/** SELECTs that project each portable table to (key, ...COMPARED) so\n * both sides can be compared by natural key. */\nconst SNAPSHOT_SQL: Record<string, string> = {\n workstreams: `SELECT w.name AS key, w.created_at\n FROM workstreams w`,\n tasks: `SELECT w.name || '/' || t.local_id AS key,\n t.title, t.status, t.substate, t.impact, t.effort_days, t.created_at, t.updated_at\n FROM tasks t JOIN workstreams w ON w.id = t.workstream_id`,\n // Notes are a GROW-ONLY SET whose local surrogate id is not portable,\n // so identity for diffing is (task, author, content, created_at) —\n // exactly the identity applyNotePut uses for its insert-if-absent check. Using the\n // '#<id>' key here would report drift on every note that replayed to a\n // different rowid, which is normal and not drift.\n //\n // char(31) (ASCII Unit Separator) joins the parts: it cannot occur in a\n // workstream/task id and is vanishingly unlikely in note prose, so two\n // distinct notes cannot collide into one composite key. A literal NUL\n // would be the textbook choice but SQLite truncates TEXT at NUL.\n task_notes: `SELECT w.name || '/' || t.local_id || char(31) ||\n COALESCE(n.author, '') || char(31) || n.content || char(31) ||\n n.created_at AS key,\n n.author, n.content, n.created_at\n FROM task_notes n\n JOIN tasks t ON t.id = n.task_id\n JOIN workstreams w ON w.id = t.workstream_id`,\n task_edges: `SELECT wf.name || '/' || f.local_id || '->' || wt.name || '/' || t.local_id AS key,\n e.created_at\n FROM task_edges e\n JOIN tasks f ON f.id = e.from_task_id\n JOIN tasks t ON t.id = e.to_task_id\n JOIN workstreams wf ON wf.id = f.workstream_id\n JOIN workstreams wt ON wt.id = t.workstream_id`,\n};\n\ntype Row = Record<string, string | number | null>;\n\nfunction snapshot(db: Db, table: string): Map<string, Row> {\n const sql = SNAPSHOT_SQL[table];\n if (sql === undefined) throw new Error(`no drift snapshot defined for table ${table}`);\n const out = new Map<string, Row>();\n for (const row of db.prepare(sql).all() as Row[]) {\n const key = row.key;\n if (typeof key !== \"string\") continue;\n out.set(key, row);\n }\n return out;\n}\n\n/** Render a value for the report. Distinguishes SQL NULL from the string\n * \"null\", which matters when the drift IS a set-to-NULL. */\nfunction show(v: string | number | null | undefined): string | null {\n if (v === null || v === undefined) return null;\n return String(v);\n}\n\n/**\n * Rebuild the log into a temp DB and diff it against the live tables.\n *\n * Any divergence is a bug in capture (a mutation that left no op) or in\n * apply (an op that does not reproduce its mutation). The log is\n * canonical, so `expected` is always the rebuilt value.\n *\n * SYNCHRONOUS, matching rebuildInto / applyOp: the op context is a\n * per-connection temp table, so interleaved async scopes would clobber\n * each other's capture-suppression flag.\n *\n * Cleans up its temp directory even on throw — a doctor run that leaked\n * a DB copy per invocation into /tmp would be its own bug.\n */\nexport function checkDrift(db: Db): DriftReport {\n const started = Date.now();\n const dir = mkdtempSync(join(tmpdir(), \"mu-drift-\"));\n const target = join(dir, \"rebuilt.db\");\n\n try {\n rebuildInto(db, { targetPath: target });\n const rebuilt = openDb({ path: target });\n try {\n const records: DriftRecord[] = [];\n const rowsCompared: Record<string, number> = {};\n let total = 0;\n\n const push = (record: DriftRecord): void => {\n total += 1;\n if (records.length < DRIFT_REPORT_CAP) records.push(record);\n };\n\n for (const table of Object.keys(SNAPSHOT_SQL)) {\n const live = snapshot(db, table);\n const expected = snapshot(rebuilt, table);\n rowsCompared[table] = live.size;\n\n for (const [key, liveRow] of live) {\n const expectedRow = expected.get(key);\n if (expectedRow === undefined) {\n // Live has a row the log cannot account for: capture missed\n // its creation, or a tombstone that should not have applied.\n push({\n table,\n key,\n field: \"<row>\",\n live: \"present\",\n expected: null,\n presence: \"missing-in-rebuild\",\n });\n continue;\n }\n for (const field of COMPARED[table] ?? []) {\n const a = liveRow[field];\n const b = expectedRow[field];\n // Compare rendered values so 1 and 1.0 (INTEGER vs REAL\n // affinity across a replay) do not read as drift.\n if (show(a) !== show(b)) {\n push({ table, key, field, live: show(a), expected: show(b) });\n }\n }\n }\n\n for (const key of expected.keys()) {\n if (live.has(key)) continue;\n // The log says this row should exist but it is gone from live:\n // a delete that left no tombstone.\n push({\n table,\n key,\n field: \"<row>\",\n live: null,\n expected: \"present\",\n presence: \"missing-in-live\",\n });\n }\n }\n\n return {\n clean: total === 0,\n records,\n totalDrift: total,\n rowsCompared,\n elapsedMs: Date.now() - started,\n };\n } finally {\n rebuilt.close();\n }\n } finally {\n rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 25 });\n }\n}\n\n/** One-line-per-record rendering shared by the CLI and the TUI popup, so\n * the wording of a drift report lives in exactly one place. */\nexport function formatDriftRecord(record: DriftRecord): string {\n if (record.field === \"<row>\") {\n return record.presence === \"missing-in-live\"\n ? `${record.table} ${record.key}: row MISSING from live tables (the log says it exists)`\n : `${record.table} ${record.key}: row present in live tables but the log cannot explain it`;\n }\n const live = record.live === null ? \"NULL\" : record.live;\n const expected = record.expected === null ? \"NULL\" : record.expected;\n return `${record.table} ${record.key}.${record.field}: live=${live} log=${expected}`;\n}\n\n/** What an operator should DO about drift. Detection without remediation\n * is just an alarm; this is the part that matters at 3am.\n *\n * Deliberately does NOT tell them to swap the rebuild in blindly. Drift\n * means one of the two sides is wrong and we cannot know which from\n * here: if capture missed a mutation, the LIVE tables hold the truth and\n * the log is incomplete, so rebuilding would DISCARD real work. If apply\n * is lossy, the log is right. So the guidance is: capture the evidence,\n * then choose deliberately. */\nexport function driftRemediation(): readonly string[] {\n return [\n \"Drift means the ops log and the live tables disagree. One of them is wrong.\",\n \"\",\n \" * If CAPTURE missed a mutation, the LIVE tables hold work the log never\",\n \" recorded. Rebuilding would DISCARD it. Do not rebuild reflexively.\",\n \" * If APPLY is lossy, the log is authoritative and a rebuild is the fix.\",\n \"\",\n \"Do this, in order:\",\n \" 1. Back up first, before anything else: mu db backup /tmp/mu-drift-evidence.db\",\n \" 2. Materialize what the log believes: mu rebuild /tmp/mu-rebuilt.db\",\n \" 3. Compare the two by hand for the keys named above, and decide which\",\n \" side is correct. `mu sql` against both files is the fastest way.\",\n \" 4. Report it: drift is a capture/apply BUG, not operator error. The\",\n \" named table/key/field above is the reproduction.\",\n ];\n}\n\n// ─── the typed error ──────────────────────────────────────────────────\n\n/**\n * Thrown by `mu doctor` when drift is found, so the verb exits non-zero\n * and a CI job or a wrapper script notices.\n *\n * Drift is not operator error and not a transient condition: it means the\n * ops log and the tables disagree, which is a capture or apply BUG. An\n * exit code is how that reaches automation; the printed report is how it\n * reaches a human.\n */\nexport class DriftDetectedError extends Error implements HasNextSteps {\n constructor(\n readonly totalDrift: number,\n readonly records: readonly DriftRecord[],\n ) {\n super(\n `ops-log drift: ${totalDrift} divergence${totalDrift === 1 ? \"\" : \"s\"} between the live tables and the ops log`,\n );\n this.name = \"DriftDetectedError\";\n }\n\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Back up the current state FIRST\",\n command: \"mu db backup /tmp/mu-drift-evidence.db\",\n },\n { intent: \"Materialize what the log believes\", command: \"mu rebuild /tmp/mu-rebuilt.db\" },\n {\n intent: \"Compare the named keys by hand before choosing a side\",\n command: 'mu sql \"SELECT local_id, title, status, impact FROM tasks\"',\n },\n ];\n }\n}\n","// mu — rebuild: materialize a fresh DB by replaying the ops log.\n//\n// The third leg of the ops-log substrate, after capture (write side) and\n// apply (read side). This is the DISASTER-RECOVERY story that replaces\n// the snapshot files mu no longer keeps: given an intact `ops` table, every\n// portable row is reconstructable, because the log is the canonical\n// record and the tables are a projection of it.\n//\n// It is also the foundation the doctor drift check stands on. \"Is the\n// projection still faithful to the log?\" is answered by rebuilding into\n// a temp DB and diffing — which is why `rebuildInto` takes the target\n// path as a PARAMETER and prints nothing. All human output lives in the\n// CLI layer (src/cli/rebuild.ts).\n//\n// REBUILD INTO A NEW FILE, NEVER IN PLACE\n// ---------------------------------------\n// Locked decision. A rebuild that overwrote the live DB would be a\n// destructive operation whose failure mode is \"no database at all\", and\n// it would have to hold a write lock over the whole replay. Writing a\n// new file and PRINTING THE SWAP COMMAND keeps the operator in control:\n// they can diff, inspect, and swap when ready. It also means a rebuild\n// is safe to run at any time, including from the drift check, with no\n// risk to live state.\n//\n// REBUILD IS NOT INGEST\n// ---------------------\n// The distinction is load-bearing and easy to get backwards.\n//\n// ingest = \"absorb a PEER's ops\". Only SYNCED_ENTITIES may cross a\n// machine boundary, so ingest filters. A peer's agent rows\n// are meaningless here (`pane_id` '%17' names a pane on\n// their box), so they must never arrive.\n// rebuild = \"reconstruct MY OWN database\". A local recovery\n// operation, so it must replay EVERYTHING the log knows,\n// including machine-local ops. Dropping them would silently\n// discard this machine's own log history — `mu log` would\n// come back empty after a recovery.\n//\n// So this module deliberately does NOT filter by SYNCED_ENTITIES. It\n// copies every op verbatim and projects the ones that have a portable\n// table to project into.\n//\n// WHAT A REBUILT DB LEGITIMATELY LOSES\n// ------------------------------------\n// The ops log only contains what capture recorded, and capture has\n// triggers on the PORTABLE tables only. `agents` and `vcs_workspaces`\n// have no triggers, so they leave no ops and cannot be reconstructed.\n//\n// That is CORRECT, not a gap. Both tables hold values that are\n// meaningless after recovery: `agents.pane_id` names a tmux pane in a\n// server that no longer has it, and `vcs_workspaces.path` is an absolute\n// path whose working copy may be gone. Resurrecting either would produce\n// rows that lie about reality, and `mu doctor` would immediately flag\n// them as ghosts.\n//\n// But it must be REPORTED, never silent. An operator who rebuilds and\n// does not realise their agent registry is empty will wonder why\n// `mu agent list` is blank. `RebuildReport.machineLocalLost` carries the\n// per-table counts so the CLI can say so explicitly and tell them to\n// re-spawn.\n\nimport { existsSync, mkdirSync, rmSync, statSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { applyOp, type Op } from \"./apply.js\";\nimport { type Db, openDb, SYNCED_ENTITIES } from \"./db.js\";\nimport { compareHlc } from \"./hlc.js\";\nimport { isLegacyLogOnlyIntent } from \"./legacy-ops.js\";\nimport { withCaptureSuppressed } from \"./op-context.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\n\n/** Raised when the rebuild target already exists. Never overwrite: the\n * operator may have pointed at their live DB by mistake, and a rebuild\n * is supposed to be the SAFE recovery path. */\nexport class RebuildTargetExistsError extends Error implements HasNextSteps {\n constructor(readonly path: string) {\n super(`rebuild target already exists: ${path}`);\n this.name = \"RebuildTargetExistsError\";\n }\n\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"Pick a path that does not exist\", command: \"mu rebuild /tmp/mu-rebuilt.db\" },\n { intent: \"Or remove the stale target first\", command: `rm -f ${this.path}` },\n ];\n }\n}\n\n/** Raised when the target path resolves to the source DB. Rebuilding a\n * DB onto itself would truncate the very log being replayed. */\nexport class RebuildTargetIsSourceError extends Error implements HasNextSteps {\n constructor(readonly path: string) {\n super(`rebuild target is the source DB: ${path}`);\n this.name = \"RebuildTargetIsSourceError\";\n }\n\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Rebuild writes a NEW file; pick a different path\",\n command: \"mu rebuild /tmp/mu-rebuilt.db\",\n },\n ];\n }\n}\n\n/** Per-table counts of rows a rebuild cannot reconstruct, because the\n * table has no capture triggers and therefore leaves no ops. */\nexport interface MachineLocalLoss {\n table: string;\n /** Rows present in the SOURCE that will be absent from the rebuild. */\n rows: number;\n}\n\n/** What a rebuild did. Returned rather than printed so the drift check\n * can consume it programmatically. */\nexport interface RebuildReport {\n /** Absolute-ish path written (as given by the caller). */\n targetPath: string;\n /** Ops copied into the target's log. Every op, not just synced ones. */\n opsCopied: number;\n /** Ops that projected into a portable table. Always <= opsCopied:\n * log-only entities (message / event / broadcast), machine-local\n * agent / workspace ops and legacy log-only intents are copied but\n * have no table to land in. */\n opsProjected: number;\n /** Ops that changed a row when applied. Lower than opsProjected\n * whenever later ops superseded earlier ones — which is normal and\n * is exactly what makes the rebuild a merge rather than a diff. */\n opsChangedRows: number;\n /** Ops skipped as non-projectable, by entity. Diagnostic only. */\n logOnlyByEntity: Record<string, number>;\n /** Row counts per portable table in the rebuilt DB. */\n rebuiltRows: Record<string, number>;\n /** Tables that cannot be rebuilt, with the row counts being lost. */\n machineLocalLost: MachineLocalLoss[];\n /** The machine identity carried across, so the rebuilt DB remains the\n * SAME peer rather than becoming a new one. */\n machineId: string;\n}\n\n/** Tables that hold real state but leave no ops, so a rebuild cannot\n * reconstruct them. Both are machine-local by nature — see the module\n * comment for why resurrecting them would be wrong even if we could. */\nconst UNREBUILDABLE_TABLES = [\"agents\", \"vcs_workspaces\"] as const;\n\n/** Entities that exist only in the log: there is no portable table to\n * project them into, so copying the op IS applying it.\n *\n * 'message' / 'event' / 'broadcast' are log lines (src/logs.ts writes\n * them). All three are copied verbatim and deliberately not passed to\n * applyOp, which would reject the log kinds as non-synced. */\nconst LOG_ONLY_ENTITIES = new Set([\"message\", \"event\", \"broadcast\"]);\n\n/** Ops that `applyOp` knows how to project into a portable table. This\n * is the intersection of \"synced\" and \"has a table\", which excludes\n * 'message' even though it is in SYNCED_ENTITIES.\n *\n * Historical log-only intents can name a synced entity, so intent is\n * also part of the classification even after its command is removed. */\nfunction isProjectable(entity: string, intent: string | null): boolean {\n if (isLegacyLogOnlyIntent(intent)) return false;\n if (LOG_ONLY_ENTITIES.has(entity)) return false;\n return (SYNCED_ENTITIES as readonly string[]).includes(entity);\n}\n\ninterface RawOpRow {\n hlc: string;\n machine_id: string;\n group_id: string;\n actor: string | null;\n intent: string | null;\n entity: string;\n key: string;\n op: string;\n payload: string;\n created_at: string;\n}\n\nfunction opFromRow(row: RawOpRow): Op {\n return {\n hlc: row.hlc,\n machineId: row.machine_id,\n groupId: row.group_id,\n actor: row.actor,\n intent: row.intent,\n entity: row.entity,\n key: row.key,\n op: row.op === \"del\" ? \"del\" : \"put\",\n payload: row.payload,\n };\n}\n\nexport interface RebuildOptions {\n /** Path for the new DB. Must not exist. */\n targetPath: string;\n /** Overwrite an existing target. Off by default so a mistyped path\n * cannot clobber a real file; the drift check sets it because it\n * owns a temp path it just created. */\n force?: boolean;\n}\n\n/**\n * Replay `source`'s ops log into a brand-new DB at `opts.targetPath`.\n *\n * Prints nothing and returns a report, so both `mu rebuild` and the\n * (forthcoming) doctor drift check can use it — the latter rebuilds into\n * a temp path and diffs rather than showing a human anything.\n *\n * SYNCHRONOUS, matching applyOp and withOpContext: the op context is a\n * per-connection temp table, so interleaved async scopes would clobber\n * each other's suppression flag.\n */\nexport function rebuildInto(source: Db, opts: RebuildOptions): RebuildReport {\n const { targetPath } = opts;\n\n // Refuse to touch the source. `sqlite_master`-level identity is not\n // enough — the caller may pass a different string for the same file —\n // so compare the resolved filename SQLite itself reports.\n const sourceFile = (source.pragma(\"database_list\") as Array<{ name: string; file: string }>).find(\n (row) => row.name === \"main\",\n )?.file;\n if (sourceFile !== undefined && sourceFile.length > 0) {\n const sameFile =\n sourceFile === targetPath ||\n (existsSync(targetPath) && existsSync(sourceFile) && sameInode(sourceFile, targetPath));\n if (sameFile) throw new RebuildTargetIsSourceError(targetPath);\n }\n if (existsSync(targetPath) && opts.force !== true) {\n throw new RebuildTargetExistsError(targetPath);\n }\n\n mkdirSync(dirname(targetPath), { recursive: true });\n\n // Read everything we need from the source FIRST, so the target is only\n // opened once we know the replay can proceed.\n const identity = source\n .prepare(\n \"SELECT machine_id, hostname, created_at, last_wall, last_counter FROM machine_identity WHERE id = 1\",\n )\n .get() as\n | {\n machine_id: string;\n hostname: string | null;\n created_at: string;\n last_wall: number;\n last_counter: number;\n }\n | undefined;\n if (!identity) throw new Error(\"source DB has no machine_identity row; not a mu DB\");\n\n const opRows = source\n .prepare(\n `SELECT hlc, machine_id, group_id, actor, intent, entity, key, op, payload, created_at\n FROM ops`,\n )\n .all() as RawOpRow[];\n\n // HLC ORDER, not seq order. seq is a local append cursor, so it\n // reflects the order ops were RECORDED, which for ingested peer ops is\n // arrival order rather than causal order. Replaying in HLC order is\n // what makes per-field LWW resolve correctly (see src/apply.ts), and\n // it is why the rebuild converges to the same state regardless of how\n // the log happened to be assembled.\n opRows.sort((a, b) => compareHlc(a.hlc, b.hlc));\n\n const peerRows = source\n .prepare(\"SELECT machine_id, last_applied_seq, last_seen_at FROM sync_peers\")\n .all() as Array<{\n machine_id: string;\n last_applied_seq: number;\n last_seen_at: string | null;\n }>;\n\n const machineLocalLost: MachineLocalLoss[] = [];\n for (const table of UNREBUILDABLE_TABLES) {\n const row = source.prepare(`SELECT COUNT(*) AS n FROM ${table}`).get() as { n: number };\n if (row.n > 0) machineLocalLost.push({ table, rows: row.n });\n }\n\n // `force` replaces the target, never merges into it: replaying over an\n // existing DB would keep its foreign rows and ops under our identity.\n if (opts.force === true) {\n for (const suffix of [\"\", \"-wal\", \"-shm\"]) rmSync(`${targetPath}${suffix}`, { force: true });\n }\n const target = openDb({ path: targetPath });\n try {\n // Carry the machine identity across BEFORE replaying.\n //\n // A fresh openDb seeds a NEW uuid, which would make the rebuilt DB a\n // DIFFERENT PEER: its own historical ops would look foreign to it,\n // the UNIQUE (machine_id, hlc) identity of every op would no longer\n // match its author, and peers tracking watermarks against the old id\n // would treat it as an unknown machine. Recovery must preserve who\n // this machine IS.\n //\n // The HLC clock (last_wall / last_counter) comes too. Without it the\n // rebuilt DB starts at 0 and the next local edit would mint an HLC\n // BELOW every op already in its log — so a brand-new change would\n // sort as older than history and lose every LWW comparison against\n // it. Monotonicity is a property of the machine, not of the file.\n target\n .prepare(\n `UPDATE machine_identity\n SET machine_id = @machineId, hostname = @hostname, created_at = @createdAt,\n last_wall = @lastWall, last_counter = @lastCounter\n WHERE id = 1`,\n )\n .run({\n machineId: identity.machine_id,\n hostname: identity.hostname,\n createdAt: identity.created_at,\n lastWall: identity.last_wall,\n lastCounter: identity.last_counter,\n });\n\n const insertOp = target.prepare(\n `INSERT OR IGNORE INTO ops\n (hlc, machine_id, group_id, actor, intent, entity, key, op, payload, created_at)\n VALUES (@hlc, @machineId, @groupId, @actor, @intent, @entity, @key, @op, @payload, @createdAt)`,\n );\n const insertPeer = target.prepare(\n `INSERT OR REPLACE INTO sync_peers (machine_id, last_applied_seq, last_seen_at)\n VALUES (@machineId, @lastAppliedSeq, @lastSeenAt)`,\n );\n\n const logOnlyByEntity: Record<string, number> = {};\n let opsProjected = 0;\n let opsChangedRows = 0;\n\n // ONE transaction for the whole replay. A rebuild is all-or-nothing:\n // a half-replayed DB is worse than no DB, because it looks usable.\n const replay = target.transaction(() => {\n // Capture-suppressed for the entire replay. Without this, applying\n // each op would fire the capture triggers and mint a SECOND op per\n // row, so the rebuilt log would be roughly double the original and\n // full of ops with fresh HLCs that never happened. The suppression\n // wraps the op INSERTs too, so nothing here can mint.\n withCaptureSuppressed(target, () => {\n for (const row of opRows) {\n // Copy the op verbatim, whatever its entity. This is the\n // rebuild-is-not-ingest rule: the log is this machine's own\n // history and every row of it survives recovery, including\n // machine-local entities that would never cross a peer\n // boundary.\n insertOp.run({\n hlc: row.hlc,\n machineId: row.machine_id,\n groupId: row.group_id,\n actor: row.actor,\n intent: row.intent,\n entity: row.entity,\n key: row.key,\n op: row.op,\n payload: row.payload,\n createdAt: row.created_at,\n });\n\n if (!isProjectable(row.entity, row.intent)) {\n logOnlyByEntity[row.entity] = (logOnlyByEntity[row.entity] ?? 0) + 1;\n continue;\n }\n // Project through the SAME apply path sync ingest uses. Not a\n // second implementation of the merge rules: tombstone\n // ordering, per-field LWW and grow-only sets all come from\n // src/apply.ts, so a rebuild and an ingest can never disagree.\n opsProjected += 1;\n if (applyOp(target, opFromRow(row)).changed) opsChangedRows += 1;\n }\n\n for (const peer of peerRows) {\n insertPeer.run({\n machineId: peer.machine_id,\n lastAppliedSeq: peer.last_applied_seq,\n lastSeenAt: peer.last_seen_at,\n });\n }\n });\n });\n replay.immediate();\n\n const rebuiltRows: Record<string, number> = {};\n for (const table of [\"workstreams\", \"tasks\", \"task_edges\", \"task_notes\", \"ops\"]) {\n const row = target.prepare(`SELECT COUNT(*) AS n FROM ${table}`).get() as { n: number };\n rebuiltRows[table] = row.n;\n }\n\n return {\n targetPath,\n opsCopied: rebuiltRows.ops ?? 0,\n opsProjected,\n opsChangedRows,\n logOnlyByEntity,\n rebuiltRows,\n machineLocalLost,\n machineId: identity.machine_id,\n };\n } finally {\n target.close();\n }\n}\n\n/** True iff both paths name the same file on disk. Guards the\n * rebuild-onto-itself case when the caller passes a different string\n * for the same file (a symlink, or ./x vs x). */\nfunction sameInode(a: string, b: string): boolean {\n try {\n const sa = statSync(a);\n const sb = statSync(b);\n return sa.ino === sb.ino && sa.dev === sb.dev;\n } catch {\n // A path we cannot stat is not the source by definition.\n return false;\n }\n}\n","// mu — mixed-fleet hazard detection.\n//\n// Three environment conditions that silently corrupt or diverge state on\n// a multi-machine fleet. All three are cheap, pure-ish checks that run in\n// the DEFAULT `mu doctor`, because unlike drift they cost microseconds\n// and unlike drift they are PREVENTABLE — the operator can fix each one\n// before it costs them data.\n//\n// (a) DB inside the sync dir -> corruption. THE footgun of the design.\n// (b) DB on a network mount -> broken WAL locking.\n// (c) case-colliding names -> collide on macOS, coexist on Linux.\n//\n// The checks that need MU_SYNC_DIR read it if set and no-op otherwise,\n// so a single-machine install pays nothing.\n\nimport { existsSync, statfsSync } from \"node:fs\";\nimport { resolve, sep } from \"node:path\";\nimport type { Db } from \"./db.js\";\n\nexport type HazardSeverity = \"ok\" | \"warn\" | \"fail\";\n\nexport interface FleetHazard {\n /** Stable token, used as the doctor row label. */\n name: string;\n severity: HazardSeverity;\n /** One-line summary for the row. */\n detail: string;\n /** Multi-line explanation + remediation, shown when non-ok. */\n remediation?: readonly string[];\n}\n\n/**\n * `mu doctor` found at least one `fail`-severity row. Thrown after the\n * whole report is printed (or the --json payload emitted) so scripts and\n * CI see a non-zero exit; maps to exit 5 like drift, because a `fail`\n * row means the substrate is unsafe, not that a hint is pending.\n */\nexport class HazardFailError extends Error {\n override readonly name = \"HazardFailError\";\n constructor(readonly failedRows: readonly string[]) {\n super(\n `doctor: ${failedRows.length} row${failedRows.length === 1 ? \"\" : \"s\"} failed (${failedRows.join(\", \")}); see the remediation above`,\n );\n }\n}\n\n// ─── (a) DB inside the sync dir ───────────────────────────────────────\n\n/**\n * True iff `child` is inside `parent` (or is `parent`).\n *\n * Path-based, deliberately: the check must fire even when the DB file\n * does not exist yet, so it cannot rely on stat/inode identity. Both\n * sides are resolved to absolute form first, and a separator is appended\n * so `/sync-data` does not read as the parent of `/sync-database`.\n */\nexport function isPathInside(child: string, parent: string): boolean {\n const c = resolve(child);\n const p = resolve(parent);\n if (c === p) return true;\n return c.startsWith(p.endsWith(sep) ? p : p + sep);\n}\n\n/**\n * THE footgun of the whole sync design, and the reason it is `fail` rather\n * than `warn`.\n *\n * Sync tools (Syncthing, Dropbox, iCloud, rsync loops) copy files\n * whole-file and out of order. A live SQLite DB in WAL mode is THREE\n * files — `mu.db`, `mu.db-wal`, `mu.db-shm` — whose mutual consistency is\n * the entire basis of durability. A sync daemon that copies the main file\n * while the WAL is mid-checkpoint, or that resurrects a stale `-wal` from\n * another machine, produces a DB that opens fine and is silently corrupt.\n * Two machines writing the same synced file is worse still: last-writer\n * wins on the FILE, so an entire machine's history vanishes.\n *\n * mu's sync design specifically avoids this by shipping append-only\n * per-machine SEGMENTS (one writer per file, never contended) rather than\n * the DB itself. Putting the DB inside the sync dir defeats that on\n * purpose-built-to-be-safe transport, so it is a hard failure.\n */\nexport function checkDbInsideSyncDir(dbPath: string, syncDir: string | undefined): FleetHazard {\n if (syncDir === undefined || syncDir.trim() === \"\") {\n return {\n name: \"db-vs-sync\",\n severity: \"ok\",\n detail: \"MU_SYNC_DIR not set (no sync configured)\",\n };\n }\n if (!isPathInside(dbPath, syncDir)) {\n return { name: \"db-vs-sync\", severity: \"ok\", detail: \"DB is outside the sync dir\" };\n }\n return {\n name: \"db-vs-sync\",\n severity: \"fail\",\n detail: `DB is INSIDE MU_SYNC_DIR — this WILL corrupt it (${dbPath})`,\n remediation: [\n \"The mu database must never live inside the synced folder.\",\n \"\",\n \"A live WAL-mode SQLite DB is three files (mu.db, -wal, -shm) whose mutual\",\n \"consistency is the basis of durability. File-sync tools copy whole files,\",\n \"out of order, and across machines: a stale -wal resurrected from a peer, or\",\n \"a main file copied mid-checkpoint, yields a DB that OPENS FINE and is\",\n \"silently corrupt. Two machines writing the same synced file is worse — last\",\n \"writer wins on the whole FILE, so one machine's entire history disappears.\",\n \"\",\n \"mu syncs append-only per-machine SEGMENTS precisely to avoid this: one\",\n \"writer per file, never contended, so any file-mover is adequate transport.\",\n \"\",\n \"Fix: move the DB out of the sync dir, e.g.\",\n \" export MU_DB_PATH=$HOME/.local/state/mu/mu.db\",\n \"and keep MU_SYNC_DIR pointing at the shared folder for segments only.\",\n ],\n };\n}\n\n// ─── (b) DB on a network mount ────────────────────────────────────────\n\n/** Linux `statfs.f_type` magic numbers for filesystems where POSIX\n * advisory locking is unreliable or absent. SQLite's WAL mode needs\n * working locks AND shared memory; neither is dependable here.\n *\n * Values from linux/magic.h. Kept as a table rather than inline so the\n * list is auditable and extendable. */\nconst NETWORK_FS_MAGIC = new Map<number, string>([\n // Every value here is transcribed from linux/magic.h, not recalled.\n // An invented magic is worse than a missing one: it would either never\n // match (silent no-op) or match the wrong filesystem and warn falsely.\n [0x6969, \"NFS\"], // NFS_SUPER_MAGIC\n [0xff534d42, \"CIFS/SMB\"], // CIFS_SUPER_MAGIC\n [0xfe534d42, \"SMB2\"], // SMB2_SUPER_MAGIC\n [0x517b, \"SMBFS\"], // SMB_SUPER_MAGIC\n [0x65735546, \"FUSE (may be sshfs/rclone/cloud drive)\"], // FUSE_SUPER_MAGIC\n [0x00c36400, \"Ceph\"], // CEPH_SUPER_MAGIC\n]);\n\n/** Result of a filesystem probe. `unknown` is a first-class outcome:\n * telling the operator \"cannot determine\" is honest, whereas claiming\n * \"ok\" on a platform we cannot inspect would be a false assurance. */\nexport interface FsProbe {\n kind: \"local\" | \"network\" | \"unknown\";\n /** Human label for the detected fs, when known. */\n label: string;\n}\n\n/**\n * Classify the filesystem a path lives on.\n *\n * PORTABILITY, and why this degrades rather than guesses:\n *\n * Linux — `statfsSync().type` is a documented magic number, so the\n * classification is exact.\n * macOS — `f_type` is a small driver INDEX, not a stable magic, so\n * the same number means different things across releases.\n * Comparing it would produce confident nonsense, so we do\n * not; macOS falls through to `unknown`.\n * Other — `unknown`.\n *\n * Exported separately from the check so it can be unit-tested against\n * SYNTHETIC input: mounting NFS in a test suite is not feasible, so the\n * tests exercise `classifyFsType` (pure, takes the magic number) rather\n * than the syscall. Stated plainly because an untested detector is worse\n * than no detector.\n */\nexport function classifyFsType(magic: number, platform: string = process.platform): FsProbe {\n if (platform !== \"linux\") {\n // See above: f_type is not a portable magic off Linux.\n return { kind: \"unknown\", label: `unrecognised (${platform})` };\n }\n const network = NETWORK_FS_MAGIC.get(magic);\n if (network !== undefined) return { kind: \"network\", label: network };\n return { kind: \"local\", label: `local (0x${magic.toString(16)})` };\n}\n\n/** Probe the real filesystem for a path, falling back to `unknown` when\n * the syscall is unavailable or the path does not exist yet. */\nexport function probeFilesystem(path: string): FsProbe {\n try {\n // Probe the nearest existing ancestor: the DB file may not exist on a\n // first run, but its directory tells us the same thing.\n let probeTarget = resolve(path);\n while (!existsSync(probeTarget)) {\n const parent = resolve(probeTarget, \"..\");\n if (parent === probeTarget) break;\n probeTarget = parent;\n }\n const stats = statfsSync(probeTarget);\n return classifyFsType(Number(stats.type));\n } catch {\n return { kind: \"unknown\", label: \"probe failed\" };\n }\n}\n\n/**\n * WARN rather than fail on a network mount.\n *\n * Warn, not fail, because it is not always fatal: a single machine using\n * an NFS home with no concurrent access often works, and refusing\n * outright would lock such an operator out of their own tool. But it IS\n * the second-most-common corruption cause after (a), because WAL needs\n * both advisory locking and a shared-memory file, and NFS/SMB/sshfs\n * deliver neither reliably. Symptoms are 'database is locked' under no\n * contention, or silent corruption under real contention.\n */\nexport function checkNetworkMount(\n dbPath: string,\n probe: FsProbe = probeFilesystem(dbPath),\n): FleetHazard {\n if (probe.kind === \"network\") {\n return {\n name: \"db-filesystem\",\n severity: \"warn\",\n detail: `DB is on a network mount (${probe.label}) — WAL locking is unreliable there`,\n remediation: [\n `The DB appears to live on ${probe.label}.`,\n \"\",\n \"SQLite's WAL mode needs working POSIX advisory locks AND a shared-memory\",\n \"file. NFS, SMB/CIFS and FUSE mounts (sshfs, rclone, cloud drives) provide\",\n \"neither dependably. Expect 'database is locked' with no contention, or\",\n \"silent corruption with real contention from parallel mu invocations.\",\n \"\",\n \"Fix: keep the DB on local disk and sync SEGMENTS instead —\",\n \" export MU_DB_PATH=$HOME/.local/state/mu/mu.db\",\n \"That is what the segment design is for: append-only, single-writer files\",\n \"that any file-mover can carry safely.\",\n ],\n };\n }\n if (probe.kind === \"unknown\") {\n return {\n name: \"db-filesystem\",\n severity: \"ok\",\n detail: `filesystem not classifiable on this platform (${probe.label}); assuming local`,\n };\n }\n return { name: \"db-filesystem\", severity: \"ok\", detail: `DB is on ${probe.label}` };\n}\n\n// ─── (c) case-colliding workstream names ──────────────────────────────\n\nexport interface CaseCollision {\n /** The names, as stored, that fold to the same lowercase form. */\n names: readonly string[];\n /** The shared case-folded form. */\n folded: string;\n}\n\n/**\n * Workstream names differing only by case.\n *\n * On ext4 these coexist happily. On APFS (macOS default,\n * case-INSENSITIVE though case-preserving) and on NTFS they collide, so\n * the same fleet reaches different states depending on which machine\n * applies an op first. Concretely: `workstreams.name` is UNIQUE and IS\n * the tmux session name, so on a Mac 'Foo' and 'foo' are one session and\n * one directory but two DB rows — and every workspace path derived from\n * the name aliases onto one directory.\n *\n * Detected in the DB rather than at write time on purpose: the rows may\n * have been created on Linux and only become a hazard when the fleet\n * gains a Mac, so this must be a standing check rather than a validation.\n *\n * SQL-side `LOWER()` is ASCII-only in SQLite, which is the right\n * comparison here: workstream names are already constrained to\n * `[a-z0-9_-]` by `isValidWorkstreamName`, so any collision within the\n * legal charset is ASCII. Names that predate the rule (or were inserted\n * via `mu sql`) still get caught because we fold in JS too.\n */\nexport function findCaseCollisions(db: Db): CaseCollision[] {\n const rows = db.prepare(\"SELECT name FROM workstreams ORDER BY name\").all() as {\n name: string;\n }[];\n const byFolded = new Map<string, string[]>();\n for (const row of rows) {\n // toLocaleLowerCase would be locale-dependent (Turkish dotless i);\n // toLowerCase is stable, which is what a cross-machine check needs.\n const folded = row.name.toLowerCase();\n const bucket = byFolded.get(folded);\n if (bucket === undefined) byFolded.set(folded, [row.name]);\n else bucket.push(row.name);\n }\n const collisions: CaseCollision[] = [];\n for (const [folded, names] of byFolded) {\n if (names.length > 1) collisions.push({ folded, names });\n }\n return collisions;\n}\n\nexport function checkCaseCollisions(db: Db): FleetHazard {\n const collisions = findCaseCollisions(db);\n if (collisions.length === 0) {\n return {\n name: \"name-case\",\n severity: \"ok\",\n detail: \"no case-colliding workstream names\",\n };\n }\n const shown = collisions\n .slice(0, 5)\n .map((c) => c.names.join(\" / \"))\n .join(\", \");\n return {\n name: \"name-case\",\n severity: \"warn\",\n detail: `${collisions.length} case-colliding workstream name(s): ${shown}`,\n remediation: [\n \"These workstream names differ only by letter case:\",\n ...collisions.map((c) => ` ${c.names.join(\" / \")}`),\n \"\",\n \"They coexist on Linux (ext4 is case-sensitive) but COLLIDE on macOS (APFS\",\n \"is case-insensitive) and on Windows (NTFS). Because a workstream name IS a\",\n \"tmux session name and seeds every workspace path, a Mac in the fleet sees\",\n \"one session and one directory where Linux sees two — so the fleet reaches\",\n \"different states depending on which machine applies an op first.\",\n \"\",\n \"Fix: rename one side to a distinct name before adding a Mac to the fleet.\",\n \"There is no in-place rename verb; back the DB up, re-init under the new\",\n \"name, and tear down the old one:\",\n \" mu db backup <file>\",\n \" mu workstream init <new-name>\",\n \" mu workstream teardown <old-name> --yes\",\n ],\n };\n}\n\n// ─── the aggregate ────────────────────────────────────────────────────\n\n/**\n * Run all three mixed-fleet checks. Cheap enough for the default doctor:\n * two path/string comparisons, one statfs, one indexed table scan.\n */\nexport function checkFleetHazards(\n db: Db,\n opts: { dbPath: string; syncDir?: string | undefined } = { dbPath: \"\" },\n): FleetHazard[] {\n const syncDir = opts.syncDir !== undefined ? opts.syncDir : process.env.MU_SYNC_DIR;\n return [\n checkDbInsideSyncDir(opts.dbPath, syncDir),\n checkNetworkMount(opts.dbPath),\n checkCaseCollisions(db),\n ];\n}\n","// mu — `mu link pi`: install the mu pi extension and the mu skill.\n//\n// Modelled on murmur's `link pi` (~/hacking/crew/murmur/src/cli/link.ts):\n// the extension is installed as a re-export SHIM, not a copy, so\n// upgrading mu upgrades the extension with no relink. The skill is a\n// SYMLINK to the package's `skills/mu`, for the same reason.\n//\n// Destinations live under `MU_PI_HOME ?? homedir()`; tests point\n// MU_PI_HOME (or the `home` option) at a temp dir. Never writes into\n// pi-meta's PI_CODING_AGENT_DIR instance dirs: pi-meta copies/links from\n// ~/.pi/agent and ~/.agents at launch.\n\nimport {\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n readlinkSync,\n realpathSync,\n rmSync,\n symlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\n\n/**\n * Identifies a generated shim on its own line, so re-linking (and\n * `inspectLinks`) can tell it from an older inlined copy without\n * depending on the import statement's exact shape.\n */\nexport const MU_SHIM_MARKER = \"// mu:shim\";\n\nexport type LinkState = \"ok\" | \"missing\" | \"stale-copy\" | \"foreign\" | \"dangling\";\n\nexport interface LinkStatus {\n path: string;\n state: LinkState;\n target?: string;\n}\n\nexport interface LinkOptions {\n /** Root under which `.pi/` and `.agents/` live. Default `MU_PI_HOME ?? homedir()`. */\n home?: string;\n}\n\n/** Raised when the destination holds something mu did not put there\n * (a real directory, or a symlink to another checkout without\n * `--force`). mu never deletes user files to make room. */\nexport class LinkConflictError extends Error implements HasNextSteps {\n constructor(\n readonly path: string,\n readonly reason: \"not-a-symlink\" | \"foreign-symlink\",\n readonly current?: string,\n ) {\n super(\n reason === \"not-a-symlink\"\n ? `${path} exists and is not a symlink; refusing to replace it`\n : `${path} already links to ${current ?? \"?\"}; pass --force to replace it`,\n );\n this.name = \"LinkConflictError\";\n }\n\n errorNextSteps(): NextStep[] {\n if (this.reason === \"foreign-symlink\") {\n return [{ intent: \"Replace the existing link\", command: \"mu link pi --force\" }];\n }\n return [\n { intent: \"Inspect what is there\", command: `ls -la ${this.path}` },\n { intent: \"Move it aside, then relink\", command: `mv ${this.path} ${this.path}.bak` },\n ];\n }\n}\n\nfunction resolveHome(opts?: LinkOptions): string {\n return opts?.home ?? process.env.MU_PI_HOME ?? homedir();\n}\n\n/** Package root. src/link.ts and the bundled dist/*.js both sit exactly\n * one directory below it. */\nfunction packageRoot(): string {\n return resolve(dirname(fileURLToPath(import.meta.url)), \"..\");\n}\n\n/** The built extension the shim imports. `MU_EXTENSION_ENTRY` overrides\n * it (tests point it at a fixture so they need no prior build). */\nexport function extensionEntry(): string {\n return process.env.MU_EXTENSION_ENTRY ?? join(packageRoot(), \"dist\", \"extension\", \"mu-pi.js\");\n}\n\n/** The skill directory the symlink points at. */\nexport function skillSource(): string {\n return join(packageRoot(), \"skills\", \"mu\");\n}\n\nexport function extensionPath(opts?: LinkOptions): string {\n return join(resolveHome(opts), \".pi\", \"agent\", \"extensions\", \"mu.ts\");\n}\n\nexport function skillPath(opts?: LinkOptions): string {\n return join(resolveHome(opts), \".agents\", \"skills\", \"mu\");\n}\n\n/**\n * The installed extension as a re-export of this installation. One\n * statement on purpose: any logic here is logic that cannot be upgraded.\n * A dynamic import, not `export ... from`, matching murmur's shim (ESM\n * hoisting bit it once).\n */\nfunction shim(entry: string): string {\n return `${MU_SHIM_MARKER}\n// Generated by \\`mu link pi\\`. Do not edit.\n//\n// A re-export, not a copy: the extension code lives in the mu install, so\n// upgrading mu upgrades the extension. Re-run \\`mu link pi\\` only if the\n// install path itself moves.\nconst { default: extension } = await import(${JSON.stringify(pathToFileURL(entry).href)});\nexport default extension;\n`;\n}\n\n/** Install the extension shim (or, with `copy`, an inlined copy pinned\n * to this version). `replacedCopy` is true when an existing file without\n * the shim marker was overwritten. A symlink at the destination (e.g. to\n * a dev checkout) is never written through: a live one needs `force`,\n * which replaces the link itself, never its target. */\nexport function linkPi(opts?: LinkOptions & { copy?: boolean; force?: boolean }): {\n path: string;\n replacedCopy: boolean;\n} {\n const path = extensionPath(opts);\n const entry = extensionEntry();\n if (!existsSync(entry)) {\n throw new Error(`mu pi extension not built: ${entry} is missing (run npm run build)`);\n }\n if (lstatOrUndefined(path)?.isSymbolicLink() === true) {\n const current = resolve(dirname(path), readlinkSync(path));\n if (existsSync(current) && opts?.force !== true) {\n throw new LinkConflictError(path, \"foreign-symlink\", current);\n }\n rmSync(path);\n }\n let replacedCopy = false;\n try {\n replacedCopy = !readFileSync(path, \"utf8\").includes(MU_SHIM_MARKER);\n } catch {\n // No previous install.\n }\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, opts?.copy === true ? readFileSync(entry, \"utf8\") : shim(entry));\n return { path, replacedCopy };\n}\n\n/** Symlink `<home>/.agents/skills/mu` to the package's `skills/mu`.\n * `previous` is the old link target when one was replaced. */\nexport function linkSkill(opts?: LinkOptions & { force?: boolean }): {\n path: string;\n target: string;\n previous?: string;\n} {\n const path = skillPath(opts);\n const target = skillSource();\n let previous: string | undefined;\n const st = lstatOrUndefined(path);\n if (st) {\n if (!st.isSymbolicLink()) throw new LinkConflictError(path, \"not-a-symlink\");\n const current = resolve(dirname(path), readlinkSync(path));\n if (current === target) return { path, target };\n // A dangling link holds nothing worth protecting; anything live\n // (e.g. a dev checkout) is replaced only on request.\n if (existsSync(current) && opts?.force !== true) {\n throw new LinkConflictError(path, \"foreign-symlink\", current);\n }\n previous = current;\n rmSync(path);\n }\n mkdirSync(dirname(path), { recursive: true });\n symlinkSync(target, path, \"dir\");\n return previous === undefined ? { path, target } : { path, target, previous };\n}\n\nfunction lstatOrUndefined(path: string): ReturnType<typeof lstatSync> | undefined {\n try {\n return lstatSync(path);\n } catch {\n return undefined;\n }\n}\n\nfunction inspectExtension(path: string): LinkStatus {\n let content: string;\n try {\n content = readFileSync(path, \"utf8\");\n } catch {\n return { path, state: \"missing\" };\n }\n if (!content.includes(MU_SHIM_MARKER)) return { path, state: \"stale-copy\" };\n const m = /await import\\((\".*?\")\\)/.exec(content);\n const spec = m?.[1];\n if (!spec) return { path, state: \"dangling\" };\n const raw = JSON.parse(spec) as string;\n const target = raw.startsWith(\"file:\") ? fileURLToPath(raw) : raw;\n return { path, state: existsSync(target) ? \"ok\" : \"dangling\", target };\n}\n\nfunction inspectSkill(path: string): LinkStatus {\n const st = lstatOrUndefined(path);\n if (!st) return { path, state: \"missing\" };\n if (!st.isSymbolicLink()) return { path, state: \"foreign\" };\n let target: string;\n try {\n target = realpathSync(path);\n } catch {\n return { path, state: \"dangling\", target: resolve(dirname(path), readlinkSync(path)) };\n }\n let skillMd: string;\n try {\n skillMd = readFileSync(join(target, \"SKILL.md\"), \"utf8\");\n } catch {\n return { path, state: \"foreign\", target };\n }\n const front = /^---\\n([\\s\\S]*?)\\n---/.exec(skillMd)?.[1] ?? \"\";\n const isMu = /^name:\\s*[\"']?mu[\"']?\\s*$/m.test(front);\n return { path, state: isMu ? \"ok\" : \"foreign\", target };\n}\n\n/** Report the state of both installs without changing anything. */\nexport function inspectLinks(opts?: LinkOptions): { extension: LinkStatus; skill: LinkStatus } {\n return {\n extension: inspectExtension(extensionPath(opts)),\n skill: inspectSkill(skillPath(opts)),\n };\n}\n","// mu — the ONE op → prose formatter.\n//\n// WHY THIS FILE EXISTS\n//\n// mu once rendered log lines by prefix-matching free prose: `classifyEventVerb`\n// walked `EVENT_VERB_PREFIXES` looking for a leading two-word verb, and\n// `CLAIM_EVENT_PREFIX` was bolted on because that matching kept breaking\n// (review_code_last_claim_actor_brittle). Rewording any payload silently\n// broke its renderer, and attribution had to be smuggled through a\n// tab-delimited prefix so it could be parsed back out.\n//\n// Since v2-capture + v2-retire-log-shim, every op carries a structured\n// `intent` ('task.close'), a natural `key` ('demo/t1'), and a JSON\n// payload holding ONLY the columns that changed. So rendering is a\n// lookup on intent, not a search through text. THIS FILE MUST NEVER\n// STRING-MATCH A PAYLOAD to decide what an op is — that is the exact\n// brittleness the ops log deletes. It reads `intent`, then pulls named fields\n// out of the parsed payload.\n//\n// Layering: pure and colour-free. `src/cli/format.ts` adds picocolors\n// for the terminal, and `src/cli/tui/` renders the same strings inside\n// ink. Neither owns any prose logic — one formatter, three surfaces\n// (ROADMAP: no second render layer).\n\nimport type { LocalIntent } from \"./logs.js\";\nimport { formatPair, resolvePair } from \"./tasks/status.js\";\n\n/** Every intent mu writes: the local ones (`emitEvent`, for changes no\n * trigger can see) plus the capture-trigger ones (set via\n * `withOpContext` around a portable-table mutation). */\nexport type CaptureIntent =\n | \"task.add\"\n | \"task.update\"\n | \"task.note\"\n | \"task.delete\"\n | \"task.close\"\n | \"task.open\"\n | \"task.park\"\n | \"task.unpark\"\n | \"task.accept\"\n | \"task.reject\"\n | \"task.defer\"\n | \"task.claim\"\n | \"task.release\"\n | \"task.reap\"\n | \"task.block\"\n | \"task.unblock\"\n | \"task.reparent\"\n | \"workstream.init\"\n | \"workstream.teardown\"\n // Any write made through `mu sql`, on whichever portable table it hit.\n | \"sql.write\"\n // Pre-1.2.0 name for workstream.teardown. Read-only: nothing emits it\n // any more, but ~5k ops in existing logs carry it and must keep\n // rendering. See LEGACY_INTENT_SYNONYMS in src/legacy-ops.ts.\n | \"workstream.destroy\";\n\nexport type KnownIntent = CaptureIntent | LocalIntent;\n\n/** A log row, reduced to what rendering needs. Structural so both\n * `LogRow` (the SDK shape) and raw op rows satisfy it. */\nexport interface RenderableOp {\n intent: string | null;\n /** `ops.entity`. */\n kind: string;\n /** `ops.key` — the natural key, verbatim. */\n workstreamName: string | null;\n payload: string;\n /** `ops.actor`. */\n source: string;\n op?: string;\n}\n\n/** Rendered op, split so callers can colour the verb independently.\n * `subject` is the entity the line is about; `detail` is the\n * human-readable consequence. */\nexport interface RenderedOp {\n /** Operator-facing verb, e.g. 'task close'. Colour this. */\n verb: string;\n /** The entity acted on, already stripped of workstream scope. */\n subject: string;\n /** What changed, in prose. May be empty. */\n detail: string;\n}\n\n// ─── payload access ────────────────────────────────────────────────────\n\n/** Parse an op payload into a field bag. Tolerant by design: a payload\n * that is not JSON (operator prose from `mu log \"<text>\"`) yields an empty\n * bag rather than throwing, and the caller falls back to showing the\n * text verbatim. */\nfunction fields(payload: string): Record<string, unknown> {\n if (!payload.startsWith(\"{\")) return {};\n try {\n const parsed: unknown = JSON.parse(payload);\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) return {};\n return parsed as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nfunction str(bag: Record<string, unknown>, key: string): string | undefined {\n const v = bag[key];\n return typeof v === \"string\" ? v : undefined;\n}\n\nfunction num(bag: Record<string, unknown>, key: string): number | undefined {\n const v = bag[key];\n return typeof v === \"number\" ? v : undefined;\n}\n\n/** Columns that are pure bookkeeping — never interesting on their own. */\nconst NOISE_FIELDS = new Set([\"updated_at\", \"created_at\"]);\n\n/** Field names, in the order a human wants to read them. */\nfunction changedFields(bag: Record<string, unknown>): string[] {\n return Object.keys(bag).filter((k) => !NOISE_FIELDS.has(k));\n}\n\n// ─── key parsing ───────────────────────────────────────────────────────\n\n/**\n * Split a natural key into workstream + local part.\n *\n * 'demo' -> { workstream: 'demo' }\n * 'demo/t1' -> { workstream: 'demo', local: 't1' }\n * 'demo/t1#3' -> { workstream: 'demo', local: 't1', note: '3' }\n * 'demo/a->demo/b' -> { workstream: 'demo', local: 'a', to: 'b' }\n */\nexport function parseOpKey(key: string | null): {\n workstream?: string;\n local?: string;\n note?: string;\n to?: string;\n} {\n if (key === null || key === \"\") return {};\n // Edge keys are '<ws>/<from>-><ws>/<to>'.\n const arrow = key.indexOf(\"->\");\n if (arrow !== -1) {\n const from = parseOpKey(key.slice(0, arrow));\n const to = parseOpKey(key.slice(arrow + 2));\n const out: { workstream?: string; local?: string; to?: string } = {};\n if (from.workstream !== undefined) out.workstream = from.workstream;\n if (from.local !== undefined) out.local = from.local;\n if (to.local !== undefined) out.to = to.local;\n return out;\n }\n const slash = key.indexOf(\"/\");\n if (slash === -1) return { workstream: key };\n const workstream = key.slice(0, slash);\n const rest = key.slice(slash + 1);\n const hash = rest.indexOf(\"#\");\n if (hash === -1) return { workstream, local: rest };\n return { workstream, local: rest.slice(0, hash), note: rest.slice(hash + 1) };\n}\n\n// ─── the formatter ─────────────────────────────────────────────────────\n\n/** Human verb per intent. Space-separated so it reads as a command,\n * matching how operators talk about these ('task close', not\n * 'task.close'). */\nconst VERBS: Record<KnownIntent, string> = {\n \"task.add\": \"task add\",\n \"task.update\": \"task update\",\n \"task.note\": \"task note\",\n \"task.delete\": \"task delete\",\n \"task.close\": \"task close\",\n \"task.open\": \"task open\",\n \"task.park\": \"task park\",\n \"task.unpark\": \"task unpark\",\n \"task.accept\": \"task accept\",\n \"task.reject\": \"task reject\",\n \"task.defer\": \"task defer\",\n \"task.claim\": \"task claim\",\n \"task.release\": \"task release\",\n \"task.reap\": \"task reap\",\n \"task.block\": \"task block\",\n \"task.unblock\": \"task unblock\",\n \"task.reparent\": \"task reparent\",\n \"workstream.init\": \"workstream init\",\n \"workstream.teardown\": \"workstream teardown\",\n \"workstream.destroy\": \"workstream teardown\",\n \"sql.write\": \"sql write\",\n \"agent.spawn\": \"agent spawn\",\n \"agent.close\": \"agent close\",\n \"agent.adopt\": \"agent adopt\",\n \"agent.kick\": \"agent kick\",\n \"agent.send\": \"agent send\",\n // Payload prose reads \"agent stalled ...\"; keep the verb matching it\n // so the emitted text is not printed with a second, different verb.\n \"agent.stall\": \"agent stalled\",\n \"workspace.create\": \"workspace create\",\n \"workspace.free\": \"workspace free\",\n \"workspace.refresh\": \"workspace refresh\",\n};\n\n/** Every intent this formatter knows, derived from the verb table so\n * the two can't drift. */\nexport const KNOWN_INTENTS: readonly KnownIntent[] = Object.keys(VERBS) as KnownIntent[];\n\n/** Every operator-facing verb the formatter can emit. Exported so audits\n * can check \"this emitter's verb is declared\" without re-deriving it\n * from the intent — the two are deliberately not always identical\n * (`agent.stall` renders as \"agent stalled\", matching its payload). */\nexport const KNOWN_VERBS: readonly string[] = Object.values(VERBS);\n\nfunction isKnownIntent(intent: string): intent is KnownIntent {\n return Object.hasOwn(VERBS, intent);\n}\n\n/**\n * Render one op as prose.\n *\n * Returns null when the row is not a rendered op at all — operator prose\n * from `mu log \"<text>\"` / a `--kind` ledger, which has no intent and\n * should be shown verbatim. Callers print `payload` in that case.\n */\nexport function renderOp(row: RenderableOp): RenderedOp | null {\n const intent = row.intent;\n if (intent === null) return null;\n\n // A bare status set (`setTaskStatus` with no more specific verb) uses\n // 'task.set-<status>' via intentIfUnset. Normalise it here rather than\n // enumerating six more table entries.\n const setPrefix = \"task.set-\";\n if (intent.startsWith(setPrefix)) {\n const status = intent.slice(setPrefix.length).toUpperCase();\n const { local } = parseOpKey(row.workstreamName);\n return { verb: \"task status\", subject: local ?? \"\", detail: `\\u2192 ${status}` };\n }\n\n if (!isKnownIntent(intent)) {\n // Forward-compatible: an intent written by a NEWER mu (ingested from\n // a peer's segment) — or by the one-shot scripts/migrate.ts\n // importer, whose 'migrate.v8' / 'migrate.archive' / 'migrate.status'\n // ops deliberately do NOT claim to be typed verbs — still renders\n // legibly instead of vanishing. Deliberately not a throw: sync must\n // never be blocked by a rendering gap.\n //\n // A PROSE payload is shown as the detail, because for an unknown\n // intent the prose is the only information the line carries and\n // dropping it renders a bare verb with no subject at all (which is\n // what every carried pre-1.0 log line looked like). A JSON payload is\n // still withheld — \"no raw JSON in mu log\" is a hard property —\n // and reduced to its changed field names.\n const bag = fields(row.payload);\n const detail = row.payload.startsWith(\"{\")\n ? changedFields(bag).join(\" \")\n : oneLine(row.payload, 100);\n return {\n verb: intent.replace(\".\", \" \"),\n subject: parseOpKey(row.workstreamName).local ?? \"\",\n detail,\n };\n }\n return renderKnown(row, intent);\n}\n\n/** Split from `renderOp` so the switch is this function's ONLY exit\n * path: that is what makes TS treat it as exhaustive and reject a\n * missing `KnownIntent` case at COMPILE time (TS2366), instead of\n * silently falling through to a runtime default. */\nfunction renderKnown(row: RenderableOp, intent: KnownIntent): RenderedOp {\n const bag = fields(row.payload);\n const key = parseOpKey(row.workstreamName);\n const subject = key.local ?? key.workstream ?? \"\";\n const deleted = row.op === \"del\";\n\n // Exhaustive switch over the closed union: adding an intent to VERBS\n // without handling it here is a COMPILE error, not a runtime fallback.\n switch (intent) {\n case \"task.add\": {\n const impact = num(bag, \"impact\");\n const effort = num(bag, \"effort_days\");\n const title = str(bag, \"title\");\n const bits: string[] = [];\n if (title !== undefined) bits.push(`\"${title}\"`);\n if (impact !== undefined) bits.push(`impact=${impact}`);\n if (effort !== undefined) bits.push(`effort=${effort}`);\n return { verb: VERBS[intent], subject, detail: bits.join(\" \") };\n }\n case \"task.update\": {\n const changed = changedFields(bag);\n const detail = changed\n .map((f) => {\n const v = bag[f];\n return typeof v === \"object\" ? f : `${f}=${String(v)}`;\n })\n .join(\" \");\n // A payload of only updated_at is a parent-row touch (a note or\n // edge bumped its task). Say so rather than printing nothing.\n return { verb: VERBS[intent], subject, detail: detail === \"\" ? \"(touched)\" : detail };\n }\n case \"task.note\": {\n const author = str(bag, \"author\");\n const content = str(bag, \"content\") ?? \"\";\n const n = key.note;\n const head = n === undefined ? \"\" : `#${n} `;\n const by = author === undefined || author === \"\" ? \"\" : ` by ${author}`;\n return { verb: VERBS[intent], subject, detail: `${head}${oneLine(content)}${by}` };\n }\n case \"task.delete\":\n // Always a tombstone (op='del'), so the payload is '{}' — there is\n // nothing to report beyond the fact of it.\n return { verb: VERBS[intent], subject, detail: \"\" };\n case \"task.close\":\n case \"task.open\":\n case \"task.reject\":\n case \"task.defer\": {\n const status = str(bag, \"status\");\n return {\n verb: VERBS[intent],\n subject,\n detail: status === undefined ? \"\" : `→ ${pairText(bag, status)}`,\n };\n }\n case \"task.park\":\n case \"task.unpark\":\n case \"task.accept\": {\n // All three stay OPEN, so the payload carries only the substate.\n const detail = str(bag, \"substate\") === undefined ? \"\" : `→ ${pairText(bag, \"OPEN\")}`;\n return { verb: VERBS[intent], subject, detail };\n }\n case \"task.claim\": {\n // The payload cannot name the actor on the `--self` path (owner_id\n // stays NULL by design), so attribution comes from ops.actor.\n const by = row.source === \"\" ? \"\" : `by ${row.source}`;\n const status = str(bag, \"status\");\n const tail = status === undefined ? \"\" : ` → ${status}`;\n return { verb: VERBS[intent], subject, detail: `${by}${tail}` };\n }\n case \"task.release\":\n case \"task.reap\": {\n const status = str(bag, \"status\");\n const bits: string[] = [];\n if (Object.hasOwn(bag, \"owner_id\") && bag.owner_id === null) bits.push(\"owner cleared\");\n if (status !== undefined) bits.push(`→ ${status}`);\n return { verb: VERBS[intent], subject, detail: bits.join(\" \") };\n }\n case \"task.block\":\n case \"task.unblock\":\n case \"task.reparent\": {\n // Edge ops carry the pair in the key; the task-row op that shares\n // the group is just a touch.\n if (key.to !== undefined) {\n const dir = intent === \"task.unblock\" ? \"no longer blocks\" : \"blocks\";\n return { verb: VERBS[intent], subject, detail: `${dir} ${key.to}` };\n }\n return { verb: VERBS[intent], subject, detail: deleted ? \"(edge removed)\" : \"(touched)\" };\n }\n case \"workstream.init\":\n return { verb: VERBS[intent], subject, detail: \"\" };\n case \"workstream.teardown\":\n case \"workstream.destroy\":\n return {\n verb: VERBS[intent],\n subject,\n detail: key.local === undefined ? \"\" : \"(cascaded)\",\n };\n case \"sql.write\": {\n // A raw write can hit any portable table, so name the entity and\n // the changed columns (values truncated: a note body is free text).\n if (deleted) return { verb: VERBS[intent], subject, detail: `${row.kind} deleted` };\n const edge = key.to === undefined ? \"\" : ` -> ${key.to}`;\n const changed = changedFields(bag)\n .map((f) => {\n const v = bag[f];\n return typeof v === \"object\" && v !== null ? f : `${f}=${oneLine(String(v), 40)}`;\n })\n .join(\" \");\n return {\n verb: VERBS[intent],\n subject,\n detail: `${row.kind}${edge} ${changed === \"\" ? \"(touched)\" : changed}`,\n };\n }\n // Local intents: no trigger can see these, so `emitEvent` wrote the\n // payload as prose. It is already human-readable — show it as the\n // detail rather than inventing a second phrasing. The leading verb\n // is stripped so it is not printed twice.\n case \"agent.spawn\":\n case \"agent.close\":\n case \"agent.adopt\":\n case \"agent.kick\":\n case \"agent.send\":\n case \"agent.stall\":\n case \"workspace.create\":\n case \"workspace.free\":\n case \"workspace.refresh\": {\n const verb = VERBS[intent];\n // These payloads are prose by construction (emitEvent writes them).\n // Guard anyway: a JSON payload here would otherwise be dumped raw\n // into the line, and \"no raw JSON in mu log\" is a hard property.\n if (row.payload.startsWith(\"{\")) {\n const named = str(bag, \"name\") ?? subject;\n return { verb, subject: named, detail: changedFields(bag).join(\" \") };\n }\n const prose = row.payload.startsWith(verb)\n ? row.payload.slice(verb.length).trim()\n : row.payload;\n const firstSpace = prose.indexOf(\" \");\n const name = firstSpace === -1 ? prose : prose.slice(0, firstSpace);\n const rest = firstSpace === -1 ? \"\" : prose.slice(firstSpace + 1).trim();\n return { verb, subject: name, detail: rest };\n }\n }\n}\n\n/** \"CLOSED/wontfix\", or bare \"CLOSED\" for a default substate. */\nfunction pairText(bag: Record<string, unknown>, status: string): string {\n const pair = resolvePair(status, str(bag, \"substate\"));\n return pair === null ? status : formatPair(pair);\n}\n\n/** Collapse a multi-line string to one line, truncated. Note contents\n * are free text and can be paragraphs; a log line is a line. */\nfunction oneLine(text: string, max = 60): string {\n const flat = text.replace(/\\s+/g, \" \").trim();\n return flat.length <= max ? flat : `${flat.slice(0, max - 1)}…`;\n}\n\n/**\n * Render an op as a single plain-text string. The convenience form for\n * callers that don't colour the verb separately (JSON `rendered` field,\n * TUI cards with their own column layout).\n */\nexport function renderOpLine(row: RenderableOp): string {\n const r = renderOp(row);\n if (r === null) return oneLine(row.payload, 120);\n return [r.verb, r.subject, r.detail].filter((s) => s !== \"\").join(\" \");\n}\n\n/**\n * The entity a row is about, for building a \"show me this\" command.\n * Reads `intent` + `key`, never prose.\n */\nexport function opSubject(row: RenderableOp): { kind: \"task\" | \"agent\"; id: string } | null {\n const intent = row.intent;\n if (intent === null) return null;\n const { local } = parseOpKey(row.workstreamName);\n if (intent.startsWith(\"task.\") && local !== undefined && local !== \"\") {\n return { kind: \"task\", id: local };\n }\n if (intent.startsWith(\"agent.\") || intent.startsWith(\"workspace.\")) {\n const r = renderOp(row);\n if (r !== null && r.subject !== \"\") return { kind: \"agent\", id: r.subject };\n }\n return null;\n}\n","// mu — segments: how ops LEAVE and ENTER a machine.\n//\n// THE ONE IDEA THAT MAKES THIS WORK\n// ---------------------------------\n// <MU_SYNC_DIR>/<machine_id>.jsonl\n//\n// Each machine APPENDS ONLY to its own segment and read-onlys every\n// other. No file is ever written by two machines, so there is no\n// file-level conflict, ever. That single property is what makes\n// Syncthing, rsync, scp, git, and a USB stick all adequate transport: it\n// removes the one thing every file-mover is bad at. There is nothing to\n// merge, so nothing can merge wrongly.\n//\n// TWO LOGS, ONLY ONE CRITICAL\n// ---------------------------\n// The CANONICAL log is the `ops` table inside mu.db: ACID, WAL,\n// crash-safe, written in the same transaction as the mutation it records.\n// A SEGMENT is DERIVED and REGENERABLE — literally `SELECT ... FROM ops` —\n// so losing one costs a re-flush and nothing else.\n//\n// That asymmetry is what licenses plain append-only files here, and it has\n// one concrete consequence worth stating because it looks like an\n// oversight: NO fsync ON APPEND. Losing the tail to power loss is\n// harmless, because the next flush re-derives it from the table. Paying\n// for durability twice would be paying for nothing.\n//\n// WHY NOT A SQLITE FILE PER PEER\n// ------------------------------\n// Rejected deliberately (design note): a `-wal`/`-shm` sidecar in a synced\n// folder is THE canonical way to corrupt a SQLite DB; a torn transfer is\n// fatal to the WHOLE file rather than costing one JSONL line; and page\n// churn defeats rsync/Syncthing delta transfer, whereas an append-only\n// file is the best case for it.\n//\n// ROBUSTNESS: FOUR LAYERS\n// -----------------------\n// The pattern every append-only log uses (RocksDB, Kafka, etcd,\n// SQLite-WAL): detect the bad record, stop at it, refetch the tail later.\n// Never guess, never skip-and-continue past damage.\n//\n// 1. JSON.parse failure = torn write. FREE, and truncation is the\n// DOMINANT failure mode (a transfer caught in flight).\n// 2. crc32 per line = bit rot that JSON.parse would happily\n// accept. Belt-and-braces, not load-bearing — see the note's\n// honesty about this. ~5 LOC via node:zlib.\n// 3. Monotonic hlc = reordering, duplication, and silent\n// mid-file truncation. Structural, zero extra bytes.\n// 4. Manifest sidecar = whole-file verification (count, last_hlc,\n// sha256). REPORTS, does not halt: it fires when every remaining\n// line is individually valid (a truncation on a line boundary), so\n// the prefix is safe to apply and the fix is a fresh copy.\n//\n// On a bad record (layers 1-3) we stop at the last GOOD one and advance\n// the watermark only that far, then report it. Because `UNIQUE (machine_id, hlc)` makes\n// ingest idempotent, the universal repair is \"re-read from zero\" — so a\n// damaged segment is recoverable, never fatal.\n\nimport { createHash } from \"node:crypto\";\nimport {\n appendFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join, resolve, sep } from \"node:path\";\nimport { crc32 } from \"node:zlib\";\nimport { applyOp, type Op, OpEntityNotSyncedError, reprojectDeferredOps } from \"./apply.js\";\nimport { type Db, SYNCED_ENTITIES } from \"./db.js\";\nimport { locksDir, withFileLock } from \"./file-lock.js\";\nimport { receiveHlc } from \"./hlc.js\";\nimport { isLegacyLogOnlyIntent } from \"./legacy-ops.js\";\n\n/** Current segment line format. Bumped only on a breaking shape change;\n * a reader that sees a version it does not know REFUSES the line rather\n * than guessing at its meaning. */\nexport const SEGMENT_FORMAT_VERSION = 1;\n\n/** Segment filename suffix. */\nconst SEGMENT_EXT = \".jsonl\";\n/** Manifest filename suffix. */\nconst MANIFEST_EXT = \".manifest\";\n\n/** One serialized op, as it appears on a line of a segment. */\nexport interface SegmentLine {\n v: number;\n hlc: string;\n machine: string;\n group: string;\n intent: string | null;\n actor: string | null;\n entity: string;\n key: string;\n op: \"put\" | \"del\";\n payload: unknown;\n crc: string;\n}\n\n/** Whole-file verification sidecar. */\nexport interface SegmentManifest {\n v: number;\n machine: string;\n count: number;\n lastHlc: string | null;\n sha256: string;\n updatedAt: string;\n}\n\n/** Why a segment line was rejected. Reported, never silently swallowed. */\nexport type SegmentDefectKind =\n | \"torn-write\"\n | \"manifest-mismatch\"\n | \"crc-mismatch\"\n | \"non-monotonic-hlc\"\n | \"unknown-version\"\n | \"malformed-shape\"\n | \"entity-not-synced\"\n | \"duplicate-op\";\n\nexport interface SegmentDefect {\n kind: SegmentDefectKind;\n /** 1-based line number within the segment. */\n line: number;\n detail: string;\n}\n\n// ─── sync dir + naming ────────────────────────────────────────────────\n\n/**\n * The sync directory, or null when sync is not configured.\n *\n * Null is the normal single-machine case, and every entry point here\n * treats it as \"do nothing, cost nothing\" rather than an error. Sync is\n * opt-in by setting one env var; there is no config file and no\n * membership list (see `discoverPeers`).\n */\nexport function syncDir(): string | null {\n const dir = process.env.MU_SYNC_DIR;\n if (dir === undefined || dir.trim() === \"\") return null;\n refuseUserSyncDirDuringTests(dir);\n return dir;\n}\n\n/**\n * The sibling of `refuseUserDbDuringTests` in src/db.ts, for the OTHER\n * piece of real state a stray test can reach.\n *\n * Pointing MU_DB_PATH at a temp file is not enough to contain a test:\n * sync is ambient, so the first flush stamps the temp DB's fresh\n * `machine_id` onto a segment in whatever MU_SYNC_DIR names. On a dev\n * box that is the user's REAL sync folder, and the result is a\n * permanent phantom peer — `mu sync` lists a machine that never\n * existed, forever, because absence of a segment is the only way a\n * peer disappears and nothing prunes them.\n *\n * Observed: eight such phantoms in ~/mu, each holding one op from a\n * throwaway `workstream init`. They came from the documented\n * smoke-test recipe (`MU_DB_PATH=/tmp/mu-smoke.db mu <verb>`), which\n * overrides the DB and says nothing about the sync dir.\n *\n * So: under a test runner, refuse a sync dir that is not clearly\n * disposable. Tests that exercise sync legitimately already point at a\n * per-test temp dir (mkdtemp under $TMPDIR), which is allowed; the\n * refusal only fires for a path outside it, which is by definition\n * somebody's real folder. Production never sets VITEST, so this is a\n * no-op for the shipped CLI.\n */\nfunction refuseUserSyncDirDuringTests(dir: string): void {\n const inTest = process.env.VITEST !== undefined || process.env.NODE_ENV === \"test\";\n if (!inTest) return;\n const resolved = resolve(dir);\n const temp = resolve(tmpdir());\n if (resolved === temp || resolved.startsWith(temp + sep)) return;\n throw new Error(\n `syncDir refused: tests must NEVER flush ops into a real sync dir (MU_SYNC_DIR=${resolved}). ` +\n `Doing so writes a segment named after this run's machine_id and leaves a permanent phantom peer. ` +\n `Point MU_SYNC_DIR at a mkdtemp() path under ${temp}, or leave it unset (test/_setup.ts clears it by default).`,\n );\n}\n\n/** This machine's id — the identity every op it writes is stamped with. */\nexport function localMachineId(db: Db): string {\n const row = db.prepare(\"SELECT machine_id FROM machine_identity WHERE id = 1\").get() as\n | { machine_id: string }\n | undefined;\n if (row === undefined) throw new Error(\"machine_identity row missing; not a mu DB\");\n return row.machine_id;\n}\n\n/** Path of a machine's own segment inside `dir`. */\nexport function segmentPath(dir: string, machineId: string): string {\n return join(dir, `${machineId}${SEGMENT_EXT}`);\n}\n\nfunction manifestPath(segment: string): string {\n return segment.replace(new RegExp(`${SEGMENT_EXT}$`), MANIFEST_EXT);\n}\n\n// ─── framing ──────────────────────────────────────────────────────────\n\n/**\n * Canonical serialization the crc is computed over.\n *\n * Field order is FIXED here rather than taken from object key order, so\n * two machines (or two Node versions) cannot disagree about the bytes and\n * produce a spurious mismatch. `payload` is embedded as its stored JSON\n * TEXT, verbatim, for the same reason: re-serializing a parsed object\n * risks key reordering and number reformatting.\n */\nfunction canonicalBytes(line: Omit<SegmentLine, \"crc\">, payloadText: string): string {\n return [\n String(line.v),\n line.hlc,\n line.machine,\n line.group,\n line.intent ?? \"\",\n line.actor ?? \"\",\n line.entity,\n line.key,\n line.op,\n payloadText,\n ].join(\"\\u001f\"); // Unit Separator: cannot occur in any of these fields\n}\n\nfunction computeCrc(line: Omit<SegmentLine, \"crc\">, payloadText: string): string {\n return crc32(canonicalBytes(line, payloadText)).toString(16).padStart(8, \"0\");\n}\n\n/**\n * `ops.payload` is TEXT that is *usually* JSON but is not guaranteed to\n * be: `appendLog` has always accepted a bare prose payload (entity\n * 'message' via `mu log \"text\"`), and pre-1.0 rows carry plenty of it.\n * Embedding such text raw produced `\"payload\":Added 5 tasks, ...` — a\n * line that is not JSON at all, which every later read then reported as\n * a torn write, on every single mu invocation, forever (the segment is\n * regenerated from `ops`, so the repair re-emitted the same bad bytes).\n *\n * So: pass valid JSON through verbatim (byte-preserving, which is what\n * the crc contract needs), and encode anything else as a JSON string.\n */\nfunction payloadTextFor(payload: string): string {\n try {\n JSON.parse(payload);\n return payload;\n } catch {\n return JSON.stringify(payload);\n }\n}\n\n/** Serialize one op row to a segment line (without its trailing newline). */\nexport function encodeSegmentLine(op: {\n hlc: string;\n machineId: string;\n groupId: string;\n intent: string | null;\n actor: string | null;\n entity: string;\n key: string;\n op: \"put\" | \"del\";\n payload: string;\n}): string {\n const base: Omit<SegmentLine, \"crc\"> = {\n v: SEGMENT_FORMAT_VERSION,\n hlc: op.hlc,\n machine: op.machineId,\n group: op.groupId,\n intent: op.intent,\n actor: op.actor,\n entity: op.entity,\n key: op.key,\n op: op.op,\n payload: null, // replaced below; kept out of the crc input shape\n };\n const payloadText = payloadTextFor(op.payload);\n const crc = computeCrc(base, payloadText);\n // Assemble by hand so `payload` is embedded as raw JSON rather than\n // being re-encoded, keeping the bytes the crc covered.\n return `{\"v\":${base.v},\"hlc\":${JSON.stringify(base.hlc)},\"machine\":${JSON.stringify(\n base.machine,\n )},\"group\":${JSON.stringify(base.group)},\"intent\":${JSON.stringify(\n base.intent,\n )},\"actor\":${JSON.stringify(base.actor)},\"entity\":${JSON.stringify(\n base.entity,\n )},\"key\":${JSON.stringify(base.key)},\"op\":${JSON.stringify(\n base.op,\n )},\"payload\":${payloadText},\"crc\":${JSON.stringify(crc)}}`;\n}\n\n/** Outcome of decoding one line. */\ntype DecodeResult =\n | { ok: true; line: SegmentLine; payloadText: string }\n | { ok: false; kind: SegmentDefectKind; detail: string };\n\n/**\n * Decode and verify one line.\n *\n * Layers 1 and 2 both live here. Layer 1 (JSON.parse) is what actually\n * fires in practice — a truncated transfer leaves a partial line, which\n * cannot parse. Layer 2 (crc) only catches damage that leaves valid JSON,\n * i.e. bit rot inside a string or number.\n */\nfunction decodeLine(raw: string): DecodeResult {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n // LAYER 1: torn write. The dominant failure mode, and free.\n //\n // But only call it TORN when the line actually looks cut off. A\n // complete line that does not parse has a different cause (a writer\n // bug, or a hand edit) and a different remediation, and mislabelling\n // it sends the reader hunting for a crash-during-write that never\n // happened.\n const detail = err instanceof Error ? err.message : String(err);\n return looksComplete(raw)\n ? { ok: false, kind: \"malformed-shape\", detail: `complete line is not valid JSON: ${detail}` }\n : { ok: false, kind: \"torn-write\", detail };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { ok: false, kind: \"malformed-shape\", detail: \"line is not a JSON object\" };\n }\n const obj = parsed as Record<string, unknown>;\n\n if (typeof obj.v !== \"number\") {\n return { ok: false, kind: \"malformed-shape\", detail: \"missing numeric 'v'\" };\n }\n if (obj.v !== SEGMENT_FORMAT_VERSION) {\n // Refuse rather than guess: a future version may mean different\n // things by the same field names.\n return {\n ok: false,\n kind: \"unknown-version\",\n detail: `segment format v${obj.v}, this mu understands v${SEGMENT_FORMAT_VERSION}`,\n };\n }\n for (const field of [\"hlc\", \"machine\", \"group\", \"entity\", \"key\", \"op\", \"crc\"]) {\n if (typeof obj[field] !== \"string\") {\n return { ok: false, kind: \"malformed-shape\", detail: `missing string '${field}'` };\n }\n }\n const opKind = obj.op;\n if (opKind !== \"put\" && opKind !== \"del\") {\n return { ok: false, kind: \"malformed-shape\", detail: `bad op '${String(opKind)}'` };\n }\n if (!(\"payload\" in obj)) {\n return { ok: false, kind: \"malformed-shape\", detail: \"missing 'payload'\" };\n }\n\n // Recover the payload's ORIGINAL text from the raw line so the crc is\n // computed over the same bytes the writer covered. Re-serializing the\n // parsed value could reorder keys and would false-positive.\n const payloadText = extractPayloadText(raw);\n if (payloadText === null) {\n return { ok: false, kind: \"malformed-shape\", detail: \"could not locate payload text\" };\n }\n\n const line: SegmentLine = {\n v: obj.v,\n hlc: obj.hlc as string,\n machine: obj.machine as string,\n group: obj.group as string,\n intent: typeof obj.intent === \"string\" ? obj.intent : null,\n actor: typeof obj.actor === \"string\" ? obj.actor : null,\n entity: obj.entity as string,\n key: obj.key as string,\n op: opKind,\n payload: obj.payload,\n crc: obj.crc as string,\n };\n\n // LAYER 2: crc over the canonical bytes. Belt-and-braces.\n const expected = computeCrc({ ...line, payload: null }, payloadText);\n if (expected !== line.crc) {\n return {\n ok: false,\n kind: \"crc-mismatch\",\n detail: `crc ${line.crc} != computed ${expected} (bit rot?)`,\n };\n }\n return { ok: true, line, payloadText: storedPayloadFor(line, payloadText) };\n}\n\n/**\n * The text to store back into `ops.payload`, undoing the wrapping\n * `payloadTextFor` applied on the way out, so a peer's ops table holds\n * the bytes the origin's does and `mu log` renders a prose message as\n * prose rather than as a quoted string.\n *\n * The inverse is exact because the wrap rule is: wrap iff the stored\n * text is NOT valid JSON. So unwrap iff the line's payload is a JSON\n * string whose contents are NOT valid JSON — anything else was passed\n * through verbatim and must stay that way.\n */\nfunction storedPayloadFor(line: SegmentLine, payloadText: string): string {\n if (typeof line.payload !== \"string\") return payloadText;\n try {\n JSON.parse(line.payload);\n return payloadText;\n } catch {\n return line.payload;\n }\n}\n\n/** Whether a line carries the trailing framing `encodeSegmentLine`\n * always writes. A transfer caught in flight cannot: it stops wherever\n * the bytes stopped. */\nfunction looksComplete(raw: string): boolean {\n return raw.trimEnd().endsWith(\"}\") && raw.includes(',\"crc\":\"');\n}\n\n/** Slice out the raw `\"payload\":<json>` text, between its key and the\n * trailing `,\"crc\":`. Written by `encodeSegmentLine`, so the anchors are\n * exact rather than heuristic. */\nfunction extractPayloadText(raw: string): string | null {\n const start = raw.indexOf('\"payload\":');\n if (start < 0) return null;\n const end = raw.lastIndexOf(',\"crc\":');\n if (end <= start) return null;\n return raw.slice(start + '\"payload\":'.length, end);\n}\n\n// ─── flush: ops -> my segment ─────────────────────────────────────────\n\nexport interface FlushResult {\n /** Absolute path written, or null when sync is not configured. */\n segmentPath: string | null;\n /** Ops appended by this call. */\n appended: number;\n /** Total lines in the segment afterwards. */\n total: number;\n /** Ops skipped because their entity is machine-local. */\n skippedLocal: number;\n /**\n * Non-null when THIS MACHINE'S OWN segment was found defective past\n * its last good line (bit rot, a torn write not at EOF, a bad manual\n * edit). A segment is DERIVED and REGENERABLE from the canonical\n * `ops` table, so the repair is to truncate the file back to its\n * last good record and let the append below regenerate the rest —\n * never to append after the damage, which is the defect that caused\n * unbounded regrowth (each flush re-deriving the same ops from a\n * watermark frozen at the corruption point and stacking them after\n * it, forever). Surfaced here rather than printed directly: this\n * module does no I/O beyond the filesystem, callers decide how loud\n * to be (`mu sync`, `ambientFlush`).\n */\n selfRepaired: SegmentDefect | null;\n}\n\n/**\n * Append this machine's not-yet-flushed ops to its own segment.\n *\n * FILTERING IS LOAD-BEARING. Only ops whose entity is in\n * `SYNCED_ENTITIES` are written. Machine-local ops (agent.*, workspace.*)\n * are captured and DO appear in `mu log`, but they must never reach a\n * segment: they carry pane ids and absolute paths that are meaningless,\n * and frequently wrong, on another machine. \"Not synced\" is not \"not\n * logged\".\n *\n * Also filters `machine_id = <me>`: a segment holds ONE machine's ops.\n * Ops ingested from a peer live in our `ops` table too, and re-flushing\n * them into our own segment would duplicate a peer's history under our\n * name — and would grow without bound as two machines echoed each other.\n *\n * The high-water mark is the last hlc already in the file (read from the\n * manifest when present, else derived by scanning), so flush is\n * incremental and idempotent: calling it twice appends nothing the second\n * time.\n */\nexport async function flushSegment(db: Db, dir: string | null = syncDir()): Promise<FlushResult> {\n if (dir === null)\n return { segmentPath: null, appended: 0, total: 0, skippedLocal: 0, selfRepaired: null };\n\n const machineId = localMachineId(db);\n mkdirSync(dir, { recursive: true });\n const path = segmentPath(dir, machineId);\n\n // Serialise concurrent local flushes so two processes cannot interleave\n // partial lines in the same file. Keyed on the sync dir + machine, since\n // that names the single file being appended to.\n const lockName = createHash(\"sha256\")\n .update(`${dir}\\u001f${machineId}`)\n .digest(\"hex\")\n .slice(0, 16);\n return withFileLock(join(locksDir(), `segment-${lockName}.lock`), `segment:${machineId}`, () =>\n Promise.resolve(flushLocked(db, path, machineId)),\n );\n}\n\nfunction flushLocked(db: Db, path: string, machineId: string): FlushResult {\n const existing = readSegmentTail(path);\n const since = existing.lastHlc;\n\n // The defect this exists to close: readSegmentTail STOPS at the first\n // bad record either because it hit real damage or because it simply\n // ran out of well-formed lines. Those two cases used to be\n // indistinguishable to this function, and treating \"stopped early on\n // damage\" the same as \"reached clean EOF\" is exactly what let a\n // corrupted line grow the file forever: `since` never advanced past\n // it, so every flush re-selected and re-appended the same ops after\n // it again, unbounded. Distinguish them and heal our own segment\n // (truncate back to the last verified-good line) before appending\n // anything new, rather than stacking fresh data after the wound.\n let selfRepaired: SegmentDefect | null = null;\n if (existing.defect !== null) {\n selfRepaired = existing.defect;\n writeFileSync(\n path,\n existing.goodLines.length > 0 ? `${existing.goodLines.join(\"\\n\")}\\n` : \"\",\n \"utf8\",\n );\n }\n\n const rows = db\n .prepare(\n `SELECT hlc, machine_id, group_id, intent, actor, entity, key, op, payload\n FROM ops\n WHERE machine_id = @machineId\n AND (@since IS NULL OR hlc > @since)\n ORDER BY hlc`,\n )\n .all({ machineId, since }) as Array<{\n hlc: string;\n machine_id: string;\n group_id: string;\n intent: string | null;\n actor: string | null;\n entity: string;\n key: string;\n op: string;\n payload: string;\n }>;\n\n const synced = new Set<string>(SYNCED_ENTITIES);\n const lines: string[] = [];\n let skippedLocal = 0;\n let lastHlc = existing.lastHlc;\n\n for (const row of rows) {\n if (!synced.has(row.entity) || isLegacyLogOnlyIntent(row.intent)) {\n skippedLocal += 1;\n continue;\n }\n lines.push(\n encodeSegmentLine({\n hlc: row.hlc,\n machineId: row.machine_id,\n groupId: row.group_id,\n intent: row.intent,\n actor: row.actor,\n entity: row.entity,\n key: row.key,\n op: row.op === \"del\" ? \"del\" : \"put\",\n payload: row.payload,\n }),\n );\n lastHlc = row.hlc;\n }\n\n if (lines.length > 0) {\n // NO fsync. The segment is derived from `ops`; a lost tail costs one\n // re-flush, so paying for durability here would buy nothing.\n appendFileSync(path, `${lines.join(\"\\n\")}\\n`, \"utf8\");\n } else if (!existsSync(path)) {\n // Create the file even with nothing to say, so peers can discover\n // this machine before its first change.\n writeFileSync(path, \"\", \"utf8\");\n }\n\n const total = existing.count + lines.length;\n writeManifest(path, machineId, total, lastHlc);\n return { segmentPath: path, appended: lines.length, total, skippedLocal, selfRepaired };\n}\n\n/** Number of GOOD lines in a segment (stopping at the first defect, as\n * ingest does). The denominator of \"how far behind am I\" — exported for\n * `mu sync`'s peer table. */\nexport function segmentLineCount(path: string): number {\n return readSegmentTail(path).count;\n}\n\n/**\n * Count + last hlc of an existing segment, cheaply, plus (this is the\n * part that matters) WHY it stopped: `defect` is null when every line\n * decoded cleanly (a genuine, trustworthy EOF), and set when decoding\n * stopped early because a line was bad. Conflating those two used to be\n * the whole bug: a stop-on-damage looked exactly like a stop-on-EOF to\n * `flushLocked`, so it kept appending after the damage forever.\n * `goodLines` is the verified-good prefix, raw, so a caller that finds a\n * defect can truncate back to it byte-for-byte rather than re-encoding.\n */\nfunction readSegmentTail(path: string): {\n count: number;\n lastHlc: string | null;\n goodLines: readonly string[];\n defect: SegmentDefect | null;\n} {\n if (!existsSync(path)) return { count: 0, lastHlc: null, goodLines: [], defect: null };\n const lines = segmentLines(readFileSync(path, \"utf8\"));\n let lastHlc: string | null = null;\n let count = 0;\n let defect: SegmentDefect | null = null;\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index];\n if (line === undefined) break;\n // Same rule as `ingestSegment`: a blank line is damage. Skipping it\n // here while peers halt on it left the owner blind to the defect,\n // so it never self-repaired and every peer stopped there forever.\n if (line.trim() === \"\") {\n defect = { kind: \"malformed-shape\", line: index + 1, detail: \"blank line\" };\n break;\n }\n const decoded = decodeLine(line);\n if (!decoded.ok) {\n defect = { kind: decoded.kind, line: index + 1, detail: decoded.detail };\n break;\n }\n lastHlc = decoded.line.hlc;\n count += 1;\n }\n return { count, lastHlc, goodLines: lines.slice(0, count), defect };\n}\n\n/** Split a segment into its lines exactly as every reader must: a\n * trailing newline is normal, a trailing PARTIAL line is a torn write\n * (kept so layer 1 can catch it), and a blank line stays in place so\n * the caller reports it as damage. One splitter, so the owner's\n * self-check and a peer's ingest cannot disagree about line numbers. */\nfunction segmentLines(raw: string): string[] {\n const lines = raw.split(\"\\n\");\n if (lines.length > 0 && lines[lines.length - 1] === \"\") lines.pop();\n return lines;\n}\n\n/** LAYER 4: whole-file verification sidecar. */\nfunction writeManifest(path: string, machine: string, count: number, lastHlc: string | null): void {\n const bytes = existsSync(path) ? readFileSync(path) : Buffer.alloc(0);\n const manifest: SegmentManifest = {\n v: SEGMENT_FORMAT_VERSION,\n machine,\n count,\n lastHlc,\n sha256: createHash(\"sha256\").update(bytes).digest(\"hex\"),\n updatedAt: new Date().toISOString(),\n };\n writeFileSync(manifestPath(path), `${JSON.stringify(manifest, null, 2)}\\n`, \"utf8\");\n}\n\n/** Read a segment's manifest, or null when absent/unparsable. */\nexport function readManifest(segment: string): SegmentManifest | null {\n const path = manifestPath(segment);\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as SegmentManifest;\n } catch {\n return null;\n }\n}\n\n/**\n * Verify a segment against its manifest (layer 4).\n *\n * Whole-file, so it catches damage the per-line layers cannot see: a\n * segment silently replaced wholesale, or truncated exactly on a line\n * boundary (where every remaining line is individually valid).\n */\nexport function verifyAgainstManifest(\n segment: string,\n): { ok: true } | { ok: false; reason: string } {\n const manifest = readManifest(segment);\n if (manifest === null) return { ok: true }; // no manifest: nothing to check\n if (!existsSync(segment)) return { ok: false, reason: \"segment missing but manifest present\" };\n const sha = createHash(\"sha256\").update(readFileSync(segment)).digest(\"hex\");\n if (sha === manifest.sha256) return { ok: true };\n // A GROWN file is expected: the peer appended after writing the\n // manifest we have, or our copy is mid-transfer. That is not damage.\n const tail = readSegmentTail(segment);\n if (tail.count >= manifest.count) {\n return { ok: true };\n }\n return {\n ok: false,\n reason: `sha mismatch and file has FEWER records than the manifest (${tail.count} < ${manifest.count}): truncated`,\n };\n}\n\n// ─── peer discovery ───────────────────────────────────────────────────\n\nexport interface PeerSegment {\n /** Machine id the segment belongs to. */\n machineId: string;\n /** Key of this FILE's watermark in `sync_peers`: the machine id for\n * the peer's own segment, the full file stem for a conflict copy.\n * A copy diverges from its original, so line N of one says nothing\n * about line N of the other; sharing one watermark skipped every op\n * that existed only in the copy. Optional for SDK callers written\n * before it existed: absent means the machine id (`peerWatermarkKey`). */\n watermarkKey?: string;\n /** Path on disk. */\n path: string;\n /** True for a Syncthing-style conflict copy. */\n conflictCopy: boolean;\n}\n\n/**\n * Every segment in `dir` that is not mine.\n *\n * IMPLICIT, with no membership list. `MU_SYNC_PEERS` was explicitly\n * rejected as \"a config file with extra steps that must be kept\n * consistent across every machine\" — dropping a segment in the folder\n * joins the cluster, deleting it leaves.\n *\n * CONFLICT COPIES ARE INGESTED, not ignored. Syncthing names them\n * `<machine>.sync-conflict-20260609-123456-ABCDEFG.jsonl`; they are still\n * valid op logs, and dedup by `(machine_id, hlc)` makes reading them\n * safe. Ignoring them would silently drop real ops precisely when\n * something already went wrong.\n */\nexport function discoverPeers(dir: string, selfMachineId: string): PeerSegment[] {\n if (!existsSync(dir)) return [];\n const peers: PeerSegment[] = [];\n for (const name of readdirSync(dir)) {\n if (!name.endsWith(SEGMENT_EXT)) continue;\n const stem = name.slice(0, -SEGMENT_EXT.length);\n // Syncthing: \"<machine>.sync-conflict-<date>-<time>-<id>\"\n const conflictAt = stem.indexOf(\".sync-conflict-\");\n const conflictCopy = conflictAt > 0;\n const machineId = conflictCopy ? stem.slice(0, conflictAt) : stem;\n if (machineId === selfMachineId && !conflictCopy) continue;\n // A conflict copy OF MY OWN segment is still a peer's view of my\n // history; ingesting it is a no-op thanks to (machine_id, hlc)\n // dedupe, so allow it rather than special-casing.\n const path = join(dir, name);\n try {\n if (!statSync(path).isFile()) continue;\n } catch {\n continue;\n }\n peers.push({ machineId, watermarkKey: conflictCopy ? stem : machineId, path, conflictCopy });\n }\n return peers.sort((a, b) => a.path.localeCompare(b.path));\n}\n\n/** The `sync_peers` key for a segment file: `watermarkKey`, else the\n * machine id (the pre-watermarkKey `PeerSegment` shape). */\nexport function peerWatermarkKey(peer: PeerSegment): string {\n return peer.watermarkKey ?? peer.machineId;\n}\n\n// ─── watermarks ───────────────────────────────────────────────────────\n\n/**\n * How far into one segment file we have applied, keyed by\n * `PeerSegment.watermarkKey` (the machine id, or a conflict copy's own\n * stem).\n *\n * ONE INTEGER SUFFICES because segments are append-only and ordered — a\n * set or a vector clock would be strictly more state for no more\n * information. Stored in `sync_peers.last_applied_seq`, which has been in\n * the v9 schema unused until now.\n *\n * The integer is a LINE COUNT within that peer's segment, not the peer's\n * `ops.seq` (which is a local-only cursor on their machine and means\n * nothing here).\n */\nexport function getWatermark(db: Db, watermarkKey: string): number {\n const row = db\n .prepare(\"SELECT last_applied_seq AS n FROM sync_peers WHERE machine_id = ?\")\n .get(watermarkKey) as { n: number } | undefined;\n return row?.n ?? 0;\n}\n\nexport function setWatermark(db: Db, watermarkKey: string, value: number): void {\n db.prepare(\n `INSERT INTO sync_peers (machine_id, last_applied_seq, last_seen_at)\n VALUES (@machineId, @value, @seenAt)\n ON CONFLICT (machine_id) DO UPDATE\n SET last_applied_seq = @value, last_seen_at = @seenAt`,\n ).run({ machineId: watermarkKey, value, seenAt: new Date().toISOString() });\n}\n\n/** Reset a peer's watermark so the next ingest re-reads from zero. The\n * universal repair, safe because ingest is idempotent. */\nexport function resetWatermark(db: Db, watermarkKey: string): void {\n setWatermark(db, watermarkKey, 0);\n}\n\n// ─── ingest: peer segment -> applyOp ──────────────────────────────────\n\nexport interface IngestResult {\n machineId: string;\n path: string;\n /** Lines consumed past the starting watermark: applied plus skipped\n * (refused, re-delivered, historical log-only) lines. A line that\n * halted ingest is not counted. */\n read: number;\n /** Ops applied (some are no-ops: already present, or lost an LWW). */\n applied: number;\n /** Ops that changed a row. */\n changed: number;\n /** Watermark after this ingest. */\n watermark: number;\n /** Problems found, in line order. Reported, never swallowed. */\n defects: readonly SegmentDefect[];\n /** True iff a defect stopped us short of the file's end. */\n truncatedAt: number | null;\n}\n\n/**\n * Read one peer segment from its watermark and apply each op.\n *\n * STOPS AT THE FIRST DAMAGED RECORD and advances the watermark only that\n * far. Never skips a damaged line to continue past it: in an ordered log,\n * a gap is indistinguishable from reordering, and applying ops around a\n * hole risks a state neither machine ever had. The tail is re-read on the\n * next ingest, by which time the transfer has usually completed.\n *\n * A REFUSED line is not a damaged one. A well-formed op naming an entity\n * that must not travel (`entity-not-synced`) is reported as a defect and\n * SKIPPED: it projects nothing, so it leaves no hole, and halting on it\n * makes the watermark unrecoverable by any means the CLI offers.\n *\n * A RE-DELIVERED line is not a damaged one either. A block of ops the\n * peer already wrote, appended verbatim a second time, breaks the hlc\n * ordering (`duplicate-op`) without losing anything: every one of those\n * ops is already in our `ops` table byte-for-byte, so applying them\n * again is a no-op by construction. Halting there is unrecoverable —\n * `--repair` re-reads from zero straight back into the same three\n * lines, forever — so it is reported and SKIPPED. A non-monotonic line\n * we have NOT seen before is still real damage and still halts.\n *\n * A HISTORICAL log-only line (`isLegacyLogOnlyIntent`: a prose payload\n * under an otherwise projectable entity, written by a mu older than the\n * flush filter) is skipped silently, exactly as flush and `--from` skip\n * it. Applying it threw a JSON SyntaxError that rolled back the whole\n * segment on every invocation.\n *\n * A manifest mismatch (layer 4) is reported but does not halt: every\n * line still present is individually valid, so applying them is safe.\n *\n * Calls `receiveHlc` per op so the local clock advances past the peer's,\n * which is what makes \"laptop edits after seeing the devserver's op\" order\n * correctly rather than losing to it.\n */\nexport function ingestSegment(db: Db, peer: PeerSegment): IngestResult {\n const defects: SegmentDefect[] = [];\n const watermarkKey = peerWatermarkKey(peer);\n const start = getWatermark(db, watermarkKey);\n\n if (!existsSync(peer.path)) {\n return {\n machineId: peer.machineId,\n path: peer.path,\n read: 0,\n applied: 0,\n changed: 0,\n watermark: start,\n defects,\n truncatedAt: null,\n };\n }\n\n const verified = verifyAgainstManifest(peer.path);\n if (!verified.ok) {\n // Whole-file damage, distinct from a torn line: every remaining\n // record may be individually valid (truncation exactly on a line\n // boundary), which is precisely what the per-line layers cannot see.\n // Reported, NOT a halt: the lines that remain are a valid prefix, so\n // the per-line layers below still decide how far to apply.\n defects.push({ kind: \"manifest-mismatch\", line: 0, detail: verified.reason });\n }\n\n const lines = segmentLines(readFileSync(peer.path, \"utf8\"));\n\n let applied = 0;\n let changed = 0;\n let watermark = start;\n let truncatedAt: number | null = null;\n let previousHlc: string | null = null;\n\n // Seed the monotonicity check from the last line we already accepted,\n // so a segment rewritten out of order is caught even mid-file.\n if (start > 0 && start <= lines.length) {\n const prior = lines[start - 1];\n if (prior !== undefined) {\n const decoded = decodeLine(prior);\n if (decoded.ok) previousHlc = decoded.line.hlc;\n }\n }\n\n const run = db.transaction(() => {\n for (let index = start; index < lines.length; index++) {\n const raw = lines[index];\n const lineNo = index + 1;\n if (raw === undefined || raw.trim() === \"\") {\n // Blank line: treat as damage rather than skipping, since a\n // well-formed segment never contains one.\n defects.push({ kind: \"malformed-shape\", line: lineNo, detail: \"blank line\" });\n truncatedAt = lineNo;\n break;\n }\n\n const decoded = decodeLine(raw);\n if (!decoded.ok) {\n defects.push({ kind: decoded.kind, line: lineNo, detail: decoded.detail });\n truncatedAt = lineNo;\n break;\n }\n\n const op: Op = {\n hlc: decoded.line.hlc,\n machineId: decoded.line.machine,\n groupId: decoded.line.group,\n actor: decoded.line.actor,\n intent: decoded.line.intent,\n entity: decoded.line.entity,\n key: decoded.line.key,\n op: decoded.line.op,\n payload: decoded.payloadText,\n };\n\n // LAYER 3: monotonic hlc. Structural, zero extra bytes. Catches\n // reordering, duplication, and silent mid-file truncation.\n if (previousHlc !== null && decoded.line.hlc <= previousHlc) {\n // ... unless we have this exact op already, in which case the\n // peer re-appended a block it had already written and there is\n // nothing to reorder: an identical op carries identical state,\n // so skipping it cannot produce a state neither machine had.\n if (isAlreadyRecorded(db, op)) {\n defects.push({\n kind: \"duplicate-op\",\n line: lineNo,\n detail: `hlc ${decoded.line.hlc} re-delivered — skipped`,\n });\n watermark = lineNo;\n continue;\n }\n defects.push({\n kind: \"non-monotonic-hlc\",\n line: lineNo,\n detail: `hlc ${decoded.line.hlc} <= previous ${previousHlc}`,\n });\n truncatedAt = lineNo;\n break;\n }\n\n if (isLegacyLogOnlyIntent(decoded.line.intent)) {\n previousHlc = op.hlc;\n watermark = lineNo;\n continue;\n }\n\n try {\n const result = applyIncomingOp(db, op);\n if (result.changed) changed += 1;\n } catch (err) {\n if (err instanceof OpEntityNotSyncedError) {\n // A peer sent something that must never cross a machine\n // boundary (a pane id, an absolute path). Report it as a\n // bad-peer defect — and SKIP THE LINE rather than stopping.\n //\n // Stopping is right for DAMAGE, where a gap is\n // indistinguishable from reordering. This line is not\n // damaged: it decoded, its crc verified, and its hlc is in\n // order. Refusing it projects nothing, so there is no hole to\n // reason about, and continuing is strictly safer than the\n // alternative we actually shipped — one such line at position\n // 2533 of a 20,305-line segment froze that peer's watermark\n // permanently, and `mu sync --repair` re-read straight back\n // into the same wall. A defect is reported; nothing is lost;\n // the remaining 17,772 ops arrive.\n defects.push({\n kind: \"entity-not-synced\",\n line: lineNo,\n detail: `peer sent machine-local entity '${op.entity}' — skipped`,\n });\n previousHlc = op.hlc;\n watermark = lineNo;\n continue;\n }\n throw err;\n }\n\n applied += 1;\n previousHlc = op.hlc;\n watermark = lineNo;\n }\n setWatermark(db, watermarkKey, watermark);\n });\n run.immediate();\n\n return {\n machineId: peer.machineId,\n path: peer.path,\n read: watermark - start,\n applied,\n changed,\n watermark,\n defects,\n truncatedAt,\n };\n}\n\n/**\n * Is this exact op already in our `ops` table?\n *\n * `UNIQUE (machine_id, hlc)` is the op's identity everywhere else in\n * the system — it is what makes `applyIncomingOp` idempotent and\n * \"re-read from zero\" a safe universal repair — so it is the identity\n * used here too. Entity and key are matched as well so a hlc collision\n * between genuinely different ops (which the UNIQUE constraint would\n * itself refuse) can never be mistaken for a re-delivery.\n */\nfunction isAlreadyRecorded(db: Db, op: Op): boolean {\n const row = db\n .prepare(\n `SELECT 1 FROM ops\n WHERE machine_id = @machineId AND hlc = @hlc\n AND entity = @entity AND key = @key AND op = @op\n LIMIT 1`,\n )\n .get({\n machineId: op.machineId,\n hlc: op.hlc,\n entity: op.entity,\n key: op.key,\n op: op.op,\n });\n return row !== undefined;\n}\n\n/**\n * Apply ONE incoming op and record it in the local `ops` table.\n *\n * The shared tail of every ingest path — segment ingest above, and the\n * `mu sync --from <peer.db>` reader in `src/sync.ts`, which is a\n * different READER over the same apply semantics. Extracted so the two\n * cannot drift: a second copy of \"advance the clock, apply, record\" is\n * how one of them silently stops advancing the clock.\n *\n * Three steps, in this order:\n * 1. `receiveHlc` BEFORE applying, so anything we mint afterwards\n * sorts above the peer's op.\n * 2. `applyOp`, which is capture-suppressed (no echo op is minted).\n * 3. Record the op locally so it survives, participates in\n * provenance, and can be re-flushed by rebuild. INSERT OR IGNORE\n * makes this idempotent via UNIQUE (machine_id, hlc) — the\n * property that lets \"re-read from zero\" be the universal repair.\n */\nexport function applyIncomingOp(db: Db, op: Op): { changed: boolean } {\n receiveHlc(db, op.hlc);\n const result = applyOp(db, op);\n db.prepare(\n `INSERT OR IGNORE INTO ops\n (hlc, machine_id, group_id, actor, intent, entity, key, op, payload, created_at)\n VALUES (@hlc, @machineId, @groupId, @actor, @intent, @entity, @key, @op, @payload, @createdAt)`,\n ).run({\n hlc: op.hlc,\n machineId: op.machineId,\n groupId: op.groupId,\n actor: op.actor ?? null,\n intent: op.intent ?? null,\n entity: op.entity,\n key: op.key,\n op: op.op,\n payload: op.payload,\n createdAt: new Date().toISOString(),\n });\n return { changed: result.changed };\n}\n\n// ─── the two halves, together ─────────────────────────────────────────\n\nexport interface SyncPassResult {\n flushed: FlushResult;\n ingested: readonly IngestResult[];\n /** True iff any peer reported a defect. */\n defective: boolean;\n}\n\n/**\n * One flush + one ingest of every discovered peer.\n *\n * The SDK seam `mu sync` calls (src/cli/sync.ts); it deliberately\n * prints nothing and starts nothing. No daemon, no watcher, no polling\n * loop that outlives the command — the anti-feature pledges are firm, and\n * mu never moves files itself: the operator owns transport.\n *\n * A no-op costing nothing when `MU_SYNC_DIR` is unset, which is the\n * normal single-machine case.\n */\nexport async function syncPass(db: Db, dir: string | null = syncDir()): Promise<SyncPassResult> {\n if (dir === null) {\n return {\n flushed: { segmentPath: null, appended: 0, total: 0, skippedLocal: 0, selfRepaired: null },\n ingested: [],\n defective: false,\n };\n }\n const flushed = await flushSegment(db, dir);\n const self = localMachineId(db);\n const ingested = discoverPeers(dir, self).map((peer) => ingestSegment(db, peer));\n // AFTER every peer, not per peer: an edge in peer A's segment may name\n // a task in peer B's, and `discoverPeers` order is `localeCompare` over\n // random UUID filenames, so \"parent first\" is a coin flip. One pass at\n // the end sees the union. See `reprojectDeferredOps`.\n reprojectDeferredOps(db);\n return {\n flushed,\n ingested,\n defective: flushed.selfRepaired !== null || ingested.some((r) => r.defects.length > 0),\n };\n}\n","// mu — parallel-track detection via union-find with diamond merge.\n//\n// Port of a parallel-tracks union-find algorithm cribbed from a\n// prior internal multi-agent runtime. The killer feature: when two goals share a prerequisite, their subgraphs\n// overlap and they collapse into ONE track, so two agents are never\n// assigned tasks that share a dependency.\n//\n// goal_a goal_b goal_a goal_b\n// \\ / \\ /\n// shared → shared (1 track)\n// | |\n// leaf leaf\n//\n// Algorithm:\n// 1. Get all open goals (tasks with no outgoing edges, not CLOSED).\n// 2. In one recursive query, compute each goal's prerequisite subgraph\n// (everything transitively reachable via reverse edges).\n// 3. Build union-find: merge goals while observing shared tasks.\n// 4. Each connected component is one Track.\n\nimport type { Db } from \"./db.js\";\nimport { listGoals, listReady, type TaskRow } from \"./tasks.js\";\n\nexport interface Track {\n /** Goal tasks (no outgoing edges) belonging to this track. */\n roots: TaskRow[];\n /** Every task id reachable as a prerequisite of any root in this track. */\n taskIds: ReadonlySet<string>;\n /** Number of READY tasks (per the SQL view) within this track's subgraph. */\n readyCount: number;\n /** True when every non-CLOSED task in the track is OPEN/parked: the\n * track holds work, but none of it is schedulable. */\n parked: boolean;\n}\n\n/**\n * Identify independent task subtrees suitable for parallel assignment\n * within a workstream. Open goals only; CLOSED goals are excluded as\n * they no longer represent work to schedule.\n *\n * Scoping: only goals belonging to `workstream` are considered.\n * Cross-workstream edges are forbidden by addTask, so a goal's\n * prerequisite subgraph is naturally workstream-internal.\n */\nexport function getParallelTracks(db: Db, workstream: string): Track[] {\n // listGoals already filters via the SQL view (status <> CLOSED),\n // but defence-in-depth: a stale db snapshot or future view tweak\n // shouldn't let closed goals leak into track count.\n const goals = listGoals(db, workstream).filter((g) => g.status !== \"CLOSED\");\n if (goals.length === 0) return [];\n\n // 2. Compute every goal's prerequisite subgraph in one traversal.\n // Running one recursive query per goal made the TUI's fast tick scale\n // with the number of goals. The goal id carried through this CTE keeps\n // the same inclusive per-goal sets in one SQLite round-trip.\n const reach = db\n .prepare(\n `WITH RECURSIVE\n active_goals(id, local_id) AS (\n SELECT g.id, g.local_id\n FROM goals g\n JOIN workstreams ws ON ws.id = g.workstream_id\n WHERE ws.name = ?\n ),\n reach(goal_id, node_id) AS (\n SELECT id, id FROM active_goals\n UNION\n SELECT r.goal_id, e.from_task_id\n FROM reach r\n JOIN task_edges e ON e.to_task_id = r.node_id\n )\n SELECT goal.local_id AS goal_id, node.local_id AS task_id,\n node.status AS status, node.substate AS substate\n FROM reach r\n JOIN tasks goal ON goal.id = r.goal_id\n JOIN tasks node ON node.id = r.node_id`,\n )\n .all(workstream) as Array<{\n goal_id: string;\n task_id: string;\n status: string;\n substate: string;\n }>;\n\n const subgraphs = new Map(goals.map((goal) => [goal.name, new Set<string>()]));\n const uf = new UnionFind(goals.map((goal) => goal.name));\n const firstGoalByTask = new Map<string, string>();\n // Substate of every non-CLOSED task reached, for the per-track `parked` flag.\n const openSubstate = new Map<string, string>();\n for (const row of reach) {\n if (row.status !== \"CLOSED\") openSubstate.set(row.task_id, row.substate);\n subgraphs.get(row.goal_id)?.add(row.task_id);\n const firstGoal = firstGoalByTask.get(row.task_id);\n if (firstGoal === undefined) firstGoalByTask.set(row.task_id, row.goal_id);\n else uf.union(firstGoal, row.goal_id);\n }\n\n // 4. Group goals + subgraph task ids by union-find root.\n const componentTaskIds = new Map<string, Set<string>>();\n const componentRoots = new Map<string, TaskRow[]>();\n for (const goal of goals) {\n const root = uf.find(goal.name);\n let bucket = componentTaskIds.get(root);\n if (!bucket) {\n bucket = new Set<string>();\n componentTaskIds.set(root, bucket);\n componentRoots.set(root, []);\n }\n componentRoots.get(root)?.push(goal);\n const sub = subgraphs.get(goal.name);\n if (sub) {\n for (const id of sub) bucket.add(id);\n }\n }\n\n // 5. Compute ready counts per track.\n const readyIds = new Set(listReady(db, workstream).map((t) => t.name));\n const tracks: Track[] = [];\n for (const [root, taskIds] of componentTaskIds) {\n const trackRoots = componentRoots.get(root) ?? [];\n let readyCount = 0;\n let openCount = 0;\n let parkedCount = 0;\n for (const id of taskIds) {\n if (readyIds.has(id)) readyCount++;\n const sub = openSubstate.get(id);\n if (sub === undefined) continue;\n openCount++;\n if (sub === \"parked\") parkedCount++;\n }\n const parked = openCount > 0 && parkedCount === openCount;\n tracks.push({ roots: trackRoots, taskIds, readyCount, parked });\n }\n\n // Stable order: by primary root's localId so output is deterministic.\n tracks.sort((a, b) => {\n const an = a.roots[0]?.name ?? \"\";\n const bn = b.roots[0]?.name ?? \"\";\n return an.localeCompare(bn);\n });\n return tracks;\n}\n\nclass UnionFind {\n private readonly parent = new Map<string, string>();\n private readonly rank = new Map<string, number>();\n\n constructor(items: readonly string[]) {\n for (const item of items) {\n this.parent.set(item, item);\n this.rank.set(item, 0);\n }\n }\n\n find(x: string): string {\n let root = x;\n while (true) {\n const next = this.parent.get(root);\n if (next === undefined || next === root) break;\n root = next;\n }\n // Path compression.\n let curr = x;\n while (curr !== root) {\n const next = this.parent.get(curr);\n if (next === undefined) break;\n this.parent.set(curr, root);\n curr = next;\n }\n return root;\n }\n\n union(a: string, b: string): void {\n const rootA = this.find(a);\n const rootB = this.find(b);\n if (rootA === rootB) return;\n const rankA = this.rank.get(rootA) ?? 0;\n const rankB = this.rank.get(rootB) ?? 0;\n if (rankA < rankB) {\n this.parent.set(rootA, rootB);\n } else if (rankA > rankB) {\n this.parent.set(rootB, rootA);\n } else {\n this.parent.set(rootB, rootA);\n this.rank.set(rootA, rankA + 1);\n }\n }\n}\n","// SDK seam for `mu state` (static) and the interactive TUI.\n//\n// Both renderers (the legacy cli-table3-based static fallback in\n// src/cli/state.ts and the new ink-based TUI in src/cli/tui/) consume\n// the same WorkstreamSnapshot. Pure data + a few small derivation\n// helpers — no rendering. See design_sdk_seam in workstream `tui` for\n// the rationale (`mu task notes design_sdk_seam -w tui`).\n\nimport type { RuntimeState } from \"./agent-state.js\";\nimport { type LiveAgent, type LiveAgentsView, listLiveAgents } from \"./agents.js\";\nimport type { Db } from \"./db.js\";\nimport { type DoctorSummary, loadDoctorSummary } from \"./doctor-summary.js\";\nimport { GLYPH } from \"./glyphs.js\";\nimport { type LogRow, listLogs } from \"./logs.js\";\nimport { activeMux } from \"./mux.js\";\nimport {\n listBlocked,\n listInProgress,\n listReady,\n listRecentClosed,\n listTasks,\n listTasksByOwner,\n type TaskRow,\n} from \"./tasks.js\";\nimport { getParallelTracks, type Track } from \"./tracks.js\";\nimport { type CommitSummary, detectBackend, type VcsBackendName } from \"./vcs.js\";\nimport {\n decorateWithDirty,\n decorateWithStaleness,\n listWorkspaceOrphans,\n listWorkspaces,\n type WorkspaceOrphan,\n type WorkspaceRow,\n} from \"./workspace.js\";\n\n// ─── WorkstreamSnapshot ───────────────────────────────────────────\n\nexport interface RemoteWorker {\n taskName: string;\n host: string;\n path: string;\n}\n\nexport interface RemoteDispatch extends RemoteWorker {\n agentName: string;\n baseSha: string;\n}\n\nexport interface WorkstreamSnapshot {\n workstreamName: string;\n view: LiveAgentsView;\n tracks: Track[];\n ready: TaskRow[];\n inProgress: TaskRow[];\n blocked: TaskRow[];\n recentClosed: TaskRow[];\n /** OPEN/parked tasks in the workstream. The ready card names them\n * when nothing is ready, since parked work is otherwise invisible there. */\n parkedCount: number;\n /** OPEN/triage tasks: proposals (review findings) awaiting accept,\n * reject, or duplicate. Out of `ready`, so listed on their own. */\n triage: TaskRow[];\n /** Every task in the workstream, any status (a COUNT, not the rows). */\n taskCount: number;\n /** Populated only when callers explicitly pass `withAllTasks: true`.\n * The TUI dashboard fast tick leaves this empty and the all-tasks\n * popup reads its exhaustive list directly from SQLite while open. */\n allTasks: TaskRow[];\n workspaces: WorkspaceRow[];\n workspaceOrphans: WorkspaceOrphan[];\n recent: LogRow[];\n /** Last N commits from the project root (process.cwd()), populated\n * when `loadWorkstreamSnapshot` is called with withRecentCommits.\n * This is intentionally NOT a per-agent workspace log. */\n recentCommits: CommitSummary[];\n /** Backend that produced recentCommits. Null when recent commits were\n * not requested or no VCS backend was detected. */\n commitsBackend?: VcsBackendName | null;\n /** Populated when `loadWorkstreamSnapshot` is called with\n * `withDoctor: true`. Used by the TUI's slot-9 Doctor card to\n * render a glanceable health badge on the dashboard\n * (feat_card_9_doctor, workstream `tui-impl`). The static `mu\n * state` card and `mu doctor` itself don't consume it — they\n * read the textual doctor card directly. Null when not requested. */\n doctor: DoctorSummary | null;\n}\n\nexport interface LoadWorkstreamSnapshotOptions {\n /** Recent-events cap (default 200). */\n eventLimit?: number;\n /** When true, slow snapshot loading also populates `WorkspaceRow.dirty`\n * via decorateWithDirty (one `git status --porcelain` shellout per row,\n * capped at DECORATE_CONCURRENCY). The TUI caches this slow-tier value\n * and merges it into every fast SQL tick. */\n withDirty?: boolean;\n /** When true, slow snapshot loading also populates\n * `WorkstreamSnapshot.doctor` via `loadDoctorSummary`. The summary is\n * cheap SQL, but it reports tmux/workspace drift from slow-tier fields,\n * so the TUI refreshes it with the subprocess tier. */\n withDoctor?: boolean;\n /** Optional full task list for the TUI all-tasks popup. */\n withAllTasks?: true;\n /** Optional recent-project-commits slice for the TUI Commits card /\n * popup. Uses process.cwd() as the project root on purpose: the TUI\n * is launched from the project checkout, while worker workspaces live\n * elsewhere under the mu state dir. */\n withRecentCommits?: { limit: number };\n}\n\nexport interface WorkstreamSnapshotSlowFields {\n view: LiveAgentsView;\n /** Workspace rows decorated with slow-tier VCS observations\n * (`commitsBehindMain`, and `dirty` when requested). */\n workspaces: WorkspaceRow[];\n recentCommits: CommitSummary[];\n commitsBackend?: VcsBackendName | null;\n doctor: DoctorSummary | null;\n}\n\nfunction remoteNoteRows(\n db: Db,\n workstream: string,\n taskName?: string,\n): Array<{ task_name: string; content: string }> {\n const filter = taskName === undefined ? \"\" : \" AND t.local_id = ?\";\n const params: string[] = taskName === undefined ? [workstream] : [workstream, taskName];\n return db\n .prepare(\n `SELECT t.local_id AS task_name, n.content AS content\n FROM task_notes n\n JOIN tasks t ON t.id = n.task_id\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE ws.name = ?${filter}\n AND (n.content LIKE '%REMOTE: %' OR n.content LIKE '%REMOTE_BASE: %')\n ORDER BY n.id`,\n )\n .all(...params) as Array<{\n task_name: string;\n content: string;\n }>;\n}\n\nexport function listRemoteWorkers(db: Db, workstream: string): RemoteWorker[] {\n const remoteWorkers: RemoteWorker[] = [];\n for (const row of remoteNoteRows(db, workstream)) {\n for (const line of row.content.split(/\\r?\\n/)) {\n const match = /^REMOTE:\\s+([^:\\s]+):(\\S+)\\s*$/.exec(line);\n const host = match?.[1];\n const path = match?.[2];\n if (host !== undefined && path !== undefined) {\n remoteWorkers.push({ taskName: row.task_name, host, path });\n }\n }\n }\n return remoteWorkers;\n}\n\nexport function findRemoteDispatch(\n db: Db,\n workstream: string,\n taskName: string,\n agentName: string,\n): RemoteDispatch | undefined {\n let dispatch: RemoteDispatch | undefined;\n for (const row of remoteNoteRows(db, workstream, taskName)) {\n let location: RemoteWorker | undefined;\n let baseSha: string | undefined;\n for (const line of row.content.split(/\\r?\\n/)) {\n const remote = /^REMOTE:\\s+([^:\\s]+):(\\S+)\\s*$/.exec(line);\n if (remote?.[1] !== undefined && remote[2] !== undefined) {\n location = { taskName, host: remote[1], path: remote[2] };\n }\n const base = /^REMOTE_BASE:\\s+([^:\\s]+):(\\S+)\\s*$/.exec(line);\n if (base?.[1] === agentName && base[2] !== undefined) baseSha = base[2];\n }\n if (location !== undefined && baseSha !== undefined) {\n dispatch = { ...location, agentName, baseSha };\n }\n }\n return dispatch;\n}\n\n/**\n * Fast TUI/state snapshot tier: pure SQLite reads only. Subprocess-backed\n * fields are intentionally empty placeholders so callers can merge the last\n * slow-tier values without blocking a 1s render tick on tmux or VCS probes.\n */\nexport async function loadWorkstreamSnapshotFast(\n db: Db,\n workstream: string,\n opts: LoadWorkstreamSnapshotOptions = {},\n): Promise<WorkstreamSnapshot> {\n const eventLimit = opts.eventLimit ?? 200;\n return {\n workstreamName: workstream,\n view: emptyLiveAgentsView(),\n tracks: getParallelTracks(db, workstream),\n ready: listReady(db, workstream).sort(byRoiDesc),\n inProgress: listInProgress(db, workstream),\n blocked: listBlocked(db, workstream),\n recentClosed: listRecentClosed(db, workstream),\n parkedCount: listTasks(db, workstream, { status: \"OPEN\", substate: \"parked\" }).length,\n triage: listTasks(db, workstream, { status: \"OPEN\", substate: \"triage\" }),\n taskCount: (\n db\n .prepare(\n \"SELECT count(*) AS n FROM tasks t JOIN workstreams w ON w.id = t.workstream_id WHERE w.name = ?\",\n )\n .get(workstream) as { n: number }\n ).n,\n allTasks: opts.withAllTasks === true ? listTasks(db, workstream) : [],\n workspaces: listWorkspaces(db, workstream),\n workspaceOrphans: listWorkspaceOrphans(db, workstream),\n // v2-retire-log-shim: this used to filter `kind: \"event\"` — the\n // prose breadcrumbs. Those are gone, so the Recent card reads every\n // op for the workstream. Payloads are raw JSON for captured ops\n // until v2-log-verb renders them from `intent`; the card stays\n // populated in the meantime rather than going silently empty.\n recent: listLogs(db, { workstream, limit: eventLimit }),\n recentCommits: [],\n commitsBackend: null,\n doctor: null,\n };\n}\n\n/**\n * Slow snapshot tier: fields backed by tmux / VCS subprocess probes (plus\n * doctor, which reports over those slow-tier observations). Returns only the\n * fields the fast snapshot deliberately leaves empty or undecorated.\n *\n * The slow snapshot tier runs full reconciliation: missing panes are\n * reaped, while mid-spawn placeholders remain protected by the prune\n * loop's pending-pane guard.\n */\nexport async function loadWorkstreamSnapshotSlow(\n db: Db,\n workstream: string,\n opts: LoadWorkstreamSnapshotOptions = {},\n baseSnapshot?: WorkstreamSnapshot,\n): Promise<WorkstreamSnapshotSlowFields> {\n const view = await listLiveAgents(db, { workstream });\n // commitsBehindMain is a VCS subprocess probe, so it belongs to this\n // tier; the fast tier's listWorkspaces rows never carry it.\n let workspaces = await decorateWithStaleness(listWorkspaces(db, workstream));\n if (opts.withDirty === true) workspaces = await decorateWithDirty(workspaces);\n const commits = await loadRecentCommits(opts.withRecentCommits);\n const slow: WorkstreamSnapshotSlowFields = {\n view,\n workspaces,\n recentCommits: commits.items,\n commitsBackend: commits.backend,\n doctor: null,\n };\n if (opts.withDoctor === true) {\n const mux = await activeMux();\n slow.doctor = loadDoctorSummary(\n db,\n mergeSnapshotFastSlow(baseSnapshot ?? minimalSnapshot(workstream), slow),\n mux.paneStatus === undefined ? \"murmur\" : \"herdr\",\n );\n }\n return slow;\n}\n\n/** Merge the latest slow-tier subprocess observations into a fresh fast tier. */\nexport function mergeSnapshotFastSlow(\n fast: WorkstreamSnapshot,\n slow: WorkstreamSnapshotSlowFields | null,\n): WorkstreamSnapshot {\n if (slow === null) return fast;\n return {\n ...fast,\n view: slow.view,\n workspaces: mergeWorkspaceSlowFields(fast.workspaces, slow.workspaces),\n recentCommits: slow.recentCommits,\n commitsBackend: slow.commitsBackend ?? null,\n doctor: slow.doctor,\n };\n}\n\n/**\n * Back-compat wrapper for non-TUI callers: return the historical union shape\n * by composing the new fast SQL tier with one slow subprocess tier.\n */\nexport async function loadWorkstreamSnapshot(\n db: Db,\n workstream: string,\n opts: LoadWorkstreamSnapshotOptions = {},\n): Promise<WorkstreamSnapshot> {\n const fast = await loadWorkstreamSnapshotFast(db, workstream, opts);\n const slow = await loadWorkstreamSnapshotSlow(db, workstream, opts, fast);\n return mergeSnapshotFastSlow(fast, slow);\n}\n\nfunction emptyLiveAgentsView(): LiveAgentsView {\n return {\n agents: [],\n orphans: [],\n report: { prunedGhosts: 0, orphans: [], mode: \"full\" },\n };\n}\n\nfunction minimalSnapshot(workstream: string): WorkstreamSnapshot {\n return {\n workstreamName: workstream,\n view: emptyLiveAgentsView(),\n tracks: [],\n ready: [],\n inProgress: [],\n blocked: [],\n recentClosed: [],\n parkedCount: 0,\n triage: [],\n taskCount: 0,\n allTasks: [],\n workspaces: [],\n workspaceOrphans: [],\n recent: [],\n recentCommits: [],\n commitsBackend: null,\n doctor: null,\n };\n}\n\nfunction mergeWorkspaceSlowFields(\n fastRows: readonly WorkspaceRow[],\n slowRows: readonly WorkspaceRow[],\n): WorkspaceRow[] {\n const slowByAgent = new Map(slowRows.map((row) => [row.agentName, row]));\n return fastRows.map((fast) => {\n const slow = slowByAgent.get(fast.agentName);\n if (slow === undefined) return fast;\n return {\n ...fast,\n commitsBehindMain: slow.commitsBehindMain ?? fast.commitsBehindMain,\n dirty: slow.dirty,\n };\n });\n}\n\nasync function loadRecentCommits(\n opt: LoadWorkstreamSnapshotOptions[\"withRecentCommits\"],\n): Promise<{ backend: VcsBackendName | null; items: CommitSummary[] }> {\n if (opt === undefined) return { backend: null, items: [] };\n const projectRoot = process.cwd();\n const backend = await detectBackend(projectRoot);\n if (backend.name === \"none\") return { backend: null, items: [] };\n return { backend: backend.name, items: await backend.recentCommits(projectRoot, opt.limit) };\n}\n\n// ─── ROI helpers ───────────────────────────────────────────────────\n\n/**\n * ROI tiers used to colour task rows. Pure: returns the bucket name; the\n * consumer maps bucket → picocolors function (or ink text colour).\n * Magic numbers (≥100 high, ≥50 mid) lifted from the previous HUD impl.\n */\nexport type RoiBucket = \"high\" | \"mid\" | \"low\" | \"infinite\";\n\nexport function roiBucket(impact: number, effortDays: number): RoiBucket {\n const r = effortDays > 0 ? impact / effortDays : Number.POSITIVE_INFINITY;\n if (!Number.isFinite(r)) return \"infinite\";\n if (r >= 100) return \"high\";\n if (r >= 50) return \"mid\";\n return \"low\";\n}\n\n/** ROI sort comparator (descending). Used by loadWorkstreamSnapshot.ready. */\nfunction byRoiDesc(a: TaskRow, b: TaskRow): number {\n const ra = a.effortDays > 0 ? a.impact / a.effortDays : Number.POSITIVE_INFINITY;\n const rb = b.effortDays > 0 ? b.impact / b.effortDays : Number.POSITIVE_INFINITY;\n if (rb !== ra) return rb - ra;\n if (a.effortDays !== b.effortDays) return a.effortDays - b.effortDays;\n return a.name.localeCompare(b.name);\n}\n\n// ─── Agent helpers ─────────────────────────────────────────────────\n\n/** Histogram of agents by runtime state. Pure derivation (no colour render). */\nexport function agentStateHistogram(\n agents: readonly LiveAgent[],\n): ReadonlyMap<RuntimeState, number> {\n const out = new Map<RuntimeState, number>();\n for (const a of agents) {\n out.set(a.state, (out.get(a.state) ?? 0) + 1);\n }\n return out;\n}\n\n// ─── Task helpers ──────────────────────────────────────────────────\n\nexport interface OwnedTasksSummary {\n /** Display token: \"—\" (none), task id (one), or GLYPH.multi + count (many). */\n bit: string;\n /** Underlying count for callers that want their own format. */\n count: number;\n /** The single owned task's local id, when count===1. */\n onlyTaskId?: string;\n}\n\n/**\n * Per-agent task summary: condensed display token + raw count. Used by\n * both the static Agents table and the ink Agents card. Pure on the\n * input rows — caller (e.g. loadWorkstreamSnapshot consumer) does the\n * listTasksByOwner query upstream and feeds the rows in.\n */\nexport function summarizeOwnedTasks(owned: readonly TaskRow[]): OwnedTasksSummary {\n const count = owned.length;\n if (count === 0) return { bit: \"—\", count: 0 };\n if (count === 1) {\n const only = owned[0];\n if (!only) return { bit: \"—\", count: 0 };\n return { bit: only.name, count: 1, onlyTaskId: only.name };\n }\n return { bit: `${GLYPH.multi}${count}`, count };\n}\n\n// Re-export for convenience: callers wanting to combine listTasksByOwner\n// with summarizeOwnedTasks in one import.\nexport { listTasksByOwner };\n","// mu — the sync seam: peer status, the ambient hook, and the two power\n// tools behind `mu sync`.\n//\n// WHAT LIVES HERE AND WHAT DOES NOT\n// ---------------------------------\n// `src/segments.ts` owns the MECHANISM (flush / ingest / watermarks /\n// the four robustness layers). This file owns the OPERATOR-FACING\n// questions that mechanism cannot answer on its own:\n//\n// - who are my peers, how far behind am I, when did I last hear from\n// them (`peerStatuses`)\n// - the AMBIENT hook every mu invocation runs (`ambientIngest` /\n// `ambientFlush`)\n// - a DIFFERENT READER: a peer's `ops` table read straight out of a\n// copied / sshfs-mounted `mu.db` (`ingestFromDb`)\n// - the universal repair: reset a watermark and re-read from zero\n// (`repairPeer`)\n//\n// AMBIENT, NOT A DAEMON\n// ---------------------\n// There is no watcher, no background process, and no polling loop that\n// outlives the command. Sync happens because you already run `mu`\n// constantly: every invocation ingests before the verb body and flushes\n// after it. With `MU_SYNC_DIR` unset both halves are a single `if` that\n// reads one env var, so the single-machine case pays nothing. With 2-5\n// peers it is a handful of `stat`/`read` calls on small append-only\n// files.\n//\n// The TUI is the one long-lived surface, and it hangs the same pass off\n// its SLOW tick (10s), never the 1s fast tick — sync is filesystem work\n// and has no business on a repaint cadence.\n//\n// SYNC MUST NEVER FAIL A COMMAND\n// ------------------------------\n// A truncated segment, an unreadable sync dir, a full disk: all of them\n// warn and return. `mu task add` has to work when sync is broken,\n// because the alternative is a tool that stops working when a folder you\n// do not control misbehaves. Every ambient entry point here is\n// try/catch-total by construction (see `ambientIngest`).\n//\n// TRANSPORT STAYS THE OPERATOR'S\n// ------------------------------\n// mu reads and writes FILES. It does not shell out to ssh/scp/rsync, and\n// it will not grow `--push <host>`: that is a remote backend wearing a\n// small hat (ssh config, jump hosts, ProxyCommand, ports, identity\n// files, interactive prompts inside a TUI tick, network-vs-auth error\n// mapping), and it violates the standing ROADMAP pledge that the user\n// owns transport. Instead, when a peer looks stale we PRINT a\n// copy-pasteable rsync line through the ordinary NextStep convention and\n// let the operator paste it, alias it, or run Syncthing and never see\n// it.\n\nimport { existsSync, statSync } from \"node:fs\";\nimport Database from \"better-sqlite3\";\nimport { type Op, reprojectDeferredOps } from \"./apply.js\";\nimport { type Db, SYNCED_ENTITIES } from \"./db.js\";\nimport { isLegacyLogOnlyIntent } from \"./legacy-ops.js\";\nimport type { NextStep } from \"./output.js\";\nimport {\n applyIncomingOp,\n discoverPeers,\n type FlushResult,\n flushSegment,\n getWatermark,\n type IngestResult,\n ingestSegment,\n localMachineId,\n peerWatermarkKey,\n resetWatermark,\n type SegmentDefect,\n segmentLineCount,\n syncDir,\n} from \"./segments.js\";\n\n/** No discovered peer matches the operator's `--repair <peer>` ref.\n * Exit 3 (not found), like every other resolve-time miss. */\nexport class SyncPeerNotFoundError extends Error {\n override readonly name = \"SyncPeerNotFoundError\";\n constructor(\n readonly ref: string,\n readonly known: readonly string[],\n ) {\n super(\n `no peer matches ${JSON.stringify(ref)}${\n known.length === 0 ? \" (no peers discovered)\" : ` (known peers: ${known.join(\", \")})`\n }`,\n );\n }\n}\n\n/** A `--repair <peer>` prefix matched several peers. A conflict (exit 4),\n * never a guess — the same rule `mu undo <group>` follows for\n * abbreviated group ids. */\nexport class SyncPeerRefAmbiguousError extends Error {\n override readonly name = \"SyncPeerRefAmbiguousError\";\n constructor(\n readonly ref: string,\n readonly candidates: readonly string[],\n ) {\n super(\n `peer ref ${JSON.stringify(ref)} matches ${candidates.length} peers (${candidates.join(\", \")}); pass more characters`,\n );\n }\n}\n\n/** `mu sync --from <path>` pointed at a file that is not there. Exit 3. */\nexport class SyncSourceNotFoundError extends Error {\n override readonly name = \"SyncSourceNotFoundError\";\n constructor(readonly path: string) {\n super(`--from: no such file: ${path}`);\n }\n}\n\n/** A peer whose segment has not moved in this long is reported STALE.\n * A fixed constant, not an env var: it is a display threshold, and mu's\n * whole cluster configuration is deliberately ONE env var. */\nexport const PEER_STALE_MS = 24 * 60 * 60 * 1000;\n\n/** How many characters of a `machine_id` uuid we show. Peers are known\n * only by uuid — `machine_identity.hostname` is machine-LOCAL and never\n * ships, so mu genuinely cannot render a peer's hostname without\n * inventing a membership file. A short prefix is the honest display,\n * and every verb that takes one accepts any unique prefix (the\n * affordance git gives for shas). */\nexport const PEER_SHORT_LEN = 8;\n\nexport interface PeerStatus {\n machineId: string;\n /** First `PEER_SHORT_LEN` chars of the machine id, for display. */\n short: string;\n path: string;\n /** True for a Syncthing-style `*.sync-conflict-*.jsonl` copy. */\n conflictCopy: boolean;\n /** `sync_peers` key of this file's watermark (see `PeerSegment`).\n * `peerStatuses` always sets it. Optional so SDK callers that build\n * the pre-watermarkKey shape still compile; absent means the machine\n * id (`peerWatermarkKey`). */\n watermarkKey?: string;\n /** Lines of this segment file already applied. */\n watermark: number;\n /** GOOD lines currently in the segment (a defect stops the count). */\n total: number;\n /** `total - watermark`: how much of what we HOLD is not yet applied.\n * Non-zero after a defect stopped an ingest short, or mid-transfer. */\n behind: number;\n /** Segment mtime in epoch ms — when transport last delivered. */\n lastSeenMs: number | null;\n /** Age of the segment file, ms. Null when it does not exist. */\n ageMs: number | null;\n stale: boolean;\n}\n\n/** Peer table for the sync dir, newest contact first. Pure read: it\n * neither flushes nor ingests, so `mu sync` can call it after its own\n * pass and the TUI could call it on a tick. */\nexport function peerStatuses(db: Db, dir: string): PeerStatus[] {\n const self = localMachineId(db);\n const now = Date.now();\n const rows = discoverPeers(dir, self).map((peer) => {\n let lastSeenMs: number | null = null;\n try {\n lastSeenMs = statSync(peer.path).mtimeMs;\n } catch {\n lastSeenMs = null;\n }\n const total = segmentLineCount(peer.path);\n const watermarkKey = peerWatermarkKey(peer);\n const watermark = getWatermark(db, watermarkKey);\n const ageMs = lastSeenMs === null ? null : Math.max(0, now - lastSeenMs);\n return {\n machineId: peer.machineId,\n short: peer.machineId.slice(0, PEER_SHORT_LEN),\n path: peer.path,\n conflictCopy: peer.conflictCopy,\n watermarkKey,\n watermark,\n total,\n behind: Math.max(0, total - watermark),\n lastSeenMs,\n ageMs,\n stale: ageMs === null || ageMs > PEER_STALE_MS,\n };\n });\n return rows.sort((a, b) => (b.lastSeenMs ?? 0) - (a.lastSeenMs ?? 0));\n}\n\n/** Resolve an operator-typed peer reference (full machine id, or any\n * unique prefix) against the discovered peers. Ambiguity is a\n * UsageError, never a guess — repairing the wrong peer would re-read a\n * whole segment for nothing and confuse the report.\n *\n * A ref names a MACHINE, so a segment and its conflict copies (same\n * machine id) count as one match; the peer's own segment is returned\n * when present. Counting them separately made the short id `mu sync`\n * prints unresolvable. */\nexport function resolvePeerRef(peers: readonly PeerStatus[], ref: string): PeerStatus {\n const matches = peers.filter((p) => p.machineId.startsWith(ref));\n const exact = matches.filter((p) => p.machineId === ref);\n const candidates = exact.length > 0 ? exact : matches;\n const machines = [...new Set(candidates.map((p) => p.machineId))];\n const first = candidates.find((p) => !p.conflictCopy) ?? candidates[0];\n if (first === undefined) {\n throw new SyncPeerNotFoundError(\n ref,\n peers.map((p) => p.short),\n );\n }\n if (machines.length > 1) {\n throw new SyncPeerRefAmbiguousError(\n ref,\n machines.map((m) => m.slice(0, PEER_SHORT_LEN)),\n );\n }\n return first;\n}\n\n/** Reset the watermark of every segment file a peer has (its own and\n * any conflict copies) so the next ingest re-reads them from zero. Safe\n * by construction: apply is idempotent and `ops` dedupes on\n * `UNIQUE (machine_id, hlc)`. */\nexport function repairPeer(db: Db, ref: string, dir: string): PeerStatus {\n const peers = peerStatuses(db, dir);\n const peer = resolvePeerRef(peers, ref);\n for (const p of peers) {\n if (p.machineId === peer.machineId) resetWatermark(db, peerWatermarkKey(p));\n }\n return peer;\n}\n\n// ─── the ambient hook ─────────────────────────────────────────────────\n\n/** Is sync configured at all? THE single `if` the no-sync case pays.\n * Callers check this before awaiting anything, so an unconfigured\n * machine allocates no promise and touches no filesystem. */\nexport function syncEnabled(): boolean {\n return syncDir() !== null;\n}\n\nexport interface AmbientResult {\n /** Peers whose segments were read. Empty when sync is off. */\n ingested: readonly IngestResult[];\n /** Non-fatal problems, already warned about. */\n warnings: readonly string[];\n}\n\nexport interface AmbientOptions {\n /** Suppress the stderr warnings. Set by the TUI, which owns the\n * alternate screen — a stray write there paints garbage over the\n * dashboard. The warnings are still returned, and the Doctor card\n * is where a TUI operator learns about a broken sync dir. */\n quiet?: boolean;\n}\n\n/** Format one non-fatal sync problem for stderr. Prefixed so it is\n * unmistakably mu's own aside and not the verb's output. */\nfunction warn(message: string, opts?: AmbientOptions): void {\n if (opts?.quiet === true) return;\n // Deliberately stderr: stdout is the verb's contract (and may be JSON).\n process.stderr.write(`mu: sync: ${message}\\n`);\n}\n\nfunction describeDefects(peerShort: string, defects: readonly SegmentDefect[]): string {\n const first = defects[0];\n const detail = first === undefined ? \"unknown defect\" : `${first.kind} at line ${first.line}`;\n const more = defects.length > 1 ? ` (+${defects.length - 1} more)` : \"\";\n // `--repair` only resets the watermark, so it is advice ONLY for a\n // defect a re-read can get past (a torn write mid-transfer). A refused\n // line is deterministic: re-reading hits the identical line and the\n // hint would be self-referentially useless, which is exactly how the\n // original incident's operator was sent in a circle. A re-delivered\n // op is deterministic for the same reason. So is a manifest mismatch:\n // the file itself is short, re-reading it changes nothing, and only a\n // fresh copy from the peer clears it.\n const skipped = new Set([\"entity-not-synced\", \"duplicate-op\"]);\n const fileWide = new Set([\"manifest-mismatch\"]);\n let hint: string;\n if (defects.every((d) => skipped.has(d.kind))) {\n hint = \" — skipped; the rest of the segment applied\";\n } else if (defects.every((d) => skipped.has(d.kind) || fileWide.has(d.kind))) {\n hint = \" — the file is shorter than its manifest; copy it from the peer again\";\n } else {\n hint = ` — re-read with \\`mu sync --repair ${peerShort}\\``;\n }\n return `peer ${peerShort}: ${detail}${more}${hint}`;\n}\n\n/**\n * INGEST half of the ambient hook: pull every peer segment before the\n * verb body runs, so the command sees the freshest state the filesystem\n * can offer.\n *\n * Total: no input can make this throw. A per-peer failure is isolated so\n * one broken segment cannot hide the others.\n */\nexport async function ambientIngest(db: Db, opts?: AmbientOptions): Promise<AmbientResult> {\n const dir = syncDir();\n if (dir === null) return { ingested: [], warnings: [] };\n const warnings: string[] = [];\n const ingested: IngestResult[] = [];\n try {\n const self = localMachineId(db);\n for (const peer of discoverPeers(dir, self)) {\n try {\n const result = ingestSegment(db, peer);\n ingested.push(result);\n if (result.defects.length > 0) {\n const message = describeDefects(peer.machineId.slice(0, PEER_SHORT_LEN), result.defects);\n warnings.push(message);\n warn(message, opts);\n }\n } catch (err) {\n const message = `peer ${peer.machineId.slice(0, PEER_SHORT_LEN)}: ${err instanceof Error ? err.message : String(err)}`;\n warnings.push(message);\n warn(message, opts);\n }\n }\n // Every peer is in; now project anything that arrived before its\n // parent task did. Deliberately outside the per-peer loop, and\n // deliberately inside the outer try: a repair failure must not fail\n // the verb any more than a torn segment does.\n reprojectDeferredOps(db);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n warnings.push(message);\n warn(message, opts);\n }\n // `await` nothing above: ingest is synchronous better-sqlite3 work.\n // The async signature exists so the caller's seam is uniform with\n // ambientFlush (which takes a file lock).\n return { ingested, warnings };\n}\n\n/**\n * FLUSH half of the ambient hook: append this invocation's own ops to\n * this machine's segment AFTER the verb body has committed them.\n *\n * Order matters and is the whole reason the hook is split in two: a\n * flush before the body would leave the ops the operator just wrote\n * sitting unflushed until the NEXT invocation, so `mu task add` on the\n * laptop followed by `mu sync` on the devserver would show nothing —\n * exactly the no-hands claim, broken.\n *\n * Runs under the cross-process file lock inside `flushSegment`, so two\n * concurrent mu processes cannot interleave partial lines.\n */\nexport async function ambientFlush(db: Db, opts?: AmbientOptions): Promise<FlushResult | null> {\n const dir = syncDir();\n if (dir === null) return null;\n try {\n const result = await flushSegment(db, dir);\n if (result.selfRepaired !== null) {\n const d = result.selfRepaired;\n warn(\n `own segment truncated back to its last good line: ${d.kind} at line ${d.line} (${d.detail}) — regenerated from the ops table`,\n opts,\n );\n }\n return result;\n } catch (err) {\n warn(err instanceof Error ? err.message : String(err), opts);\n return null;\n }\n}\n\n/**\n * Both halves in one call, for a caller that has no before/after seam to\n * straddle — today the TUI's SLOW tick (10s; never the 1s fast tick,\n * which is a repaint cadence and has no business doing filesystem work).\n *\n * Ingest first, then flush: the same order the CLI hook uses, so a\n * long-lived TUI converges on exactly the same schedule as a shell that\n * runs one verb every ten seconds.\n */\nexport async function ambientSyncPass(\n db: Db,\n opts?: AmbientOptions,\n): Promise<{ ingested: readonly IngestResult[]; flushed: FlushResult | null }> {\n if (!syncEnabled()) return { ingested: [], flushed: null };\n const { ingested } = await ambientIngest(db, opts);\n const flushed = await ambientFlush(db, opts);\n return { ingested, flushed };\n}\n\n// ─── `mu sync --from <peer.db>`: a different READER ───────────────────\n\nexport interface IngestFromDbResult {\n path: string;\n /** Ops read out of the peer's `ops` table (already filtered). */\n read: number;\n /** Ops that changed a row here. */\n changed: number;\n /** Ops skipped because their entity is machine-local. */\n skippedLocal: number;\n}\n\n/**\n * Ingest straight from a peer's `mu.db`, reading its `ops` table with\n * SQLite instead of parsing a JSONL segment.\n *\n * WHY THIS EARNS A FLAG when `MU_SYNC_DIR=/mnt/whatever mu state` covers\n * the one-off-directory case: it is a DIFFERENT READER, and nothing\n * about an env var can express it. The file you have is a database, not\n * a segment — because you scp'd it, or because you have the devserver's\n * state dir on sshfs and would rather read the real thing than wait for\n * a flush.\n *\n * Opened `readonly` so mu cannot write to a file it does not own, and\n * `fileMustExist` so a typo'd path is an error rather than a freshly\n * created empty DB.\n *\n * Filters exactly as flush does — `SYNCED_ENTITIES` only, and never the\n * peer's copy of OUR ops (we already have those; `UNIQUE (machine_id,\n * hlc)` would dedupe them anyway, but not reading them is cheaper and\n * keeps the reported count honest). Ops the peer itself ingested from a\n * THIRD machine are read, which is how transitive convergence falls out\n * for free.\n *\n * Watermarks are deliberately untouched: a watermark counts LINES of a\n * segment, and a DB has no such coordinate. The next segment ingest for\n * that peer re-reads from wherever it was, applies the same ops again,\n * and changes nothing — idempotence is what makes leaving it alone safe.\n */\nexport function ingestFromDb(db: Db, path: string): IngestFromDbResult {\n if (!existsSync(path)) throw new SyncSourceNotFoundError(path);\n const self = localMachineId(db);\n const source = new Database(path, { readonly: true, fileMustExist: true });\n try {\n const rows = source\n .prepare(\n `SELECT hlc, machine_id, group_id, actor, intent, entity, key, op, payload\n FROM ops\n WHERE machine_id != ?\n ORDER BY hlc`,\n )\n .all(self) as Array<{\n hlc: string;\n machine_id: string;\n group_id: string;\n actor: string | null;\n intent: string | null;\n entity: string;\n key: string;\n op: string;\n payload: string;\n }>;\n\n const synced = new Set<string>(SYNCED_ENTITIES);\n let read = 0;\n let changed = 0;\n let skippedLocal = 0;\n const run = db.transaction(() => {\n for (const row of rows) {\n if (!synced.has(row.entity) || isLegacyLogOnlyIntent(row.intent)) {\n skippedLocal += 1;\n continue;\n }\n const op: Op = {\n hlc: row.hlc,\n machineId: row.machine_id,\n groupId: row.group_id,\n actor: row.actor,\n intent: row.intent,\n entity: row.entity,\n key: row.key,\n op: row.op === \"del\" ? \"del\" : \"put\",\n payload: row.payload,\n };\n const result = applyIncomingOp(db, op);\n read += 1;\n if (result.changed) changed += 1;\n }\n });\n run.immediate();\n // Same out-of-order repair the segment path runs: a peer's `ops`\n // table can hold an edge whose task op we only got from a THIRD\n // machine, in either order.\n changed += reprojectDeferredOps(db);\n return { path, read, changed, skippedLocal };\n } finally {\n try {\n source.close();\n } catch {\n // best effort\n }\n }\n}\n\n// ─── transport hints ──────────────────────────────────────────────────\n\n/**\n * Copy-pasteable transport for a stale peer, via the ordinary NextStep\n * convention. This is what mu does INSTEAD of moving bytes itself.\n *\n * `<host>` is a literal placeholder on purpose: mu has no host list and\n * is not growing one (a peer list would be a config file with extra\n * steps, and one that must be kept consistent on every machine — the\n * very drift it looks like it solves).\n */\nexport function transportNextSteps(dir: string, peers: readonly PeerStatus[]): NextStep[] {\n const stale = peers.filter((p) => p.stale);\n if (stale.length === 0) return [];\n return [\n {\n intent: `Pull fresh segments (${stale.length} stale peer${stale.length === 1 ? \"\" : \"s\"})`,\n command: `rsync -av <host>:${dir}/ ${dir}/`,\n },\n {\n intent: \"Or stop doing it by hand\",\n command: `# share ${dir} with Syncthing on every machine`,\n },\n ];\n}\n","// mu — undo: emit INVERSE OPS for one group.\n//\n// THE CHANGE IN SEMANTICS FROM THE OLD UNDO\n// ------------------------------\n// The old `mu undo --yes` swapped the whole DB file back to a snapshot. That\n// reverts your OTHER workstreams too — on the real dogfood DB, 812 tasks\n// across three workstreams — so it was a blunt instrument you hesitated\n// to reach for. It also did not sync, so a peer kept the mistake.\n//\n// `mu undo` emits inverse ops for one `group_id`:\n//\n// * GRANULAR — touches only the rows that action touched.\n// * COMPOSABLE — it is itself an ordinary op, in its own group, so it\n// syncs to peers and is ITSELF UNDOABLE. There is no\n// separate redo mechanism and no asymmetry between undo\n// and redo: undoing an undo is just undoing a group.\n// * NO FILES — no snapshots, no swap, nothing to prune.\n//\n// HOW AN INVERSE IS COMPUTED\n// --------------------------\n// Per op in the group, oldest-first:\n//\n// put that CREATED the row -> inverse is `del`\n// put that CHANGED fields -> inverse is a put restoring the PRIOR\n// value of exactly those fields\n// del -> inverse is a put restoring the row\n//\n// A field the group wrote more than once is restored once, from the value\n// before its FIRST write in the group: later writes' \"prior\" values are\n// the group's own intermediate states.\n//\n// \"Did this put create the row\" is answered by provenance, not by a flag:\n// a put created the row iff no op for that key precedes it. \"What was\n// this field's prior value\" is the newest op before this one that named\n// that field — the same shape of query src/apply.ts uses for per-field\n// LWW (its private `fieldHlc`), pointed backwards. `priorFieldValue`\n// below is the one place undo answers \"what was this field before\";\n// keep its predicates (json_type, the legacy-intent exclusion) in step\n// with apply's, or undo will quietly restore wrong values.\n//\n// WHY INVERSES GO THROUGH THE NORMAL WRITE PATH\n// ---------------------------------------------\n// This module MUTATES THE TABLES inside a `withOpContext` scope and lets\n// the capture triggers record the result. It does NOT hand-write rows\n// into `ops`, and it does NOT use applyOp (which is capture-SUPPRESSED,\n// being the ingest path). Consequences, all of them the point:\n//\n// * The inverse gets a fresh HLC from the same clock as any other edit,\n// so it is newest and therefore wins per-field LWW.\n// * It is captured like any other change, so it appears in `mu log`,\n// flushes to this machine's segment, and is visible to the drift\n// check. `mu doctor --deep` stays clean after an undo, which is the\n// strongest end-to-end proof available that undo did not corrupt the\n// projection.\n// * It lands in its own group, which is what makes undoing an undo work\n// with no extra machinery.\n//\n// THE SUPERSESSION DECISION\n// -------------------------\n// Undoing a group whose rows were changed AGAIN by a later group is the\n// hard case. Because the inverse gets a fresh (newest) HLC, it would WIN\n// per-field LWW and silently clobber that newer work. Three options:\n//\n// (a) clobber silently — destroys work the operator did after\n// the mistake. Unacceptable.\n// (b) skip superseded fields — \"undo\" that partially does nothing,\n// silently. Equally unacceptable: the\n// operator believes the action is undone.\n// (c) REFUSE, name the conflict, require an explicit flag.\n//\n// (c) is implemented. `planUndo` detects, per field, whether a LATER op\n// (from a different group) has written that field since, and reports it\n// as a `supersededBy` conflict. `updated_at` is the exception: almost\n// every write touches it (a `task note` touches its task), so a later\n// write to it is not a conflict; the undo just leaves the newer\n// timestamp. `undoGroup` refuses unless\n// `opts.force === true`. So the default is safe and loud, and the\n// override exists and says what it will destroy. Fail safe, never fail\n// silent — the same rule capture follows.\n//\n// FK ORDERING\n// -----------\n// A group can span entities: a cascade close writes N task ops, and a\n// workstream teardown writes tombstones for the whole tree. Restoring a\n// task before its workstream violates the FK, so inverses are applied in\n// DEPENDENCY ORDER (workstream -> task -> note/edge), not merely in\n// reverse emission order. See ENTITY_RESTORE_ORDER.\n\nimport { repairTaskPair } from \"./apply.js\";\nimport { type Db, resolveWorkstreamId } from \"./db.js\";\nimport { LEGACY_LOG_ONLY_SQL_EXCLUSION } from \"./legacy-ops.js\";\nimport { groupIdFromPrefix } from \"./logs.js\";\nimport { withOpContext } from \"./op-context.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\nimport { localMachineId } from \"./segments.js\";\nimport { mapLegacyStatus, resolvePair } from \"./tasks/status.js\";\n\n/** One op as stored, with the provenance we need to invert it. */\ninterface GroupOpRow {\n seq: number;\n hlc: string;\n group_id: string;\n machine_id: string;\n intent: string | null;\n actor: string | null;\n entity: string;\n key: string;\n op: string;\n payload: string;\n /** Ops for this key with a strictly older HLC. */\n prior: number;\n /** Kind of the NEWEST op preceding this one for the same key, or null\n * when there is none. 'del' means this put RESURRECTED the row, which\n * is a creation for undo purposes even though prior > 0. */\n priorKind: string | null;\n}\n\n/** What undoing one op will do. */\nexport interface InverseOp {\n /** Entity of the row being restored/removed. */\n entity: string;\n /** Natural key of that row. */\n key: string;\n /** The inverse action. */\n op: \"put\" | \"del\";\n /** Field -> prior value, for a `put`. Empty for a `del`. */\n fields: Record<string, string | number | null>;\n /** Human summary, for the dry-run listing. */\n summary: string;\n /** Fields that a LATER group has written since, with the group that\n * did. Non-empty means applying this inverse would clobber newer\n * work. */\n supersededBy: Array<{ field: string; groupId: string; intent: string | null }>;\n}\n\nexport interface UndoPlan {\n /** The group being undone. */\n groupId: string;\n /** The intent(s) of the ops in the group — what the operator did. */\n intents: readonly string[];\n /** When the group was written (ISO, from the oldest op). */\n when: string;\n /** Inverse ops, in the order they will be applied (FK-safe). */\n inverses: readonly InverseOp[];\n /** True iff any inverse would clobber a newer edit. */\n superseded: boolean;\n /** Ops in the group that need no inverse (already reverted, or a\n * no-op), for an honest count. */\n skipped: number;\n}\n\nexport interface UndoResult {\n plan: UndoPlan;\n /** Group id of the ops the UNDO itself wrote — pass this to\n * `mu undo` to redo. Null when no inverse changed a row: the undo\n * wrote no ops, so there is no group to redo. */\n undoGroupId: string | null;\n /** Inverse ops that actually changed a row. */\n applied: number;\n}\n\n/** Raised when the requested group does not exist. */\nexport class UndoGroupNotFoundError extends Error implements HasNextSteps {\n constructor(readonly groupId: string) {\n super(`no ops found for group ${JSON.stringify(groupId)}`);\n this.name = \"UndoGroupNotFoundError\";\n }\n\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List recent undoable groups\", command: \"mu undo\" },\n { intent: \"Inspect a group's ops\", command: \"mu log --group <id>\" },\n ];\n }\n}\n\n/** Raised when there is nothing to undo at all. */\nexport class NothingToUndoError extends Error implements HasNextSteps {\n constructor() {\n super(\"the ops log is empty; nothing to undo\");\n this.name = \"NothingToUndoError\";\n }\n\n errorNextSteps(): NextStep[] {\n return [{ intent: \"Make a change first\", command: \"mu task add <id> -t <title>\" }];\n }\n}\n\n/** Raised when the group's rows were changed by a later group, so\n * undoing would clobber that newer work. */\nexport class UndoSupersededError extends Error implements HasNextSteps {\n constructor(\n readonly groupId: string,\n readonly conflicts: readonly { key: string; field: string; groupId: string }[],\n ) {\n const shown = conflicts\n .slice(0, 5)\n .map((c) => `${c.key}.${c.field} (changed by ${c.groupId.slice(0, 8)})`)\n .join(\", \");\n super(\n `group ${groupId.slice(0, 8)} has been superseded: ${conflicts.length} field(s) were changed ` +\n `by a later action — ${shown}. Undoing would discard that newer work.`,\n );\n this.name = \"UndoSupersededError\";\n }\n\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Inspect what changed since\",\n command: `mu log --group ${this.groupId.slice(0, 8)}`,\n },\n {\n intent: \"Undo anyway, discarding the newer edits\",\n command: `mu undo ${this.groupId.slice(0, 8)} --yes --force`,\n },\n ];\n }\n}\n\n// ─── entity ordering ──────────────────────────────────────────────────\n\n/**\n * Dependency order for RESTORING rows. A workstream must exist before its\n * tasks, and a task before its notes and edges, or the FK rejects the\n * insert.\n *\n * Not simply \"reverse of emission order\": a teardown emits the workstream\n * tombstone FIRST (it is the row the operator deleted; the rest are FK\n * cascade victims), so reversing would try to restore notes before their\n * task. Sorting by entity depth is correct regardless of how the group\n * happened to be emitted.\n */\nconst ENTITY_RESTORE_ORDER: Record<string, number> = {\n workstream: 0,\n task: 1,\n note: 2,\n edge: 2,\n};\n\n/** Entity depth for ordering inverses. Restores sort by ascending rank\n * (parents first); deletes sort by descending rank, the mirror image\n * (children first, so the workstream goes last). */\nfunction restoreRank(entity: string): number {\n return ENTITY_RESTORE_ORDER[entity] ?? 3;\n}\n\n// ─── provenance ───────────────────────────────────────────────────────\n\n/**\n * The value `field` held for `key` immediately BEFORE `hlc`.\n *\n * This is the provenance query src/apply.ts uses for per-field LWW,\n * pointed backwards: the newest op strictly older than `hlc` that NAMED\n * this field. `json_type(...) IS NOT NULL` rather than\n * `json_extract(...) IS NOT NULL` for the same reason apply does it —\n * json_extract returns SQL NULL both for an absent key and for a\n * present-but-null one, so a set-to-NULL would look absent and we would\n * restore the wrong (older) value.\n *\n * Returns `{ found: false }` when no earlier op named the field, which\n * means the field had no value before this op — so there is nothing to\n * restore and the op must have been part of the row's creation.\n *\n * `machineId` limits the lookup to one machine's ops. Undo passes it for\n * `owner_id`, whose live value only this machine's ops ever wrote (apply\n * strips it from peers' ops).\n */\nexport function priorFieldValue(\n db: Db,\n entity: string,\n key: string,\n hlc: string,\n field: string,\n machineId?: string,\n): { found: true; value: string | number | null } | { found: false } {\n const row = db\n .prepare(\n `SELECT json_extract(payload, '$.' || @field) AS value\n FROM ops\n WHERE entity = @entity\n AND key = @key\n AND op = 'put'\n AND hlc < @hlc\n AND (@machineId IS NULL OR machine_id = @machineId)\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n AND json_type(payload, '$.' || @field) IS NOT NULL\n ORDER BY hlc DESC\n LIMIT 1`,\n )\n .get({ entity, key, hlc, field, machineId: machineId ?? null }) as\n | { value: string | number | null }\n | undefined;\n if (row === undefined) return { found: false };\n return { found: true, value: row.value };\n}\n\n/** Groups that wrote `field` of `key` AFTER `hlc`. Non-empty means the\n * field has been superseded since the group we are undoing. `machineId`\n * as in `priorFieldValue`. */\nfunction laterWriters(\n db: Db,\n entity: string,\n key: string,\n hlc: string,\n field: string,\n excludeGroup: string,\n machineId?: string,\n): Array<{ groupId: string; intent: string | null }> {\n return db\n .prepare(\n `SELECT DISTINCT group_id AS groupId, intent\n FROM ops\n WHERE entity = @entity\n AND key = @key\n AND op = 'put'\n AND hlc > @hlc\n AND group_id <> @excludeGroup\n AND (@machineId IS NULL OR machine_id = @machineId)\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n AND json_type(payload, '$.' || @field) IS NOT NULL`,\n )\n .all({ entity, key, hlc, field, excludeGroup, machineId: machineId ?? null }) as Array<{\n groupId: string;\n intent: string | null;\n }>;\n}\n\n/** Groups that wrote ANY field of `key` AFTER `hlc`, regardless of which\n * field. Used for the two whole-row inverses (restore-from-tombstone,\n * delete-the-row-this-group-created) where the question is \"did a later\n * group touch this key at all\", not \"did it set one specific field\". */\nfunction laterRowWriters(\n db: Db,\n entity: string,\n key: string,\n hlc: string,\n excludeGroup: string,\n): Array<{ groupId: string; intent: string | null }> {\n return db\n .prepare(\n `SELECT DISTINCT group_id AS groupId, intent\n FROM ops\n WHERE entity = @entity\n AND key = @key\n AND op = 'put'\n AND hlc > @hlc\n AND group_id <> @excludeGroup\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}`,\n )\n .all({ entity, key, hlc, excludeGroup }) as Array<{\n groupId: string;\n intent: string | null;\n }>;\n}\n\n/** True iff the newest op for `key` is a tombstone written by a group\n * other than `groupId`, i.e. the row is gone and not by this group's\n * own hand. */\nfunction isDeletedByLaterGroup(db: Db, entity: string, key: string, groupId: string): boolean {\n const row = db\n .prepare(\n `SELECT op, group_id AS groupId FROM ops\n WHERE entity = @entity AND key = @key\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n ORDER BY hlc DESC LIMIT 1`,\n )\n .get({ entity, key }) as { op: string; groupId: string } | undefined;\n return row?.op === \"del\" && row.groupId !== groupId;\n}\n\n/** True iff a later group DELETED this key after `hlc`. Undoing a change\n * to a row that has since been deleted would resurrect it. */\nfunction laterDeleters(\n db: Db,\n entity: string,\n key: string,\n hlc: string,\n excludeGroup: string,\n): Array<{ groupId: string; intent: string | null }> {\n return db\n .prepare(\n `SELECT DISTINCT group_id AS groupId, intent\n FROM ops\n WHERE entity = @entity\n AND key = @key\n AND op = 'del'\n AND hlc > @hlc\n AND group_id <> @excludeGroup`,\n )\n .all({ entity, key, hlc, excludeGroup }) as Array<{ groupId: string; intent: string | null }>;\n}\n\n// ─── group discovery ──────────────────────────────────────────────────\n\nexport interface GroupSummary {\n groupId: string;\n /** Distinct intents in the group, in first-seen order. */\n intents: readonly string[];\n actor: string | null;\n /** Ops in the group. */\n ops: number;\n /** ISO timestamp of the group's oldest op. */\n when: string;\n /** Newest HLC in the group, for ordering. */\n hlc: string;\n}\n\n/**\n * Recent groups, newest first. This is how group ids become DISCOVERABLE:\n * `mu undo` with no argument lists these, so the operator never has to\n * know a uuid to use the verb.\n *\n * Only groups that touched a portable table are listed, because those are\n * the only ones with anything to invert.\n */\nexport function listRecentGroups(db: Db, limit = 10): GroupSummary[] {\n const rows = db\n .prepare(\n `SELECT group_id AS groupId,\n MAX(hlc) AS hlc,\n MIN(created_at) AS when_,\n COUNT(*) AS ops,\n MAX(actor) AS actor\n FROM ops\n WHERE entity IN ('workstream','task','note','edge')\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n GROUP BY group_id\n ORDER BY MAX(hlc) DESC\n LIMIT @limit`,\n )\n .all({ limit }) as Array<{\n groupId: string;\n hlc: string;\n when_: string;\n ops: number;\n actor: string | null;\n }>;\n\n return rows.map((row) => {\n const intents = (\n db\n .prepare(\n `SELECT DISTINCT intent FROM ops\n WHERE group_id = @groupId AND intent IS NOT NULL\n ORDER BY seq`,\n )\n .all({ groupId: row.groupId }) as { intent: string }[]\n ).map((r) => r.intent);\n return {\n groupId: row.groupId,\n intents,\n actor: row.actor,\n ops: row.ops,\n when: row.when_,\n hlc: row.hlc,\n };\n });\n}\n\n/** The most recent undoable group, or null when there is none. */\nexport function mostRecentGroup(db: Db): GroupSummary | null {\n return listRecentGroups(db, 1)[0] ?? null;\n}\n\n/**\n * Resolve a possibly-abbreviated group id to a full one, or raise.\n *\n * Delegates to `groupIdFromPrefix` (src/logs.ts) so `mu undo` and\n * `mu log --group` accept EXACTLY the same identifiers. They used to\n * disagree: undo resolved prefixes, `mu log --group` compared the column\n * literally and silently returned nothing\n * (bug_group_id_prefix_asymmetry). One rule, two verbs.\n *\n * Ambiguity surfaces as `GroupIdAmbiguousError` (exit 4, a conflict the\n * operator resolves) rather than being folded into not-found.\n */\nexport function resolveGroupId(db: Db, prefix: string): string {\n const resolved = groupIdFromPrefix(db, prefix);\n if (resolved === null) throw new UndoGroupNotFoundError(prefix);\n return resolved;\n}\n\n// ─── planning ─────────────────────────────────────────────────────────\n\nconst CAPTURED_TABLE_FOR: Record<string, string> = {\n workstream: \"workstreams\",\n task: \"tasks\",\n note: \"task_notes\",\n edge: \"task_edges\",\n};\n\n/** Fields never restored by an undo: surrogate ids and the columns\n * encoded in the natural key (`local_id` / `name`). */\nconst NEVER_RESTORE = new Set([\"id\", \"local_id\", \"name\", \"workstream_id\", \"task_id\"]);\n\n/** `tasks.owner_id` is an FK into machine-local `agents`. Undoing a\n * claim or release restores it with the status/substate pair it was\n * written with, but only from THIS machine's ops (a peer's value names\n * a peer's agent) and only when that agent still exists. No local op\n * before it means the row came from a peer and the owner was NULL. A whole-row\n * restore from a tombstone leaves it NULL, as apply does. */\nconst OWNER_FIELD = \"owner_id\";\n\n/** Excluded from whole-row reconstruction (tombstone restores). */\nconst NEVER_RECONSTRUCT = new Set([...NEVER_RESTORE, OWNER_FIELD]);\n\n/** Bookkeeping timestamp written by nearly every task change, including\n * the parent touch of `mu task note`. A later write to it alone is not\n * a supersession: undo then leaves the newer timestamp in place. */\nconst TOUCH_FIELD = \"updated_at\";\n\n/**\n * Compute what undoing `groupId` would do, WITHOUT doing it.\n *\n * Pure with respect to the DB: reads only. `mu undo <group>` calls this\n * for its dry run and `undoGroup` calls it again before applying, so the\n * preview and the action can never diverge.\n */\nexport function planUndo(db: Db, groupId: string): UndoPlan {\n const rows = db\n .prepare(\n `SELECT o.seq, o.hlc, o.group_id, o.machine_id, o.intent, o.actor, o.entity, o.key, o.op, o.payload,\n (SELECT COUNT(*) FROM ops p\n WHERE p.entity = o.entity AND p.key = o.key AND p.hlc < o.hlc\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}) AS prior,\n (SELECT p.op FROM ops p\n WHERE p.entity = o.entity AND p.key = o.key AND p.hlc < o.hlc\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n ORDER BY p.hlc DESC LIMIT 1) AS priorKind\n FROM ops o\n WHERE o.group_id = @groupId\n AND o.entity IN ('workstream','task','note','edge')\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n ORDER BY o.hlc`,\n )\n .all({ groupId }) as GroupOpRow[];\n\n if (rows.length === 0) throw new UndoGroupNotFoundError(groupId);\n\n const inverses: InverseOp[] = [];\n let skipped = 0;\n /** Task keys already handled by the legacy-note recovery below, which\n * works per TASK rather than per tombstone (see there). */\n const seenLegacyNoteTasks = new Set<string>();\n /** Per row key, the fields an EARLIER op of this group already\n * restores. A group can write one field twice (`task.park` writes\n * updated_at in two statements); only the first write's prior value\n * is the pre-group state, so later writes add no inverse for it. */\n const restoredFields = new Map<string, Set<string>>();\n let machine: string | undefined;\n const localMachine = (): string => {\n machine ??= localMachineId(db);\n return machine;\n };\n\n for (const row of rows) {\n if (row.op === \"del\") {\n // Inverse of a delete: restore the row from the state the log says\n // it had. Reconstruct it by replaying every put for the key that\n // preceded the tombstone, so a partial-update history still yields\n // a complete row.\n //\n // When that fold comes back empty, fall back to the tombstone's\n // OWN payload. Note tombstones are self-describing (see\n // fullOldPayload in src/capture.ts) precisely because a note's key\n // embeds a non-portable rowid: after a reprojection the puts sit\n // under the old key and the fold finds nothing, which used to make\n // the restore a silent no-op (drift-641).\n let fields = reconstructRow(db, row.entity, row.key, row.hlc);\n if (Object.keys(fields).length === 0) fields = payloadFields(row.payload);\n // Third tier, for LEGACY note tombstones only. A note key embeds a\n // non-portable rowid, so after a reprojection the puts sit under a\n // different key than the later del (drift-641). Tombstones written\n // since that fix carry the row, so tier 2 resolves them — but ones\n // written BEFORE it carry '{}', and then both tiers come back\n // empty and the note is silently not restored. Recover from the\n // puts for the same TASK instead of the same key.\n //\n // Ordinal position cannot be used to pair them: notes are a\n // grow-only SET, and before 3.1.1 it was keyed on (task, content),\n // so identical prose collapsed to one row. Observed 5 puts against 3 dels for one\n // task, the surplus being repeated '[reaper]' notes — the Nth put\n // is therefore not the Nth del. So this restores the DISTINCT\n // contents the log holds for the task and lets the set semantics\n // settle the count, which reaches the same end state the dedupe\n // produced. Consequence: the notes come back, but which tombstone\n // restored which note is not reconstructible, so this runs once\n // per task rather than once per tombstone.\n if (Object.keys(fields).length === 0 && row.entity === \"note\") {\n const recovered = recoverLegacyNoteContents(db, row.key, row.hlc, seenLegacyNoteTasks);\n for (const legacy of recovered) {\n inverses.push({\n entity: \"note\",\n key: legacy.key,\n op: \"put\",\n fields: legacy.fields,\n summary: `note ${legacy.key} (recovered from legacy history)`,\n supersededBy: [],\n });\n }\n skipped += 1;\n continue;\n }\n // History predating v10 carries retired lifecycle values that the\n // task_substates FK rejects, so a restore maps them onto the pair\n // they now mean, exactly as the apply path does. Without this,\n // undoing a pre-v10 `workstream teardown` aborted the whole\n // transaction on the first DEFERRED task.\n const status = fields.status;\n const legacy = typeof status === \"string\" ? mapLegacyStatus(status) : null;\n if (legacy) {\n fields.status = legacy.status;\n fields.substate = legacy.substate;\n }\n const conflicts = laterRowWriters(db, row.entity, row.key, row.hlc, groupId).map((w) => ({\n field: \"<row>\",\n groupId: w.groupId,\n intent: w.intent,\n }));\n inverses.push({\n entity: row.entity,\n key: row.key,\n op: \"put\",\n fields,\n summary: `${row.entity} ${row.key} (from tombstone)`,\n supersededBy: conflicts,\n });\n continue;\n }\n\n // A put. Did it CREATE the row, or CHANGE fields?\n //\n // Creation is \"no prior op at all\" OR \"the newest prior op was a\n // tombstone\" — the latter is a RESURRECTION, which creates the row\n // just as much as a first insert does. Testing only `prior === 0`\n // gets this wrong for the very common case of undoing an undo of a\n // delete: the undo's restoring put has prior ops (the original add\n // and the delete), so it would be treated as a field change and its\n // inverse would restore fields instead of deleting the row.\n if (row.prior === 0 || row.priorKind === \"del\") {\n // Created it: the inverse is a delete. If the newest op for the\n // key is a delete by another group (an earlier undo of this one,\n // or a later delete), the row is gone and there is nothing to\n // revert.\n if (isDeletedByLaterGroup(db, row.entity, row.key, groupId)) {\n skipped += 1;\n continue;\n }\n // A later group that wrote this key is a supersession — deleting\n // would discard that work.\n const later = [...laterRowWriters(db, row.entity, row.key, row.hlc, groupId)];\n inverses.push({\n entity: row.entity,\n key: row.key,\n op: \"del\",\n fields: {},\n summary: `${row.entity} ${row.key} (it was created by this group)`,\n supersededBy: later.map((w) => ({ field: \"<row>\", groupId: w.groupId, intent: w.intent })),\n });\n continue;\n }\n\n // Changed fields: restore the prior value of exactly those fields.\n const payload = JSON.parse(row.payload) as Record<string, unknown>;\n const fields: Record<string, string | number | null> = {};\n const supersededBy: InverseOp[\"supersededBy\"] = [];\n const rowKey = `${row.entity}\\u0000${row.key}`;\n const restored = restoredFields.get(rowKey) ?? new Set<string>();\n restoredFields.set(rowKey, restored);\n for (const field of Object.keys(payload)) {\n if (NEVER_RESTORE.has(field) || restored.has(field)) continue;\n restored.add(field);\n let machineId: string | undefined;\n if (field === OWNER_FIELD) {\n // A peer's owner_id was never applied here, so there is nothing\n // of it to undo.\n if (row.machine_id !== localMachine()) continue;\n machineId = localMachine();\n }\n const prior =\n field === OWNER_FIELD\n ? priorOwner(db, row.key, row.hlc, localMachine())\n : priorFieldValue(db, row.entity, row.key, row.hlc, field, machineId);\n if (!prior.found) {\n // No earlier op named this field, so it had no prior value to go\n // back to. Nothing to restore for it.\n continue;\n }\n // The prior owner's agent row is gone (ON DELETE SET NULL): its id\n // would violate the FK, so the owner stays as it is.\n if (field === OWNER_FIELD && prior.value !== null && !agentExists(db, prior.value)) continue;\n const writers = laterWriters(db, row.entity, row.key, row.hlc, field, groupId, machineId);\n if (field === TOUCH_FIELD && writers.length > 0) continue;\n fields[field] = prior.value;\n for (const writer of writers) {\n supersededBy.push({ field, groupId: writer.groupId, intent: writer.intent });\n }\n }\n // A later DELETE also supersedes: restoring fields on a row that has\n // since been removed would resurrect it.\n for (const deleter of laterDeleters(db, row.entity, row.key, row.hlc, groupId)) {\n supersededBy.push({ field: \"<row>\", groupId: deleter.groupId, intent: deleter.intent });\n }\n\n if (Object.keys(fields).length === 0) {\n skipped += 1;\n continue;\n }\n inverses.push({\n entity: row.entity,\n key: row.key,\n op: \"put\",\n fields,\n summary: `${row.entity} ${row.key} ${Object.entries(fields)\n .map(([f, v]) => `${f}=${v === null ? \"NULL\" : String(v)}`)\n .join(\" \")}`,\n supersededBy,\n });\n }\n\n // FK-safe ordering. Restores must go parents-first; deletes\n // children-first. Sorting by entity depth is correct regardless of the\n // order the group happened to be emitted in.\n const ordered = [...inverses].sort((a, b) => {\n if (a.op !== b.op) return a.op === \"put\" ? -1 : 1; // restores before deletes\n const rank =\n a.op === \"put\"\n ? restoreRank(a.entity) - restoreRank(b.entity)\n : restoreRank(b.entity) - restoreRank(a.entity);\n return rank;\n });\n\n const intents = [...new Set(rows.map((r) => r.intent).filter((i): i is string => i !== null))];\n\n return {\n groupId,\n intents,\n when: groupWhen(db, groupId),\n inverses: ordered,\n superseded: ordered.some((i) => i.supersededBy.length > 0),\n skipped,\n };\n}\n\n/**\n * The owner_id a task had just before `hlc`, from this machine's ops.\n *\n * Always found: a task row only ever starts unowned. Apply strips a\n * peer's owner_id and inserts the row with owner_id NULL without\n * capturing, so a task created (or recreated after a tombstone) by a\n * peer has no local op naming owner_id. A local op older than a\n * tombstone that won described a row that no longer exists, so it does\n * not count either. A tombstone lost (and deleted nothing) when a newer\n * put had already arrived, i.e. one with a higher hlc and a lower seq:\n * the same check applyDel makes.\n */\nfunction priorOwner(\n db: Db,\n key: string,\n hlc: string,\n machineId: string,\n): { found: true; value: string | number | null } {\n const local = priorFieldValue(db, \"task\", key, hlc, OWNER_FIELD, machineId);\n if (!local.found) return { found: true, value: null };\n const tomb = db\n .prepare(\n `SELECT 1 FROM ops d\n WHERE d.entity = 'task' AND d.key = @key AND d.op = 'del' AND d.hlc < @hlc\n AND d.hlc > (SELECT MAX(hlc) FROM ops\n WHERE entity = 'task' AND key = @key AND op = 'put'\n AND hlc < @hlc AND machine_id = @machineId\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n AND json_type(payload, '$.owner_id') IS NOT NULL)\n AND NOT EXISTS (SELECT 1 FROM ops p\n WHERE p.entity = 'task' AND p.key = @key AND p.op = 'put'\n AND p.hlc > d.hlc AND p.seq < d.seq)\n LIMIT 1`,\n )\n .get({ key, hlc, machineId });\n return tomb === undefined ? local : { found: true, value: null };\n}\n\nfunction agentExists(db: Db, id: string | number): boolean {\n return db.prepare(\"SELECT 1 FROM agents WHERE id = ?\").get(id) !== undefined;\n}\n\nfunction groupWhen(db: Db, groupId: string): string {\n const row = db\n .prepare(\"SELECT MIN(created_at) AS when_ FROM ops WHERE group_id = ?\")\n .get(groupId) as { when_: string | null } | undefined;\n return row?.when_ ?? \"\";\n}\n\n/**\n * Note contents the log holds for a task, for tombstones no other tier\n * can resolve (see the call site for why this is per-task).\n *\n * Returns one entry per DISTINCT content, skipping any whose content is\n * already live, and returns nothing at all the second time it is asked\n * about a task — `seen` makes the per-task work happen once even though\n * the caller iterates per tombstone.\n */\nfunction recoverLegacyNoteContents(\n db: Db,\n tombstoneKey: string,\n beforeHlc: string,\n seen: Set<string>,\n): Array<{ key: string; fields: Record<string, string | number | null> }> {\n const parsed = parseNoteKey(tombstoneKey);\n if (parsed === null) return [];\n if (seen.has(parsed.taskKey)) return [];\n seen.add(parsed.taskKey);\n\n const rows = db\n .prepare(\n `SELECT key, payload FROM ops\n WHERE entity = 'note'\n AND op = 'put'\n AND hlc < @hlc\n AND key LIKE @prefix\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n ORDER BY hlc`,\n )\n .all({ hlc: beforeHlc, prefix: `${parsed.taskKey}#%` }) as Array<{\n key: string;\n payload: string;\n }>;\n\n const out: Array<{ key: string; fields: Record<string, string | number | null> }> = [];\n const contents = new Set<string>();\n for (const row of rows) {\n const fields = payloadFields(row.payload);\n const content = fields.content;\n if (typeof content !== \"string\" || content === \"\") continue;\n if (contents.has(content)) continue;\n contents.add(content);\n out.push({ key: row.key, fields });\n }\n return out;\n}\n\n/** The restorable fields carried by one op's payload.\n *\n * Shares `NEVER_RECONSTRUCT` and the scalar-only guard with\n * `reconstructRow`, so a fallback can never reintroduce a surrogate id\n * or an FK the fold would have dropped. */\nfunction payloadFields(payload: string): Record<string, string | number | null> {\n const fields: Record<string, string | number | null> = {};\n const parsed = JSON.parse(payload) as Record<string, unknown>;\n for (const [field, value] of Object.entries(parsed)) {\n if (NEVER_RECONSTRUCT.has(field)) continue;\n if (value === null || typeof value === \"string\" || typeof value === \"number\") {\n fields[field] = value;\n }\n }\n return fields;\n}\n\n/**\n * Rebuild the full field set a row had just before `beforeHlc`, by\n * folding every put for that key in HLC order.\n *\n * Necessary because ops are SEMANTIC PARTIAL UPDATES: most tombstones\n * carry no payload and the creating put may have been amended by later\n * partial puts, so no single op holds the whole row. Folding is the only\n * correct reconstruction, and it is the same fold the rebuild path does —\n * just bounded to one key and one point in time.\n */\nfunction reconstructRow(\n db: Db,\n entity: string,\n key: string,\n beforeHlc: string,\n): Record<string, string | number | null> {\n const puts = db\n .prepare(\n `SELECT payload FROM ops\n WHERE entity = @entity AND key = @key AND op = 'put' AND hlc < @hlc\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n ORDER BY hlc`,\n )\n .all({ entity, key, hlc: beforeHlc }) as { payload: string }[];\n const fields: Record<string, string | number | null> = {};\n for (const put of puts) {\n const parsed = JSON.parse(put.payload) as Record<string, unknown>;\n for (const [field, value] of Object.entries(parsed)) {\n if (NEVER_RECONSTRUCT.has(field)) continue;\n if (value === null || typeof value === \"string\" || typeof value === \"number\") {\n fields[field] = value;\n }\n }\n }\n return fields;\n}\n\n// ─── applying ─────────────────────────────────────────────────────────\n\nexport interface UndoOptions {\n /** Apply even when the group has been superseded, discarding the\n * newer edits to those fields. */\n force?: boolean;\n /** Actor recorded on the undo's own ops. */\n actor?: string | undefined;\n}\n\n/**\n * Apply the inverse of `groupId`.\n *\n * Everything happens inside ONE `withOpContext` scope with\n * `group: \"new\"`, so:\n * * every inverse write lands in a single new group (making the undo\n * itself one undoable unit), and\n * * the capture triggers record each write with a fresh HLC, so the\n * undo is an ordinary op that syncs and shows up in `mu log`.\n *\n * Wrapped in one transaction: a half-applied undo is worse than none,\n * because the operator would not know which half.\n */\nexport function undoGroup(db: Db, groupId: string, opts: UndoOptions = {}): UndoResult {\n const plan = planUndo(db, groupId);\n\n if (plan.superseded && opts.force !== true) {\n const conflicts = plan.inverses.flatMap((inv) =>\n inv.supersededBy.map((s) => ({ key: inv.key, field: s.field, groupId: s.groupId })),\n );\n throw new UndoSupersededError(groupId, conflicts);\n }\n\n let applied = 0;\n let undoGroupId = \"\";\n\n const run = db.transaction(() => {\n withOpContext(\n db,\n { intent: \"undo\", group: \"new\", ...(opts.actor !== undefined ? { actor: opts.actor } : {}) },\n () => {\n const ctx = db.prepare(\"SELECT group_id FROM _op_ctx\").get() as\n | { group_id: string | null }\n | undefined;\n undoGroupId = ctx?.group_id ?? \"\";\n\n for (const inverse of plan.inverses) {\n if (applyInverse(db, inverse)) applied += 1;\n }\n },\n );\n });\n run.immediate();\n\n return { plan, undoGroupId: applied > 0 && undoGroupId !== \"\" ? undoGroupId : null, applied };\n}\n\n/** Apply one inverse by MUTATING THE TABLE, letting capture record it.\n * Returns true iff a row changed. */\nfunction applyInverse(db: Db, inverse: InverseOp): boolean {\n const table = CAPTURED_TABLE_FOR[inverse.entity];\n if (table === undefined) return false;\n\n if (inverse.op === \"del\") return deleteRow(db, inverse);\n return restoreRow(db, inverse, table);\n}\n\nfunction deleteRow(db: Db, inverse: InverseOp): boolean {\n switch (inverse.entity) {\n case \"workstream\":\n return db.prepare(\"DELETE FROM workstreams WHERE name = ?\").run(inverse.key).changes > 0;\n case \"task\": {\n const id = taskIdForKey(db, inverse.key);\n if (id === null) return false;\n return db.prepare(\"DELETE FROM tasks WHERE id = ?\").run(id).changes > 0;\n }\n case \"note\": {\n const parsed = parseNoteKey(inverse.key);\n if (parsed === null) return false;\n const taskId = taskIdForKey(db, parsed.taskKey);\n if (taskId === null) return false;\n // A note's identity is (task, author, content, created_at), as in\n // applyNotePut, so delete exactly the note the log says this was,\n // not every repeat of its text.\n const note = db\n .prepare(\n `SELECT json_extract(payload, '$.content') AS content,\n json_extract(payload, '$.author') AS author,\n json_extract(payload, '$.created_at') AS created_at FROM ops\n WHERE entity = 'note' AND key = ? AND op = 'put'\n AND ${LEGACY_LOG_ONLY_SQL_EXCLUSION}\n ORDER BY hlc DESC LIMIT 1`,\n )\n .get(inverse.key) as\n | { content: string | null; author: string | null; created_at: string | null }\n | undefined;\n if (note?.content === undefined || note.content === null) return false;\n return (\n db\n .prepare(\n `DELETE FROM task_notes\n WHERE task_id = @taskId AND content = @content\n AND COALESCE(author, '') = COALESCE(@author, '')\n AND (@createdAt IS NULL OR created_at = @createdAt)`,\n )\n .run({\n taskId,\n content: note.content,\n author: note.author,\n createdAt: note.created_at,\n }).changes > 0\n );\n }\n case \"edge\": {\n const parsed = parseEdgeKey(inverse.key);\n if (parsed === null) return false;\n const from = taskIdForKey(db, parsed.blocker);\n const to = taskIdForKey(db, parsed.blocked);\n if (from === null || to === null) return false;\n return (\n db.prepare(\"DELETE FROM task_edges WHERE from_task_id = ? AND to_task_id = ?\").run(from, to)\n .changes > 0\n );\n }\n default:\n return false;\n }\n}\n\nfunction restoreRow(db: Db, inverse: InverseOp, table: string): boolean {\n switch (inverse.entity) {\n case \"workstream\": {\n const exists = db.prepare(\"SELECT id FROM workstreams WHERE name = ?\").get(inverse.key) as\n | { id: number }\n | undefined;\n if (!exists) {\n db.prepare(\"INSERT INTO workstreams (name, created_at) VALUES (?, ?)\").run(\n inverse.key,\n String(inverse.fields.created_at ?? new Date().toISOString()),\n );\n return true;\n }\n return updateFields(db, \"workstreams\", \"id\", exists.id, inverse.fields);\n }\n case \"task\": {\n const parsed = parseTaskKey(inverse.key);\n if (parsed === null) return false;\n const id = taskIdForKey(db, inverse.key);\n if (id === null) {\n // The task row is gone, so this is a restore-from-tombstone. The\n // parent workstream must exist first; it will have been restored\n // already by the entity ordering, but create it if the group\n // only deleted the task.\n const wsId = ensureWorkstream(db, parsed.workstream);\n const now = new Date().toISOString();\n // Insert a legal pair: the FK checks it at commit, and an\n // unknown status or substate must not abort the whole undo.\n const pair = resolvePair(\n String(inverse.fields.status ?? \"OPEN\"),\n inverse.fields.substate,\n ) ?? {\n status: \"OPEN\",\n substate: \"todo\",\n };\n db.prepare(\n `INSERT INTO tasks (workstream_id, local_id, title, status, substate, impact,\n effort_days, owner_id, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, NULL, ?, ?)`,\n ).run(\n wsId,\n parsed.localId,\n String(inverse.fields.title ?? parsed.localId),\n pair.status,\n pair.substate,\n Number(inverse.fields.impact ?? 50),\n Number(inverse.fields.effort_days ?? 1),\n String(inverse.fields.created_at ?? now),\n String(inverse.fields.updated_at ?? now),\n );\n return true;\n }\n // A partial restore (e.g. status only, from history that predates\n // substates) can leave an illegal pair; repair it before commit.\n const updated = updateFields(db, \"tasks\", \"id\", id, inverse.fields);\n return repairTaskPair(db, id) || updated;\n }\n case \"note\": {\n const parsed = parseNoteKey(inverse.key);\n if (parsed === null) return false;\n const taskId = taskIdForKey(db, parsed.taskKey);\n if (taskId === null) return false;\n const content = inverse.fields.content;\n if (content === undefined || content === null) return false;\n const author = inverse.fields.author;\n const createdAt = inverse.fields.created_at;\n // Same identity as applyNotePut, so restoring one of two repeats of\n // a text does not find the other and stop.\n const exists = db\n .prepare(\n `SELECT id FROM task_notes\n WHERE task_id = @taskId AND content = @content\n AND COALESCE(author, '') = COALESCE(@author, '')\n AND (@createdAt IS NULL OR created_at = @createdAt)`,\n )\n .get({\n taskId,\n content: String(content),\n author: author === undefined || author === null ? null : String(author),\n createdAt: createdAt === undefined || createdAt === null ? null : String(createdAt),\n }) as { id: number } | undefined;\n if (exists) return false; // grow-only: already there\n db.prepare(\n \"INSERT INTO task_notes (task_id, author, content, created_at) VALUES (?, ?, ?, ?)\",\n ).run(\n taskId,\n inverse.fields.author === undefined || inverse.fields.author === null\n ? null\n : String(inverse.fields.author),\n String(content),\n String(inverse.fields.created_at ?? new Date().toISOString()),\n );\n return true;\n }\n case \"edge\": {\n const parsed = parseEdgeKey(inverse.key);\n if (parsed === null) return false;\n const from = taskIdForKey(db, parsed.blocker);\n const to = taskIdForKey(db, parsed.blocked);\n if (from === null || to === null) return false;\n const exists = db\n .prepare(\"SELECT 1 AS x FROM task_edges WHERE from_task_id = ? AND to_task_id = ?\")\n .get(from, to) as { x: number } | undefined;\n if (exists) return false;\n db.prepare(\n \"INSERT INTO task_edges (from_task_id, to_task_id, created_at) VALUES (?, ?, ?)\",\n ).run(from, to, String(inverse.fields.created_at ?? new Date().toISOString()));\n return true;\n }\n default:\n void table;\n return false;\n }\n}\n\n/**\n * Restore the named fields in ONE UPDATE.\n *\n * One statement, not one per field, and that matters: each UPDATE fires\n * the capture trigger once, so a per-field loop would emit N ops for a\n * single logical inverse. They would share the undo's group so undo/redo\n * still worked, but `mu log` would show one action as several and the\n * op count would misrepresent what happened. One statement, one op.\n *\n * Fields already holding the target value are dropped from the statement\n * first, so a fully-redundant inverse issues no UPDATE at all and\n * therefore produces no op (matching the capture WHEN guard). Returns\n * true iff a row actually changed, so `applied` never overstates the work.\n */\nfunction updateFields(\n db: Db,\n table: string,\n idColumn: string,\n id: number,\n fields: Record<string, string | number | null>,\n): boolean {\n const names = Object.keys(fields).filter((f) => !NEVER_RESTORE.has(f));\n if (names.length === 0) return false;\n\n const current = db.prepare(`SELECT * FROM ${table} WHERE ${idColumn} = ?`).get(id) as\n | Record<string, string | number | null>\n | undefined;\n\n // Only fields whose value actually differs. Comparing here rather than\n // relying on the trigger keeps `changed` honest for the caller.\n const pending = names.filter((name) => {\n const target = fields[name] ?? null;\n if (current === undefined) return true;\n return current[name] !== target;\n });\n if (pending.length === 0) return false;\n\n const setClause = pending.map((name) => `${name} = @${name}`).join(\", \");\n const params: Record<string, string | number | null> = { id };\n for (const name of pending) params[name] = fields[name] ?? null;\n db.prepare(`UPDATE ${table} SET ${setClause} WHERE ${idColumn} = @id`).run(params);\n return true;\n}\n\nfunction ensureWorkstream(db: Db, name: string): number {\n const existing = db.prepare(\"SELECT id FROM workstreams WHERE name = ?\").get(name) as\n | { id: number }\n | undefined;\n if (existing) return existing.id;\n db.prepare(\"INSERT INTO workstreams (name, created_at) VALUES (?, ?)\").run(\n name,\n new Date().toISOString(),\n );\n return resolveWorkstreamId(db, name);\n}\n\n// ─── key parsing (mirrors src/apply.ts) ───────────────────────────────\n\nfunction parseTaskKey(key: string): { workstream: string; localId: string } | null {\n const slash = key.indexOf(\"/\");\n if (slash <= 0 || slash === key.length - 1) return null;\n return { workstream: key.slice(0, slash), localId: key.slice(slash + 1) };\n}\n\nfunction parseNoteKey(key: string): { taskKey: string } | null {\n const hash = key.lastIndexOf(\"#\");\n if (hash <= 0) return null;\n return { taskKey: key.slice(0, hash) };\n}\n\nfunction parseEdgeKey(key: string): { blocker: string; blocked: string } | null {\n const arrow = key.indexOf(\"->\");\n if (arrow <= 0) return null;\n return { blocker: key.slice(0, arrow), blocked: key.slice(arrow + 2) };\n}\n\nfunction taskIdForKey(db: Db, key: string): number | null {\n const parsed = parseTaskKey(key);\n if (parsed === null) return null;\n const row = db\n .prepare(\n `SELECT t.id AS id FROM tasks t\n JOIN workstreams w ON w.id = t.workstream_id\n WHERE w.name = ? AND t.local_id = ?`,\n )\n .get(parsed.workstream, parsed.localId) as { id: number } | undefined;\n return row?.id ?? null;\n}\n"],"mappings":"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,aAAO;AAAA,QACL;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA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","names":["existsSync","homedir","join","execa","existsSync","dirname","resolve","dirname","join","join","dirname","randomUUID","machineId","randomUUID","execa","resolve","out","panes","delay","warn","PANE_ID_RE","isValidPaneId","assertValidPaneId","realExecutor","execa","currentExecutor","listSessions","sessionExists","newSession","appendEnvFlags","newSessionWithPane","killSession","listWindows","newWindow","selectLayout","listPanesInSession","listPanes","panes","splitWindow","killPane","paneExists","paneTTY","setPaneTitle","getPaneTitle","currentAgentName","currentSessionName","defaultUndeliveredWarning","sendToPane","warn","capturePane","enableMuPaneBordersForSession","enableMuPaneBordersForPane","attachHint","attachCommands","paneNotFoundNextSteps","healthCheck","execFile","mkdirSync","rmSync","dirname","promisify","dirname","randomUUID","join","rowFromDb","workspacePath","join","existsSync","rmSync","homedir","resolve","existsSync","resolve","rmSync","dirname","workspacePath","dirname","rmSync","fields","existsSync","workspacePath","fields","resolve","existsSync","existsSync","workspacePath","existsSync","existsSync","workspacePath","existsSync","existsSync","workspacePath","BACKENDS","resolve","homedir","existsSync","rmSync","rowFromDb","listWorkspaces","rows","cached","join","listWorkspaces","join","rowFromDb","rowFromDb","existsSync","readdirSync","dirname","join","listWorkspaces","join","existsSync","readdirSync","dirname","row","changedFields","resolve","ageMs","snapshot","snap","realExecutor","execa","currentExecutor","stat","rowFromDb","rows","dirname","join","mkdir","join","join","sleep","mkdir","join","DEFAULT_ACQUIRE_TIMEOUT_MS","envVarNameForCli","promisify","execFile","dirname","mkdirSync","rmSync","workspacePath","dirname","existsSync","fields","join","homedir","existsSync","execa","applied","taskKey","run","pc","pc","accessSync","constants","existsSync","readFileSync","realpathSync","delimiter","dirname","join","fileURLToPath","rmSync","join","existsSync","mkdirSync","rmSync","dirname","existsSync","mkdirSync","dirname","rmSync","join","rmSync","existsSync","resolve","syncDir","existsSync","mkdirSync","rmSync","homedir","dirname","join","resolve","snapshot","delimiter","join","accessSync","constants","realpathSync","dirname","existsSync","readFileSync","snapshot","createHash","existsSync","mkdirSync","readdirSync","readFileSync","statSync","writeFileSync","tmpdir","join","resolve","sep","resolve","tmpdir","sep","machineId","join","mkdirSync","createHash","writeFileSync","existsSync","readFileSync","readdirSync","statSync","run","listWorkspaces","existsSync","statSync","Database","statSync","existsSync","Database","run","machineId","fields","legacy","parseNoteKey","run","parseEdgeKey","parseTaskKey","ensureWorkstream"]}
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|