Skip to content

v2.15.7

Choose a tag to compare

@nokhodian nokhodian released this 22 Sep 20:21
· 1310 commits to main since this release

[2.15.7] — 2026-09-22

Fixed

  • A role could end its turn with its own task still open and nothing noticed. On the 2.15.6 release run the publisher reported its results with org_send and ended its turn without calling org_task_done; the coordinator waited on a completion that never came and the run sat still for ~10 minutes until a human intervened, because the only backstop was the org-wide idle watchdog (run_config.idle_minutes, 45 in that org). When a role's turn ends the runtime now checks what it left open and delivers one short [task:<id>] STILL OPEN — … message naming the task and what closing it takes (including evidence when run_config.completion_evidence is on), plus a task-open-at-turn-end audit event. It is bounded: nothing is sent while the role still has mail queued or coalescing, a task blocked on a real-world time (org_task_block) is never nudged, and each task earns at most one nudge per dispatch. The idle watchdog's own behaviour is unchanged.
  • A completion notification could name an already-closed task. (Fixes #319) With run_config.session_scope: "task" a role's model session is keyed per task and resumed per task, so a session resumed for a follow-up task still carries the previous, already-closed task in its context. Closing that remembered id used to succeed — nothing in the runtime refused a second close — so with notify_task_creator the creator received a second [task:<already-closed id>] DONE while the task actually in flight stayed running until a human cross-checked org_tasks against the notifications (observed twice on the 2.15.6 release run). org_task_done now refuses a task that has already reached a terminal status, names the caller's open task(s) in the refusal, records a task-already-closed audit event, and leaves the DAG and the closed task's evidence untouched; the notification's tag and title come from the task that just closed.

Changed

  • expectExit can no longer hide failures inside an aggregate command. In the 2.15.6 release run a role put expectExit: 1 on pnpm run test:all:run — a ~7,800-test suite — and the completion-evidence gate accepted it: a suite's exit 1 means "at least one of 7,800 things failed", so the known failure and any new regression are the same exit code and the gate cannot tell them apart. A human had to read the log by hand to confirm only the expected test had failed. org_task_done now refuses expectExit on a command that runs a test suite or an aggregate runner (vitest, jest, npm/pnpm/yarn test scripts, node --test, pnpm -r, pnpm --filter … test, run verify, test:all — a command naming a single test file is not one), and the refusal says to run the failing test file on its own and declare expectExit on that check, or exclude the known failure and record the exclusion. Every other expectExit now requires a one-line expectReason saying why the non-zero exit is correct; the reason travels with the exit code into the refusal, the task's stored result and the review packet (exit 1 (expected 1: 404 = branch not protected)), and each accepted expectExit raises an audit event (evidence-expect-exit) so they can be swept after a run. expectExit: 0 is the default and declares nothing.

  • Two monomind browse open calls running at once fought over one browser. (Fixes #318) With no --port, every open targeted the same default CDP port (9222) and the profile directory derived from it, so two uncoordinated invocations either collided at launch (Chrome exited before the CDP endpoint opened on port 9222) or silently shared one Chrome, where the second navigation aborted the first (net::ERR_ABORTED) and one browse close killed both. Reproduced 5/5 with two concurrent opens; 5/5 pass now, each with its own browser.

    open with no --port now starts its own session: Chrome binds a kernel-assigned free port in a profile directory of its own, and open reports it — ✓ Opened: … [port 41337]. Sessions are recorded one file per port, per working directory, in .monomind/monobrowse/sessions/<port>.json (they shared a single active-port.json before), and every later command follows one rule: --port N acts on that session, no --port acts on the newest session in this directory whose browser still answers. Dead records are dropped as they are passed, so a crashed or manually-killed browser self-heals instead of wedging later commands, and close ends exactly the session it resolved — never another invocation's browser. open --port N and connect --port N keep today's attach-to-that-port behaviour exactly, and each session keeps its own snapshot ref cache so concurrent snapshot/click work does not cross over.

    Upgrading with a session already open does not strand its browser: a directory that still has the old single-session file (.monomind/monobrowse/active-port.json) has it treated as one more candidate — the oldest, so live per-port sessions win. If its browser still answers it is adopted (rewritten as sessions/<port>.json, keeping port, PID and open/connect provenance, old file deleted) and behaves like any other session for snapshot, --port and close; if it does not answer, the old file is just removed. Nothing writes that file again, and adoption never makes a bare open join an existing session. @monoes/monobrowse 1.0.21.

npm: monomind@2.15.7, @monoes/monomindcli@2.15.7, @monoes/monobrowse@1.0.21