diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index b4bc956..7508db5 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "claudius", - "version": "5.15.0", + "version": "5.16.0", "description": "Collection of specialized development agents and skills for Claude Code", "author": { "name": "lklimek", diff --git a/CHANGELOG.md b/CHANGELOG.md index f3654d2..5de2390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). This project use ## [Unreleased] +## [5.16.0] - 2026-07-22 + +### Changed + +- **`skills/codex-crew/SKILL.md`**: coordinator-orchestrated Codex dispatch now bypasses the `codex:codex-rescue` subagent entirely — call `codex-companion.mjs task` directly with `--cwd ` and `--prompt-file ` (new § Direct Dispatch). `codex:codex-rescue` is a thin forwarder that never exposed `--cwd`/`--prompt-file`, forcing worktree binding through `EnterWorktree`/`ExitWorktree` (which serializes concurrent multi-worktree dispatch) and shell-inlined prompts (which corrupts on embedded quotes/Rust `Debug` dumps). Direct dispatch fixes both structurally and removes the wrapper's unreliable `idle_notification`/stall-watchdog signal entirely — there's no agent lifecycle to watch. `codex:codex-rescue` remains reserved for the upstream, user-typed `/codex:rescue` interactive command only. +- **`skills/codex-crew/SKILL.md`** § Never Dispatch Concurrently to the Same `--cwd` (renamed from § Never Dispatch Back-to-Back from the Same cwd): collision risk is now scoped to literal same-`--cwd` dispatches (e.g. plan-then-implement, or a retry), not every dispatch sharing one coordinator session. +- **`skills/codex-crew/SKILL.md`** § Sandbox & Workdir rule 3 and § Plan-Approval Gate: `EnterWorktree`/`ExitWorktree` is no longer required just to bind a Codex dispatch to its worktree — `--cwd` does that directly, so genuinely concurrent multi-worktree dispatch is now possible. +- **`skills/codex-crew/references/sandbox-and-recovery.md`**: on-disk job state's `workspaceRoot` now reliably matches the intended worktree for direct dispatch (it's the `--cwd` value); the old dispatching-session-cwd caveat is scoped to the interactive `codex:codex-rescue` path only. + ## [5.15.0] - 2026-07-22 ### Added diff --git a/skills/codex-crew/SKILL.md b/skills/codex-crew/SKILL.md index f91af00..5041026 100644 --- a/skills/codex-crew/SKILL.md +++ b/skills/codex-crew/SKILL.md @@ -1,11 +1,13 @@ --- name: codex-crew -description: Use before dispatching work to Codex (codex:codex-rescue) — deciding whether to route coding to Codex Sol, handling a Codex job that fails to write or commit, monitoring a running Codex job, or recovering a stale Codex broker. Pre-flight the coordinator reads once before its first Codex dispatch of a session. +description: Use before dispatching work to Codex Sol — deciding whether to route coding to Codex, dispatching directly via codex-companion.mjs (not the codex:codex-rescue subagent), handling a Codex job that fails to write or commit, monitoring a running Codex job, or recovering a stale Codex broker. Pre-flight the coordinator reads once before its first Codex dispatch of a session. --- # Codex Crew — Enlisting Codex Agents -Codex agents (OpenAI Codex CLI, dispatched through `codex:codex-rescue`) are external crew a coordinator can enlist alongside the named claudius roster. Use of Codex is **opt-in**. Read this once before the first Codex dispatch of a session — it covers routing, the sandbox's hard limits, how to monitor a Codex job, and how to recover a stuck broker. +Codex agents (OpenAI Codex CLI, run via the `codex-companion.mjs` runtime bundled with the `codex` plugin) are external crew a coordinator can enlist alongside the named claudius roster. Use of Codex is **opt-in**. Read this once before the first Codex dispatch of a session — it covers routing, direct dispatch, the sandbox's hard limits, how to monitor a Codex job, and how to recover a stuck broker. + +**Dispatch directly, not through `codex:codex-rescue`.** For coordinator-orchestrated work, `codex:codex-rescue` is pure overhead: a Claude subagent whose entire contract is one `Bash` call to `codex-companion.mjs task`, forwarding stdout unchanged — it never monitors, never adds analysis, and its own lifecycle (`idle_notification`, teammate shutdown, stall-watchdog tracking) is a second, *unreliable* signal layered on top of the actual worker, which is already a detached Node process with its own job-state files. Worse, its forwarding contract never exposes `--cwd`/`--prompt-file`, which is the root cause of most of the bugs documented below. Call `codex-companion.mjs task` directly instead (§ Direct Dispatch) — no agent to spawn, track, or shut down. Reserve `codex:codex-rescue` for the upstream, user-typed `/codex:rescue` interactive command, which this skill doesn't govern. The recurring failure this skill prevents: coordinators re-derive the same Codex sandbox and orchestration quirks session after session, each losing time to the same write-rejection and broker-staleness traps (and an inconsistent commit path — see Sandbox & Workdir rule 2). @@ -17,19 +19,41 @@ The recurring failure this skill prevents: coordinators re-derive the same Codex ## Routing — One Model, High Effort -- **Codex Sol = `--model gpt-5.6-sol --effort high`. Always high effort.** State both flags explicitly on every dispatch: `codex:codex-cli-runtime` only forwards `--effort`/`--model` when present in the request text, so an omitted flag silently drops to the runtime default. -- **Dispatch through `codex:codex-rescue`.** It is a thin forwarder: exactly one `task` invocation, returning that stdout unchanged. It does **not** monitor, poll, or fetch results on its own initiative — that's **coordinator** work (see Monitoring below). It CAN attempt a commit when the dispatch prompt explicitly instructs it to, but success is inconsistent; the coordinator must verify independently (see Sandbox & Workdir rule 2). +- **Codex Sol = `--model gpt-5.6-sol --effort high`. Always high effort.** State both flags explicitly on every direct dispatch — omitting either drops to the runtime default, not Sol. +- **Dispatch via `codex-companion.mjs task` directly** (§ Direct Dispatch below), not via the `codex:codex-rescue` subagent. Nothing monitors, polls, or fetches results on its own — that's **coordinator** work (see Monitoring below). Codex CAN attempt a commit when the dispatch prompt explicitly instructs it to, but success is inconsistent; the coordinator must verify independently (see Sandbox & Workdir rule 2). - The lighter `spark` alias (`gpt-5.3-codex-spark`) exists, but claudius routing standardizes on Sol at high effort. +## Direct Dispatch + +Resolve the installed `codex` plugin's script root once per session — version-pinned cache dirs shift on plugin updates, so never hardcode a version: + +```bash +CODEX_ROOT=$(find ~/.claude/plugins/cache/openai-codex/codex -maxdepth 1 -mindepth 1 -type d | sort -V | tail -1) +``` + +Write the prompt to a file first — never inline it as a shell argument. `task` accepts `--prompt-file ` (also reads piped stdin), and a relative `--prompt-file` path resolves against `--cwd`, so always pass an **absolute** path (e.g. under `/data/tmp`). This sidesteps the quote/escaping corruption a long inline prompt (nested quotes, Rust `Debug` dumps, etc.) suffers when built as a shell argument. + +```bash +node "$CODEX_ROOT/scripts/codex-companion.mjs" task \ + --cwd \ + --prompt-file /data/tmp/.txt \ + --write --background \ + --model gpt-5.6-sol --effort high +``` + +- **`--cwd ` binds the broker/workspace slug to the intended worktree explicitly** — no `EnterWorktree` dance needed just to make Codex write to the right place. Pass it on every dispatch; never rely on the invoking shell's own cwd or on prompt text telling Codex to `cd` (prompt text has zero effect on `codex-companion.mjs`'s own cwd resolution — see Sandbox & Workdir rule 3). +- **`--write` is not implied** — omit it and the run is silently read-only (reports normal completion, touches zero files). +- **`--background`** returns almost instantly with a job id once the detached worker is queued; the coordinator polls job state (§ Monitoring a Codex Job) rather than blocking. +- **Continuing a thread**: `--resume-last` (equivalent to `--resume`) on a second dispatch with the **identical** `--cwd` — the thread is found by workspace, so a mismatched `--cwd` resumes nothing. + ## Plan-Approval Gate Codex dispatches follow `grand-admiral` § Development-Work Delegation: goal only, no file list, agent-authored plan approved by the coordinator before writing code. Split into two dispatches on the SAME Codex thread, never two independent ones — a fresh dispatch rebuilds context from scratch, which is exactly the cost this gate must not add. Sequence: -0. **`EnterWorktree(path=)` before either dispatch** (see Sandbox & Workdir rule 3) — `--resume` finds the thread by workspace, so both dispatches must bind to the identical cwd or the implement dispatch resumes nothing. -1. **Plan dispatch**: request read-only investigation and a plan, no edits (`codex:codex-rescue` defaults to write-capable — say so explicitly to get a no-writes run). -2. **Implement dispatch**: after approval, dispatch with `--resume` (`codex:codex-cli-runtime` maps this to `task --resume-last`, continuing the same per-workspace thread) carrying only the delta instruction ("approved — implement as planned", or the requested changes) — never a restated prompt. +1. **Plan dispatch**: `--cwd `, request read-only investigation and a plan, no `--write` (direct dispatch defaults to read-only unless `--write` is passed). +2. **Implement dispatch**: after approval, dispatch with `--resume-last`, the **identical** `--cwd` as step 1, `--write`, and only the delta instruction ("approved — implement as planned", or the requested changes) — never a restated prompt. -**Known risk — a failed `--resume` can silently duplicate work.** Observed: a `--resume` dispatch fails instantly (`CODEX_FAILED "No previous Codex task thread was found for this repository"`) yet keeps running in the background despite the terminal-failure report, and later lands a second job with its own (redundant) output. If step 2 reports a `--resume` failure, do NOT immediately fire a `--fresh` redispatch from the same cwd — check job state first (§ Monitoring a Codex Job) to rule out a still-running duplicate before treating the cwd as free. Root cause open (`memcan:todo` project=claudius). +**Known risk — a failed `--resume-last` can silently duplicate work.** Observed: a resume dispatch fails instantly (`CODEX_FAILED "No previous Codex task thread was found for this repository"`) yet keeps running in the background despite the terminal-failure report, and later lands a second job with its own (redundant) output. If step 2 reports a resume failure, do NOT immediately fire a fresh redispatch at the same `--cwd` — check job state first (§ Monitoring a Codex Job) to rule out a still-running duplicate before treating the `--cwd` as free. Root cause open (`memcan:todo` project=claudius). ## Sandbox & Workdir — The Load-Bearing Rules @@ -39,25 +63,25 @@ Codex runs under `sandbox_mode = "workspace-write"` (see `~/.codex/config.toml`) 2. **Codex `git commit` in a linked worktree is inconsistent — confirmed both ways the same day (2026-07-16).** One dispatch committed cleanly (`f2639aa`, this repo, no approval prompt). A later dispatch, same repo, different worktree, hit the exact old "Git metadata is read-only"/`index.lock` error and had to be committed by the coordinator instead (`7c2d3e8`). `writable_roots` was unchanged across both, so whatever gates this isn't a static config value — likely `approval_policy = "on-request"` + `trust_level = "trusted"` interacting with something per-dispatch, not independently confirmed. **Treat coordinator-commit as the reliable default, not a fallback**: it is fine to instruct Codex to attempt `git add`/`git commit` itself as its final step (with an explicit commit message — it doesn't know your conventions unless told), but always plan for that attempt to fail and verify afterward — check `git log`/`git status` in the worktree rather than trusting Codex's self-report, and commit yourself (unsandboxed) when it didn't land. See `references/sandbox-and-recovery.md` § Git Commit in a Linked Worktree for both data points. -3. **All worktrees live under the configured root** (`$CLAUDIUS_WORKTREE_ROOT`, default `.claude/worktrees`) at `/`, where the slug derives from the startup `$PWD`. The coordinator pre-creates the worktree following the isolation pattern in `grand-admiral` § Worktree Isolation. **Injecting the absolute path into the dispatch prompt text is not enough** — confirmed 2026-07-21: a dispatch instructed to `cd` into a pre-created worktree still bound its broker to the coordinator's plain checkout, blocking ALL writes (including under `writable_roots`) even on the FIRST dispatch, because the broker keys off the invoking session's actual `$PWD`, not any path mentioned in prompt text. Fix: call `EnterWorktree(path=)` to physically move the coordinating session into the worktree BEFORE dispatching — do this even for a single, non-concurrent dispatch. Leave with `ExitWorktree(action="keep")` (never `"remove"` — the worktree may hold uncommitted work) before entering a different worktree for the next stream. +3. **All worktrees live under the configured root** (`$CLAUDIUS_WORKTREE_ROOT`, default `.claude/worktrees`) at `/`, where the slug derives from the startup `$PWD`. The coordinator pre-creates the worktree following the isolation pattern in `grand-admiral` § Worktree Isolation. **The broker keys off `codex-companion.mjs`'s own resolved cwd, not any path mentioned in prompt text** — confirmed 2026-07-21: a dispatch instructed via prompt text to `cd` into a pre-created worktree still bound its broker to the coordinator's plain checkout, blocking ALL writes (including under `writable_roots`) even on the FIRST dispatch. Pass the worktree path via the direct dispatch's `--cwd ` flag instead (§ Direct Dispatch) — this also means concurrent dispatches to different worktrees no longer require serializing the coordinator's own cwd through `EnterWorktree`/`ExitWorktree`; each dispatch's `--cwd` is self-contained, so N worktrees can be dispatched to genuinely concurrently. Deep mechanics (exact sandbox modes, the on-disk job-state layout, `git commit` in a linked worktree status and fallback) are in `references/sandbox-and-recovery.md`. -### Never Dispatch Back-to-Back from the Same cwd +### Never Dispatch Concurrently to the Same `--cwd` -**Never fire dispatch N+1 from a cwd whose dispatch N hasn't reached a terminal job status.** Same root cause as rule 3 above: `codex:codex-rescue` keys its broker and workspace slug off the **invoking session's cwd**, not the `--worktree` path carried in the dispatch prompt — so two dispatches fired from one session cwd collide on a single slug even when they target different worktrees, even minutes apart. `EnterWorktree` fixes cwd binding for one stream at a time; it does not make two dispatches from the same coordinator session concurrency-safe. Confirmed: dispatches 6–9 minutes apart still collided, and elapsed time or a prior dispatch already having its own job-state file were NOT protective — the only safe rule is polling that dispatch N's job JSON shows `status` in `completed`/`failed` before firing N+1 from that cwd. A collision either strands the earlier dispatch at `status=running` forever with no completion signal (silent orphan), or — observed separately — the earlier dispatch instantly returns Codex's generic capabilities boilerplate with `touchedFiles: []` as if it never received the real prompt (looks like a trivial done, isn't). The root cause lives in the separate `openai-codex` plugin and cannot be fixed from this repo. +**Never fire dispatch N+1 with the same `--cwd` as dispatch N until N reaches a terminal job status.** The broker/workspace slug is keyed off `--cwd` — pass distinct worktree paths and distinct dispatches no longer collide (this replaces the old requirement to serialize through `EnterWorktree`/`ExitWorktree`, see Sandbox & Workdir rule 3). The remaining risk is real only when two dispatches genuinely target the *same* `--cwd` (plan-then-implement on one thread, or a retry) before the first reaches `completed`/`failed`. Confirmed: even same-cwd dispatches minutes apart still collided — elapsed time and a prior dispatch already having its own job-state file were NOT protective — the only safe rule is polling that dispatch N's job JSON shows a terminal `status` before firing N+1 at that same `--cwd`. A collision either strands the earlier dispatch at `status=running` forever with no completion signal (silent orphan), or — observed separately — the earlier dispatch instantly returns Codex's generic capabilities boilerplate with `touchedFiles: []` as if it never received the real prompt (looks like a trivial done, isn't). The root cause (one broker per workspace slug, not per job) lives in the separate `openai-codex` plugin and cannot be fixed from this repo. -Mitigation: poll for terminal job status before the next dispatch from a given cwd (never a fixed stagger delay). `scripts/agent-watchdog.py` should eventually catch a stuck orphan as `CODEX_STALL reason=no-progress` — a detection backstop, not a substitute for avoiding the collision. After any dispatch, sanity-check the job's `workspaceRoot` matches the intended worktree and its `rawOutput` actually engages the dispatched task — a suspiciously fast, generic-sounding completion is a collision red flag, not evidence the task was trivial. +Mitigation: poll for terminal job status before the next dispatch at a given `--cwd` (never a fixed stagger delay). `scripts/agent-watchdog.py` should eventually catch a stuck orphan as `CODEX_STALL reason=no-progress` — a detection backstop, not a substitute for avoiding the collision. After any dispatch, sanity-check the job's `workspaceRoot` matches the intended worktree and its `rawOutput` actually engages the dispatched task — a suspiciously fast, generic-sounding completion is a collision red flag, not evidence the task was trivial. ## Monitoring a Codex Job **MCP watchdog covers Codex too** (`runtime: codex_cli`/`codex_companion` in `register_session`) — prefer it over the `CODEX_*` machinery below when available (see `grand-admiral` § Recovery → MCP Watchdog), with the same corroborate-before-acting caution. -**`codex:codex-rescue` gives no reliable completion heartbeat, in either direction.** `idle_notification` fires as a false-early signal while the job is still genuinely working, AND separately fails to fire at all once real work — including a silent engine crash — has already finished; confirmed 4-for-4 in one wave, with jobs sitting `completed` 40–85 minutes before their wrapper ever reported. **Never treat `idle_notification`, or the absence of a message, as a status signal in either direction.** +**Direct dispatch has no agent lifecycle to watch — by design.** A `--background` dispatch is a detached Node process with on-disk job-state files; there's no subagent to send `idle_notification`, no teammate to stall-watch, nothing to shut down. Go straight to the job-state file. (If `codex:codex-rescue` is ever used — the upstream interactive `/codex:rescue` command — treat its `idle_notification` as worthless in either direction: confirmed 4-for-4 in one wave, jobs sitting `completed` 40–85 minutes before the wrapper ever reported.) **Primary method: read the job's on-disk state directly** (mtime-gated, minimal-field reads — never load the full state blob). See `references/sandbox-and-recovery.md` § On-Disk Job State for the field list, `result.rawOutput`/`result.touchedFiles` usage, and matching jobs to dispatches. This is load-bearing, not a fallback — it is what actually recovers status/results when the stall watchdog can't. -**Get notified, don't just poll on request.** After ruling out a false-early `idle_notification`, arm a `Bash` `run_in_background` until-loop on that job's own `state/-/jobs/.json` (resolve the path per § On-Disk Job State above) — a single, job-specific completion signal that needs no team/session discovery: +**Get notified, don't just poll on request.** Arm a `Bash` `run_in_background` until-loop on that job's own `state/-/jobs/.json` (resolve the path per § On-Disk Job State above) — a single, job-specific completion signal that needs no team/session discovery: ```bash until python3 -c " @@ -75,7 +99,7 @@ This loop is itself a backgrounded Bash call, so it inherits the same silent-kil `ScheduleWakeup` is not a substitute — it's `/loop` dynamic-mode-only and errors outside that context. Don't reach for it as an ad-hoc "check back later" for a Codex dispatch. - The built-in stall watchdog (`grand-admiral` § Recovery → Built-in Stall Watchdog, `scripts/agent-watchdog.py`) discovers Codex jobs and emits `CODEX_*` transition events when the MCP watchdog isn't in use. A watchdog — MCP or built-in — is **mandatory** whenever any agent — Claude or Codex — is dispatched (see `grand-admiral` § Spawning → Monitoring). Treat its `CODEX_*` events as **best-effort, layered on top of** the direct job-state check above — never as a substitute for it. -- **Codex discovery is gated on team membership or `--worktrees`.** The watchdog reaches Codex jobs only through named teammates or an explicit `--worktrees` path on the Monitor command. A session whose Codex work is entirely unnamed background `codex:codex-rescue` dispatches, launched without `--worktrees`, gets **zero** Codex monitoring — the watchdog emits a one-time startup warning on detecting this. Either name Codex dispatches so they join the team, or always point the Monitor command's `--worktrees` flag at the configured worktree root. +- **Codex discovery requires `--worktrees` — direct dispatch has no team-membership path at all.** The watchdog reaches Codex jobs only through named teammates or an explicit `--worktrees` path on the Monitor command; a direct `codex-companion.mjs` dispatch is never a teammate, so `--worktrees` pointed at the configured worktree root is the *only* way the built-in watchdog sees it. Without it, the watchdog emits a one-time startup warning and Codex monitoring is silently zero. - **Direct discovery (`--worktrees`/Source C) now bypasses the session gate entirely** — a workspace found under the worktree root surfaces every job's `CODEX_*` events regardless of `sessionId`, closing the multi-teammate blind spot as long as the Monitor's `--worktrees` points at the configured root (see bullet above). The strict single-session match still applies to *ambient* discovery only (a workspace reachable solely via team lead/member cwd, not also under the worktree root) — `codex-companion.mjs` stamps each job's `sessionId` from its own dispatching session, never the coordinator's, so that narrower path can still under-report a mismatched session. The direct job-state check above is unaffected by any of this either way, which is why it's the primary method, not the stopgap. - Don't guess the Monitor's `--session-id`: see `grand-admiral`'s `references/stall-watchdog.md` (linked from § Recovery → Built-in Stall Watchdog) for deriving `--team-dir` from a spawn's own `agent_id` instead. diff --git a/skills/codex-crew/references/sandbox-and-recovery.md b/skills/codex-crew/references/sandbox-and-recovery.md index 4c8795c..1084820 100644 --- a/skills/codex-crew/references/sandbox-and-recovery.md +++ b/skills/codex-crew/references/sandbox-and-recovery.md @@ -9,7 +9,7 @@ Codex CLI supports three sandbox modes: | Mode | Behavior | |---|---| | `read-only` (default / review) | No writes; used for review/diagnosis runs. | -| `workspace-write` | Writes allowed under cwd + configured `writable_roots`; network disabled unless `network_access = true`. `codex:codex-rescue` uses this for `--write` tasks. | +| `workspace-write` | Writes allowed under cwd + configured `writable_roots`; network disabled unless `network_access = true`. Used for any `--write` `task` dispatch, direct or via `codex:codex-rescue`. | | `danger-full-access` | No sandbox. Not used by claudius dispatch. | ## `workspace-write` Config (this host) @@ -75,13 +75,13 @@ Per-job `.json` fields worth reading: `id`, `status` (`pending` | `running` | `c - Parse an individual `jobs/.json` only when its mtime advanced since the last poll, and extract only the few fields above. - Keep a bounded per-job last-seen map (`job-id → {status, mtime}`), never accumulated JSON. -Map a monitored worktree to its state dir by matching a job's `workspaceRoot` (or `broker.json`'s cwd) to the worktree path — remember `workspaceRoot` reflects the *dispatching session's* cwd, not necessarily the worktree the job was told to `cd` into. With several teammates dispatched at once, several `jobs/*.json` files land in the same shared state directory; match each to its dispatch by `startedAt` proximity to when that teammate was spawned (seconds apart, in spawn order) and by `result.touchedFiles`, never by `sessionId` — each teammate's job carries its own dispatching session's id, not a value the coordinator can predict or match against in advance. `status: failed` with an `errorMessage` is the signal to surface — that is exactly the class (e.g. the read-only-`.git`/`index.lock` self-commit failure path above) that otherwise goes unnoticed. +For a direct dispatch, `workspaceRoot` is exactly the `--cwd` passed to `task` — map a monitored worktree to its state dir directly by that path. (Only the interactive `codex:codex-rescue` path, which never passes `--cwd`, has `workspaceRoot` instead reflect the *dispatching session's* cwd, not necessarily the worktree the job was told to `cd` into — if several such dispatches share one session cwd, match each to its request by `startedAt` proximity and `result.touchedFiles`, never by `sessionId`, which each carries independently and unpredictably.) `status: failed` with an `errorMessage` is the signal to surface — that is exactly the class (e.g. the read-only-`.git`/`index.lock` self-commit failure path above) that otherwise goes unnoticed. `codex exec --json` also emits a JSONL event stream (`thread.started`, `turn.completed`, `item.completed`, `error`) for foreground runs — an alternative progress signal when not going through the companion's job state. ## Harness Kills of a Backgrounded Task -A `codex-companion.mjs task --write --background` run launched via a `run_in_background` Bash call can be killed by the harness mid-run — confirmed via a tmux pane reading `Background command ... was stopped`. Nothing reports it: `codex:codex-rescue` is a forwarder whose own turn ended at dispatch time and which never polls its background task, so it cannot notice or surface the kill. **Silence is not evidence of health.** +A `codex-companion.mjs task --write --background` run launched via a `run_in_background` Bash call can be killed by the harness mid-run — confirmed via a tmux pane reading `Background command ... was stopped`. Nothing reports it automatically, whether dispatched directly or via `codex:codex-rescue`: the `--background` flag already detaches and returns immediately, so nobody is polling the background task once it's queued. **Silence is not evidence of health.** - **Detect it coordinator-side.** Periodically read the job's actual log content (`jobs/.log`) and inspect the tmux pane directly. Do not infer health from `status` alone — it can sit at `running` after the process is gone. - **On-disk edits survive.** Files Codex already wrote stay written; the work is partial, not lost.