Resolve goal loops that have no predicate (#346) - #350
Merged
Merged
Conversation
A goal loop without --predicate only resolved when the app's pane saw its process exit, which an interactive agent almost never does, so finished graphs sat at IDLE and never fired their edges. - Record how a loop resolved (predicate, backend goal verdict, reported done, human, session exit, workers) and show it on the card. A verdict resolves once; a stale surface report cannot overturn one. - Read each backend's own verdict on the /goal it was launched with: Claude Code's goal_status met, Codex's thread goal complete, Copilot's objective completed. A turn ending is never treated as success. - Add `graphcode node done`, the completion report every backend can send (the only one for OpenCode and pi). A predicate still decides. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- A loop that reports done, or whose backend records its goal met, while loops it created are still unresolved is held and shows WAITING; it resolves once the last of them does. A human's `node done` is not held. - Once a loop resolves and its final report has gone out, its session is ended after a grace set in Settings (default 10 minutes, or never). The transcript is kept, a mid-turn session gets another grace, and opening the loop resumes the conversation instead of starting the goal again. - Giving a resolved goal loop a new goal reopens it: the earlier goal stays recorded, the session resumes with the new one, and its edges are not re-fired. A loop cannot hand itself a new goal. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Bind every completion to the goal it was recorded against: backend verdicts carry their record time and are revalidated after the read, a replaced goal ignores earlier verdicts, and a done report sent before a reopened loop's new goal reaches its session is refused. - A held completion is discarded when the goal or predicate changes, and is applied only if every created loop succeeded; otherwise the leader is told why. - End a resolved session only on a reported idle, confirmed a second time, with no terminal attached; re-arm the ends after a restart. - Resume a resolved loop's session when it is opened (Codex, remote and unbanked panes wait for the daemon), and deliver a reopened goal once. - Run the predicate of a done report off the command stream; match the remote shim's done output; a dead leader is not shown waiting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
scgopi
added a commit
that referenced
this pull request
Sep 13, 2026
PR #350 landed with five [LineLength] violations, so `make check` exited 2 on main. Formatter output only — five line wraps, no semantic change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016n4hSXVu6wLR4RCfnt6PPG
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #346.
A goal loop without
--predicateonly resolved when the app's pane saw its process exit, which an interactive agent almost never does — so a finished graph sat at IDLE, its edges never fired, and its sessions stayed resident. A turn ending is still never treated as success: loops waiting on mail, CI or their children end turns too.What changes
How a loop resolved is recorded. The card says whether a predicate passed, the backend recorded its goal as met, the loop reported done, or a human marked it done. A verdict resolves once; a stale report cannot overturn it.
Backends' own goal verdicts. GraphCode already launches goal loops with
/goal. The daemon now reads the verdict each backend records, dated so an earlier goal's verdict never counts for a replacement:goal_statuswithmet: truecompletecompletedgraphcode node donegraphcode node done <path> <id> [result…]. The completion report every backend can send, locally and from remote hosts. A predicate, when set, still decides.Leaders wait for their workers. A leader that reports done while loops it created are unresolved shows WAITING and resolves once they all succeed. If one did not, the report is discarded and the leader is told why.
Finished sessions are freed, loops are kept. After a loop resolves, its session is ended once it is confirmed idle with no terminal attached (Settings → End a finished loop's session: 1 min / 10 min / 1 hour / Never; default 10 minutes). The transcript stays. Opening the loop resumes the conversation and never starts the met goal again. Giving it a new goal reopens it without re-firing its edges.
Review
Independently reviewed by a Codex loop and a Claude Code loop before this was opened. Every finding was fixed; the five failing reproductions from the Codex review are kept as
GoalResolutionReviewTests.Known limits
node donecomes from$ZMX_SESSION; a worker without it is treated as a human.