Repository navigation
2.37.1
-
68f606a: Auto-continue and auto-restart no longer report a pull request or issue description edit that never happened (issue #2293). Feedback detection used to treat a REST
updated_atlater than the last commit as "description was edited", but GitHub bumpsupdated_atfor comments, labels, cross-references and pull request creation, and real edits after the last commit were usually made by the previous solver session under the same account. The check now reads the GraphQL edit history (userContentEditsfor descriptions,RenamedTitleEventfor titles), ignores bot edits and edits by the solver's own account inside a solver session window (derived from the session marker comments and the pull request creation time), and reports each remaining edit with its evidence: time, editor and a diff excerpt. When there is no separate linked issue (the issue number falls back to the pull request number), the pull request is no longer checked a second time as its own issue, and the auto-restart loop no longer watches the pull request's own title and description as issue feedback. Every auto-restart reason now comes with evidence. The "Auto-restart N/M" comment used to say only "CI failures detected" or "Issue title and description edited", so nobody could check from the pull request whether a restart was justified. It now lists each failing check with the URL of its run, each triggering comment with its author, time and link, and each issue title or description edit with what changed. The same lines are written to the log. When the same checks fail on consecutive restarts, both the comment and the next session's prompt say so, and the session is told to re-read the failing job logs and change approach. When the same checks fail with the same output on 3 restarts in a row (URLs, timestamps, commit ids and durations are ignored), the run stops as a failure instead of restarting again: the pull request goes back to draft, uncommitted work is committed, and a stop comment quotes the repeated output. A check whose output changes still counts as progress, and a check without output is never treated as repeated.HIVE_MIND_CI_FAILURE_REPEAT_STOPsets the count, and0turns the stop off. Under--verbose, Codex'sRUST_LOG=debugtracing no longer fills a third of the solve log: TRACE/DEBUG/INFO lines go to a<log>.codex-trace.logfile next to it, each distinct WARN or ERROR line is logged once with its repeat count at the end, and Codex's compaction diagnostics still read every line. The watch loop's probes (the pull request's comment threads, the account login,.merged,.stateandmergeStateStatus) are read without being mirrored, so the raw comments JSON (180 KB on link-foundation/meta-language#196) no longer lands in the log on every check. A task container that is kept after the task (HIVE_MIND_KEEP_TASK_CONTAINER) no longer holds gigabytes of regenerable build output: once the workspace has no uncommitted changes and nothing unpushed, itsCACHEDIR.TAGdirectories (Cargotarget/) andnode_modules/.cacheare deleted, and the log says what was freed or why the caches stayed. The configuration docs now explain thatkeepContainer false/keepContainerOnFail falsein$ --statusis start-command's default, which keeps a container whose command exited non-zero.$ --statusshowing thecontainerIdof a removed container, and options printed as[object Object], are reported upstream as link-foundation/start#225 and link-foundation/start#226.A failed
$ --statusquery no longer ends the tracking of a work session (issue #2303). Every failure of start-command used to look like a missing record, so a single transient failure, or a$that could not write its own store on a full disk, posted "Work session ended, but its exit status is unknown" with no kill diagnosis and no automatic resume. A failed query is now told apart from a missing record. The session stays tracked for five consecutive failures (the streak is persisted and cleared by the next answer), and a written log footer still settles the outcome at once. After that, a docker session is judged bydocker inspect: a running container stays tracked, an exited one uses docker's own exit code (OOMKilledcounts as a kill), and a container that is gone without an exit footer is reported as killed. That kill gets the kill diagnosis and the default--on-session-kill=resumerecovery. Other backends use the existing liveness probe. A docker work session whose exit docker never observed is now reported as a kill instead of a success. When start-command's watcher lost a still-running container (for exampledocker logs -ffailing on a full disk), the record saidexecutedwith exit 0 (or-1withexitReason: watcher-lost-containeron newer start-command), and Hive Mind announced "Work session finished successfully". Such an exit (endTimeSource: observed-atorwatcher-lost-container) is now reclassified as killed, so the kill diagnosis, the pull request notice and the default--on-session-kill=resumerecovery run. A-1log footer no longer skips the container liveness check. The monitor also samples the host disk while a session runs, and anENOSPCline in the log counts as evidence, so the diagnosis and the Telegram headline can name the real cause ("killed (disk full)"). A container that start-command already removed is no longer reported as "Docker container kept".Require complete issue delivery before automated merges (#2406): every tool's system, initial and continuation prompts (including the minimal claude resume prompt) now state the complete-delivery goal and ask for a requirements report; Codex keeps
features.goals=truein both the direct and agent-commander paths regardless of--auxiliary-model-calls-disabled; the shared merge path fails closed unless the pull request description holds a requirements report tied to the current head SHA and to a digest of the issue, its comments and all pull request feedback, withdonestatus and evidence for every criterion, and it merges with--match-head-commit; after a solve, bounded completion restarts run without any optional flag until the report passes.Install jq in every hive-mind Docker image built from a Box base (Dockerfile, Dockerfile.dind and coolify/Dockerfile; Dockerfile.formal-ai and Dockerfile.e2e inherit it) when the base lacks it, and check it in scripts/verify-docker-image.sh so an image without jq fails verification. AI tools'
… | jq …commands and the solver's printed "Raw command" (… | jq -c .) no longer exit 127 withjq: command not found(issue #2846).Kill recovery of a task started with a Telegram alias such as
/codexrunssolve, never the alias (issue #2887). A regression test replays the router#728 incident and checks both the in-place$ --resumecommand and the fresh-launch fallback. If an in-place recovery session still exits with 126 or 127 and that exit is only noticed when the session completes, the session is no longer dropped. Recovery starts again as a fresh isolated session under the same attempt number. A fresh session that fails this way is not retried again. The completion message for an exit of 127 or 126 now says "command not found" or "command not executable" in every locale. A recovery container's own exit 127 is still reported as a failure, not as its predecessor'soom-killed.Kill recovery in a new Docker container can now resume Codex threads (issue #2888). solve writes Codex rollouts from its repository-scoped
CODEX_HOMEto the mounted~/.codex/sessionsdirectory, so a recovery container can find them. For runs started before this change, recovery copies the thread's rollout out of the killed container. If the thread still cannot be found, recovery starts a new Codex session on the same branch and says so in the recovery notice, instead of launchingcodex exec resumethat would fail at once with "no rollout found". solve also starts a new session, with a warning, when it is asked to resume a thread whose rollout is not in its scopedCODEX_HOME. A recovery session that solve refuses to start for lack of disk space (exit 75) no longer uses up a recovery attempt. Once enough space is free, it is relaunched under the same attempt number, at most 6 times per recovery chain. agent, gemini, qwen and opencode keep their sessions inside the container, where no new container can read them, so a fresh recovery drops their--resumeand the notice says so. claude keeps its--resume, because its transcripts are in a mounted folder, except under--use-router. Two solves of the same repository that share the Codex sessions folder at the same moment no longer fail with EEXIST: the second one uses the link the first one made.In-place kill recovery now copies less and cleans up after itself. Containers started by start-command 0.37.0 or later resume with
docker startand skip the snapshot queue and disk wait entirely, and the notice says "no snapshot needed". Older containers that Hive Mind started behind its fail-closed start gate are briefly started while the gate still holds them idle, so their regenerable build caches (directories tagged withCACHEDIR.TAG, such as Cargotarget/, andnode_modules/.cache) can be deleted before the commit. Source trees are never touched. The notice then reports "Removed X GiB of regenerable build caches" and "Snapshotting X GiB (Y free, Z needed)". Once the snapshot-derived container has started, the stopped original is removed. When the task's last container is removed, itsstart-command-resume/*snapshot images are removed too. A killed session's completion no longer removes the container that adocker startrecovery is now running in. The snapshot images of a container that start-command already removed are removed too. In-place kill recovery no longer fills the disk when several task containers are killed at once. Container snapshots (docker commitof the stopped container's writable layer) now run one at a time, and each starts only when the Docker data root has twice the writable layer plus 10 GiB free. While a recovery waits, its notice says so ("Waiting for N other container snapshots to finish first" or "Waiting for disk: X free, Y needed"); if the space does not appear within ten minutes, the recovery starts a fresh isolated session instead of snapshotting. A layer whose size cannot be measured still has to leave the 10 GiB reserve free (issue #2889).Kill-recovery notices, completion links and log attachments now use the session's own pull request. When an issue is linked to several pull requests (for example a parent plan pull request that lists its sub-issues), the bot picks the one on the
issue-<n>-*branch of the session's issue and never one whose branch belongs to another issue; the session-log fallback prefers thePR URL:line printed by solve over the first pull request mentioned in the log. Without an own pull request, a failed recovery is reported on the session's issue./mergeuses the same choice, so a ready sub-issue is merged through its own pull request, never through an older parent plan pull request that also closes it (issue #2891).The issue-link repair now respects the agent's decisions (issue #3174). Wording such as "#236 to #246 stay open", "#2 and #3 stay open" or "#2, #3 remain open" keeps every listed issue open, not only the first one. The repair checks the pull request's description history and no longer re-adds a sub-issue closing line that the session removed after an earlier repair added it. It reports the conflict once in a pull request comment instead, and the merge guard keeps the merge blocked until someone decides. A negated keyword such as "does not close #N" now gets a warning, because GitHub still closes #N. The repair and post-merge closing treat that issue as kept open, and the guard refuses a merge that GitHub would use to close it. The post-session link repair and the pre-merge guard now add or require
Fixeslines only for issues the pull request is meant to close (with #3282). They skip sub-issues that are already closed, sub-issues created after the pull request was opened (a plan's output), and sub-issues another open pull request closes, including PRs stacked on another branch that GitHub does not link, found in all of an issue's cross-references, not only the first 100. They also skip a primary issue that the description marks with "Part of #N", "Towards #N" or "#N stays open", together with its sub-issues, so a tracking umbrella can be solved one sub-issue at a time. Issues listed in a repository-mode block and issues with an explicit closing keyword stay required. The repair removesFixeslines that an earlier repair added for such issues, and the merge guard refuses a merge that GitHub would use to close them.Repository mode (
/solve <repository-url>) no longer selects status pages as work (issue #3176). Open issues opened by a bot (coverage reports, dependency dashboards), issues with a label from the new--skip-issue-labelsoption (defaulthive-mind:skip,dashboard,status,noneto turn it off) and issues whose body contains<!-- hive-mind:skip -->are left out of the combined issue, its sub-issues and its required closing references, so the pull request no longer closes them and their workflow no longer reopens them into the next run. The combined issue and the log list each skipped issue with the reason;--include-bot-issuesselects bot-authored issues again. A repository whose only open issues are skipped is reported as having no work, and the Telegram preflight applies the same options.The Telegram bot no longer blocks its event loop on file or process I/O while it serves commands (issue #3278): the state lock, model catalogue cache, sidecar and CLI-refresh state, session snapshots, router session drain, session-monitor log scans, Formal AI attribution and the Docker launch path (host path probes, host-path preparation, push-guard install) now use async, timeout-bounded calls, and the bot's docker and gh
$calls are bounded with an AbortSignal. Thehive/require-exec-timeoutlint rule now checks command-stream$calls, including a renamed$andquietProbe($), and reports atimeoutoption that command-stream ignores, both lint baselines record the reason for every remaining entry with an exact count, and a new watchdog test runs every bot command,/fix,/task,/splitand/terminal_watchincluded, against slow stubbed dependencies and fails when the event loop is blocked past its budget. Two waiters that see the same stale state lock break it once and never run together.Formal AI now behaves the same through
--tool claude,--tool codexand--tool agent(issue #3279): commits made during a Claude Code or Codex CLI session carry the same four Formal-AI trailers and the same evidence bundle (with aclaude-stream.jsonlorcodex-stream.jsonlstream) that Agent CLI sessions already had, and the hosted-model guard now checks the model and the generations that really answered instead of rejecting the client name. Prices for models served outside a client's own vendor live in one registry keyed by the served model, so a Formal AI session costs $0.00 (Link.Assistant) in every client, and a non-Anthropic model behind a non-AnthropicANTHROPIC_BASE_URLno longer inherits Claude Code's Anthropic list price.A no-change Formal AI run no longer marks its draft pull request "ready for review (solution draft verified)" (issue #3330). If a run's effect cannot be measured (no baseline, unreadable git, or unreadable comments), the pull request keeps the state it had before the run and gets a "Could not verify the session's effect" notice that says why. A Formal AI non-executed terminal state such as
planned_not_executed, with no commit, now counts as finished without changes (exit code 5) on its own, without reading comments. When/useris forbidden to an installation token, the comment author is taken from GraphQLviewer(<app-slug>[bot]), orgithub-actions[bot]inside GitHub Actions. The readiness gate (getPullRequestChangeStats) now ignoresdev/log/**and AI tool scratch files, using the same predicate as the session verdict. solve no longer announces such a run as SUCCESS, the Formal AI draft and E2E containers receiveGITHUB_ACTIONS, and the next session on an unverified draft gets its feedback instead of being told the previous session failed.The E2E Hello World matrix (issue #3332) now tests the Formal AI version Hive Mind ships (
FORMAL_AI_BOOTSTRAP_VERSION, cross-checked against the Dockerfile pins) instead of the GitHub Releaseslatest. It records the shipped, GitHub release and crates.io versions in the job summary and thee2e-matrix-metadataartifact, and annotates a disagreement between releases and crates.io with a link to formal-ai#1203. Rows on the defaultGITHUB_TOKENask for the workflow underci/, which that token can push, and the harness runs it with act. A rejected workflow push is reported asharness: workflow push rejected (no workflows permission), together with the exact secret a maintainer must set. The summary reports the harness verdict apart from the model verdict, and a red Formal AI row links formal-ai#1209 and formal-ai#1154. solve reports the same rejection on the pull request: when GitHub refused a push because the credential may not create or update a workflow file and the workflow is still missing from the pull request, a notice says which credential permission a maintainer must grant, and a rejection a later push recovered from is not reported.After a merge, Hive Mind now checks every issue the pull request is meant to close and closes the ones GitHub left open, for example after a merge into a branch other than the default one (issues #3354, #3355 and #3357). It waits up to 5 minutes (checks after 10, 20, 40 and 60 seconds) for GitHub on a default-branch merge, then closes each remaining issue as completed with a comment that names the pull request, the merge commit and why GitHub did not close it. The result appears in the log, in the auto-merge comment and in the Telegram merge queue message. Issues the description keeps open ("does not close", "Part of"), issues only a repair block or a sidebar link names, and deferred sub-issues are never closed. This is on by default;
--no-auto-close-linked-issuesorHIVE_MIND_AUTO_CLOSE_LINKED_ISSUES=falseonly reports the open issues./merge, solve's auto-merge, the merge queue, the merged-PR sweeper andclose-linked-issuesnow share this one implementation, which replaces the previous per-path closing code.Pull requests merged outside Hive Mind (the GitHub UI, a person's
gh pr merge, GitHub auto-merge) now get their intended issues closed too (issue #3356). The Telegram bot (your own pull requests) and hive (the monitored repository or owner) run a sweeper every 5 minutes over pull requests merged in the last 24 hours. It skips ones merged in the last 5 minutes, checks each one only once, and stores its cursor in~/.hive-mind/merged-pr-sweeper.json(orHIVE_MIND_MERGED_PR_SWEEPER_STATE). It never touches pull requests merged before it was first enabled. The newclose-linked-issues --pr <url> [--dry-run]command does the same for one pull request and prints what it would close and what it skips and why.close-linked-issues --repo <owner/repo> --since <date>(or--owner) backfills older merges.A closing reference to an issue in another repository that the pull request author wrote in the description (above any repair block) no longer blocks the merge, and the issue is closed explicitly after the merge, because GitHub does not close it (issue #3358). Foreign references that the link repair appended, or that come from a sub-issue tree, still block the merge, as in issue #3276. A sidebar-only foreign link still blocks it too.
--no-allow-cross-repository-closingorHIVE_MIND_ALLOW_CROSS_REPOSITORY_CLOSING=falserestores the previous rule, which blocks every foreign closing reference.After the post-merge check closes an issue, Hive Mind now walks up its parent issues and closes each one whose work is complete (issue #3359). A parent is closed only when four things hold. First, every sub-issue is closed as completed; one closed as not planned or as a duplicate counts only if the parent description says so. Second, the description has no unchecked task-list item and no open "Done when" item. Third, nothing blocks it. Fourth, nobody reopened it after its last sub-issue closed. Otherwise, the parent gets one status comment with the reasons, which is updated instead of repeated. Sub-issues are never closed because their parent closed. This is on by default; turn it off with
--no-close-completed-parentsorHIVE_MIND_CLOSE_COMPLETED_PARENTS=false.Dependencies are current, and CI demands it everywhere a version is declared (issue #3282). The freshness gate now also checks transitive
package-lock.jsonentries thatnpm updatewould move, images held in DockerfileARGs or pinned by digest, image references in source code, the coolify Dockerfile and the example workflows users copy. It ignores prose and commented-out lines, and reads every page of a registry's tags. A hold on an older version must name its open follow-up issue, such as link-assistant/router 0.x (#3395). Formal AI stays just another model:hiveandsolvealso stop an unleased Formal AI sidecar and unload an image unused pastHIVE_MIND_FORMAL_AI_UNLOAD_AFTER, not only the Telegram bot, and a host that never ran Formal AI makes no Docker call for it. Model validation and the runtime share one list of the tools Formal AI runs through. The Telegram bot treats a command argument as a GitHub URL only when it names the github.com host, and the merge queue's MarkdownV2 escaping covers backslashes, also in its CI run links. Claude Code now receives the system prompt, and a resumed session's prompt, exactly as written. Before, every"in them arrived as\", and with command-stream 0.18.0 /bin/sh expanded their$(...)and backticks: every run logged/bin/sh: 1: .png: not found. The command Hive Mind logs (and--dry-runreturns) is now quoted so that it runs as shown. Tests replay commands through the command-stream version production pins (2.4.0, now a devDependency). The 0.17.2 copy that agent-commander 0.11.0 brought in split a command at a quoted|after an apostrophe. This was reported upstream as link-assistant/agent-commander#53, and agent-commander 0.11.1 fixes it by moving to command-stream 2.4.0, so only one copy remains.
Related Pull Request: #3390