Repository navigation
v2.15.6
[2.15.6] — 2026-09-22
Added
policy.sandbox.denyWritefor org roles. Paths listed there are read-only for the role's shell (OS sandbox) and its file tools; relative paths resolve against the org root, so["."]keeps a role from writing anywhere in the checkout. The release org now uses it — withmode: "required"— for its QA and audit roles, after a QA role rancleanup --forcefrom the main checkout during a release run.
Changed
monomind cleanup --forcekeeps user data unless you also pass--purge-data. Memory stores (data/memory,MONOMIND_MEMORY_PATH,memory/),.monomind/org-memory,.monomind/knowledge,.monomind/orgs,.monomind/org-skills, backups, the monograph database and any other*.dbfile are listed as kept by--forcealone;--force --purge-dataremoves them too, provided they are not git-tracked. The preview (cleanupwithout--force) runs the same plan, so it lists exactly what--forcewill remove or edit, followed by everything it keeps and why.- Completion evidence is checked against every local worktree and branch, not just the org workspace. With
run_config.completion_evidenceon,org_task_doneused to refuse any evidence whoseheadShawas not the org workspace'sHEAD— so an org that builds in a release or per-task worktree (the usual shape) could never close a task honestly. Evidence may now be pinned to the currentHEADof any worktree of the repository or the tip of any local branch, and may name itsworktreeto pin the check to that worktree'sHEADexactly. A commit that is no longer the head of any local work is still refused as stale, and the refusal now lists the current heads. - Completion evidence checks may declare the exit code they expect. On the release org's first run, checks whose correct outcome is non-zero — a branch-protection GET that 404s,
git config --getof an unset key,agent exec --timeout 1s→ 124 — were refused, and roles "fixed" them with|| true, erasing the exit code the gate checks. Each check now takes an optionalexpectExit; it passes iffexitCode === (expectExit ?? 0), a mismatch refusal names the expected and actual codes, and the review packet and the task's recorded result showexit 1 (expected 1). - Evidence refusals say how to close a report task and where to pin out-of-repo checks. A QA task that finishes by reporting failures could not close, because the failing command it found went into
checks. The gate still requires every check to pass; the refusal and theorg_task_donedescription now say that such a task's acceptance commands prove the report exists (e.g.test -s <report>) and its failures go inresultand to the coordinator. Evidence whoseworktreeis a scratch dir (e.g. an installed tarball) is still refused; the refusal now says to pinheadSha/worktreeto the git worktree the tested artifact was built from. monomind --versionnow refreshes a stale update cache in the background. It returns before the startup update check runs, so until now it never refreshed the cache its tagline reads — on a missing or stale cache it stayed silent about new releases until some other command ran. When the cache is missing or older than the 24h check interval,--versionprints its line exactly as before, then reserves the check slot and starts a detached, unref'd child that prints nothing, fetches the latest versions (5s per-request timeout, 20s hard cap, silent when offline) and rewrites the cache, so the next--versionis accurate. It uses the startup check's own gate —CI/CONTINUOUS_INTEGRATION,MONOMIND_AUTO_UPDATE=false, the 24h interval and the daily cap — and--no-update; reserving the slot stampslastCheckfirst, so a refresh that is running or just ran blocks the next one.--version --jsonnever spawns. Cost on the run that spawns: roughly 5-10 ms.
Fixed
monomind cleanup --forcedeleted git-tracked files and the project's memory. It removed a fixed list of paths wholesale —.claude/,.agents/,.gemini/,.opencode/,.codex/,.kimi-code/,data/,memory/,AGENTS.md,GEMINI.md,opencode.json,.mcp.json— without checking that monomind had created them. Run in a real repository it deleted about 1000 git-tracked files (including hand-writtenAGENTS.md/GEMINI.md, which many projects own themselves) and wiped the memory store, org memory, knowledge index and monograph database. Cleanup now builds one ownership-checked plan. It never deletes or edits a git-tracked file (a directory holding tracked files keeps them; only its untracked monomind content goes), and it refuses to delete anything if a git repository is present butgit ls-filesfails. It deletes a path only when monomind demonstrably owns it: untracked.monomind/runtime state andmonomind.config.json, entries listed in.monomind/init-manifest.json,monomind*files in agent-tool directories, aGEMINI.mdstill carrying init's title line, and files whose whole content is monomind marker blocks. From a file that mixes user content with a monomind block (AGENTS.md,CLAUDE.md,GEMINI.md) or amonomindserver entry (.mcp.json,opencode.json), only the monomind part is removed. Everything else, including.claude/settings.json, is kept and listed with the reason.cleanup --forcealso refuses to run in monomind's own source checkout (rootpackage.jsonnamedmonomindwithpackages/@monomind/clipresent).cleanup --forcekilled live agent processes when Claude Code ran as a container's pid 1. Deciding orphan-hood by pid 1's name went wrong both ways: a systemd/init allow-list left real orphans running under tini or bwrap, and the replacement "pid 1 is a shell" deny-list SIGTERMed the live SDK children of anode .../claudepid 1, whose ppid is 1 (reproduced 3/3 with real processes in a PID namespace). Aclaude-agent-sdkprocess is now reaped only when ownership says it is an orphan. It must not be the invoking process, one of its ancestors or one of its descendants. It must share neither the invoking session nor its process group. No live claude/monomind process may sit up its parent chain. And it must no longer be in its parent's session, which is what happens when init,systemd --user, tini, bwrap or any other subreaper adopts it. On Linux this is read from/proc(ppid, process group, session and start time; the start time also guards against a recycled PID). macOS keeps thepsfallback, now with the process-group check, and Windows still never reaps.- An
org_task_donecall with noevidenceobject no longer spends an evidence attempt. On the release org's first run, 4 of 6 tasks lost an attempt towardmax_evidence_attemptsbecause the role's first call simply omittedevidence— a formatting slip, not a failed proof — leaving them one refusal from escalation. Such a call is still refused and requeued, and the returned notice says it did not count; failed checks, a stale sha and an unknown worktree still count. - The no-progress alarm no longer fires for a role that has nothing to do.
role "publisher" has been running for 30m without a single bus event — it is hooked but producing nothingwas emitted for a role with no task whose process had exited onsession_idle_exit_msand was parked waiting for mail. The alarm now only considers a role with a running task, undelivered mail, or a turn in progress (not parked on its mailbox); a role with a running task or pending mail that emits nothing still trips it. - monodesign URL detection intermittently failed with "monobrowse: CDP connection closed" when browsers launched concurrently. Each detection launch picked its own CDP port (random in 9520-9899, "free" per a probe made before Chrome bound it), so two concurrent launches — e.g. test files run in parallel by
node --test— could pick the same port. The second Chrome then did not fail: it loggedbind() failed: Address already in use, silently listened on[::1]:<port>instead, and monobrowse (polling127.0.0.1:<port>) accepted the other launcher's Chrome as its own. When that owner closed its browser, this launch died mid-connect, and its own Chrome was left running.launchBrowser({ port: 0, userDataDir })(monobrowse 1.0.18) now has Chrome take a kernel-assigned port and reads it fromDevToolsActivePortin its own profile directory, so the endpoint is always the process it spawned; monodesign 1.2.18 uses it for every launch without a forcedMONODESIGN_MONOBROWSE_PORT. - Two concurrent
monomind browse opencalls (or any two port-lesslaunchBrowser()callers) reliably failed one of the two with "Chrome exited before the CDP endpoint opened on port 9222 (code=21, signal=null)". The port-scan probed each candidate with a TCP connect before spawning Chrome on it, which only proves nothing was listening a moment ago — two racing callers could both see the same candidate as free and both spawn Chrome there, so one lost the real bind and its launch threw instead of moving on. A launch that loses this race now checks whether something else is now listening on that candidate and, if so, retries the next one exactly like an already-occupied port, instead of failing outright. Also fixed a related bug the retry uncovered: process tracking used forcloseBrowser()'s kill fallback recorded a spawned pid before confirming it had actually bound the port, so a racing loser's about-to-exit pid could overwrite the winner's entry; tracking now happens only once, at confirmed success. monomind search <query>(andsearch --type code) printed a bare "No results found." on a fresh project with no indication of what to do next. Thecodecapability searches the monograph knowledge graph's on-disk database, which only the explicitmonomind monograph buildpopulates — the directory scansearchauto-runs on first use just records that code files are present, it doesn't index their content. So a fresh project legitimately returns zero code results, indistinguishable from "no matches" on the user's side. When thecodecapability is active, results are empty, any--typefilter is unset orcode, and the index database doesn't exist yet,searchnow prints a one-line hint: "Hint: the code index has not been built yet. Runmonomind monograph buildand search again."
npm: monomind@2.15.6, @monoes/monomindcli@2.15.6, @monoes/monodesign@1.2.18, @monoes/monobrowse@1.0.19