[2.16.2] — 2026-09-24
Changed
- Claude-runtime org roles get a 10-minute Bash timeout. Roles hit Claude Code's 2-minute default Bash timeout on long foreground commands (three times for runtime-qa on the 2.16.1 release run). A Claude role's session env now sets
BASH_DEFAULT_TIMEOUT_MS and BASH_MAX_TIMEOUT_MS to 600000, which Claude Code reads; run_config.bash_timeout_ms (up to 3600000) changes both. Other runtimes are unaffected.
Fixed
- The hooks keep the monograph graph fresh in
npx setups, and say so when they can't. (Fixes #328) The post-edit rebuild imported @monoes/monograph by bare name, which only finds a project-local install, so with the default npx .mcp.json every qualifying edit failed with ERR_MODULE_NOT_FOUND (1,896 copies in one project's build.log), the graph fell 79 commits behind, and past the 50-commit limit the grep gate and [MONOGRAPH] hints switched off without a word. Every hook now resolves the package the same way — the project, the global npm root (standalone or bundled with a global monomind), then the copy the MCP server's npx run left in the npx cache — and imports it by absolute path. With none of those it runs monomind monograph build from the project or PATH; it never runs npx -y. When the hooks still can't rebuild, SessionStart prints one [MONOGRAPH_WARN] line with the graph's lag and the fix (npm i -g --allow-scripts=better-sqlite3 @monoes/monograph when the package is missing or a global install skipped building better-sqlite3), the first prompt after the gate switches off says so once, and a repeated failure adds one line to build.log instead of one per edit (the log also moves aside past 1 MB). A .rebuild-lock or build.lock whose process has exited no longer blocks the next rebuild. doctor has a new Hook graph rebuild row (doctor -c hook-monograph) that fails when the graph is over 50 commits behind and the hooks can't rebuild it, and names a missing package or an unbuilt better-sqlite3.
- A blocked org task is re-checked instead of sleeping until its deadline. (Fixes #329) Nothing external wakes a task blocked with
org_task_block: a background command's completion or a Monitor event only reaches the role's own process, which idle-cycling may already have ended, and npm propagation reaches nobody. On the 2.16.1 release run a builder blocked "until 11:00Z" on a pnpm install that finished four minutes later and sat idle for 28 minutes until an operator stepped in; a publisher waiting on npm propagation idled the same way. The assignee of a blocked task is now woken every run_config.block_recheck_minutes (default 5, at most 60) with [task:<id>] still blocked (reason …) — re-check … and asked to close the task, re-block it or report, until the deadline or the close. A role may pass recheckAfterMinutes (1–60) for one block, and may now re-block a task that is already blocked. The schedule is stored on the task, so it survives process cycling and checkpoint resume. org_task_block's description now says that nothing external wakes a blocked task and that waits belong in the foreground.
- An org role no longer loses its tool calls partway through a long turn. (Fixes #331) On the 2.16.1 release run the publisher's org tools,
Read, and every Bash call that needed a permission decision started failing with Tool permission request failed: AbortError: Stream closed. Bash calls allowed by a static rule kept working, so the role could not report, block or close its task, and it ended its turn with the task still open. The cause was monomind's mailbox stream: the SDK closes the Claude Code process's input when the prompt stream ends, and permission requests and in-process org tools use that same channel. The stream's run_config.session_idle_exit_ms timer started when the SDK asked for the next message, which it does right after receiving the current one, so any turn longer than the idle window was cut off. In task scope, mail for another task arriving mid-turn cut it off the same way. The stream now stays open until the turn's result, and the idle window counts from then. If the channel closes anyway, the first such tool result ends the role's process and resumes the same session in a fresh one with a note saying why (channel-fault audit event, channel-restart status). This is the recovery path bwrap sandbox faults already use, with the same limit of two restarts per task, counted separately from sandbox restarts. After that the coordinator is told once (channel-fault-exhausted).
org_task_done evidence can name its worktree by a relative label. On the 2.16.1 release run all 11 first closes were refused. Roles pinned worktree: "src" (or docs) for the release worktree at .monomind/orgs/release/work/src, and the gate resolved that against the org root, to a directory that is not a worktree. A relative worktree is still resolved against the workspace first. If that path is not a worktree, the gate now uses the one worktree of the repository whose path ends with the label (src, work/src), and the evidence is checked against that worktree's HEAD. A label that fits more than one worktree is refused, and the refusal names each one. Stale-head, unknown-commit and placeholder refusals are unchanged.
- A role that re-closes its own finished task is told the first close worked. When the assignee calls
org_task_done again on a task it already closed and has nothing else open, the refusal now says the earlier close was accepted and nothing else is needed. Before, it said the close "would notify its creator about work that was reported long ago" and told the role to ask for a new task.
monograph watch no longer looks hung when a rebuild is skipped. The watcher's monograph:updated handler called buildAsync() without an onProgress callback, unlike the build/wiki call sites in the same file, so a build-lock collision or an already-fresh-index skip — reported via onProgress?.({ phase: 'skip', ... }) — produced no output at all, and neither did a successful rebuild; watch mode looked permanently hung. It now wires up onProgress so a skip is logged and prints "Rebuild complete." on success.
cleanup --force's orphan reaper narrows a false-positive class under a bwrap sandbox — every org role's actual runtime — but does not fully close it. selectOrphanedSdkPids matched LIVE_SESSION_RE (/\bclaude\b|monomind/i) against the raw cmdline of every ancestor in a candidate orphan's parent chain, so it wrongly treated some orphans as live-parented and skipped them. Two fixes landed in 2.16.2: bwrap's own --ro-bind .../.claude/... --ro-bind .../.monomind/... bind-mount arguments substring-matched the regex, so an ancestor's cmdline is now tested only after bwrap's -- separator between its own options and the wrapped command (36c79c6); and a bwrap-wrapped shell script's incidental mentions of a .claude path or a socket name did the same, so an ancestor is now judged by its own executable name(s) instead of whether some argument or path elsewhere on the line happens to contain those words (935c1c5). A residual gap remains unfixed in this release: a wrapped script whose text merely contains the substrings "claude-agent-sdk" and "--output-format" — unrelated to any real invocation — still falsely shields the orphan from the reaper; a fix (an argv-adjacency check requiring a claude-agent-sdk path token to sit directly next to an --output-format token, instead of a wildcard substring scan) is ready but not included in 2.16.2, tracked in a follow-up issue.
npm: monomind@2.16.2, @monoes/monomindcli@2.16.2