Repository navigation
🤖 fix: bind goal edits and complete_goal to the intended goal; heartbeat skips transient goal snapshots - #5636
Merged
Merged
Conversation
…t skips transient goal snapshots
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Member
Author
Readiness record (issue coordinator)
Generated with |
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.
Summary
This PR fixes the three "suspected" items left in #5461. Each one is confirmed on main, and each has a regression test that failed before the fix.
complete_goalwithout agoalIdon an automatic goal turn now targets the goal the turn was dispatched for./goal) no longer retry a conflict against the replacement goal.transientGoalOnlyactivity snapshots.Fixes #5461
Background
G4 of #5461 was fixed in #5536. The triage of the three remaining items is in the issue comments. All three are single-backend defects with small fixes.
Implementation
stream-startlistener's unawaitedstreaming=truemetadata write lands (workspaceService.tsaround :4381),WorkspaceGoalService.setGoalpersists a user's goal replacement at once instead of queueing it.complete_goalbound no goal id, so a continuation turn for goal A could complete A's replacement B.GoalToolContext.goalIdnow carries the goal an automatic goal turn was dispatched for. The path isinternal.goalId→streamWithHistory/prepareRolloverRequest→turnRequestBuilder(goalTurnGoalId). It is set only whengoalTurnKindis set.complete_goaluses it asexpectedGoalIdwhen the model passes none. A replaced goal now fails withgoal_conflict.goal_conflictonly when the goal id changed.setGoalWithConflictRetryre-read the goal and retried, so a Pause, budget edit, rename or completion clicked for goal A landed on its replacement B.setGoalForIntendedGoal. The sidebar and palette pass the goal they displayed (intendedGoalIdOf). A goal still pending persistence has no durable id yet, so for it the helper reads the current goal.HeartbeatService.handleActivityEventnow also returns ontransientGoalOnly, askeepAwake.tsalready does. These snapshots republish the last baseline, whose stalestreaming:falsepushed back the idle countdown.Design choice (fail closed, smallest scope): on a conflict the user retries the edit. No edit is ever re-targeted.
Validation
These tests failed before the fix:
goal.test.ts"complete_goal without goalId on a goal turn refuses a goal replaced after dispatch": the tool completed the replacement goal ("Expected tool execution to fail").setGoalForIntendedGoal.test.ts"returns a conflict without retrying against the replacement goal" ("Expected number of calls: 1, Received: 2"), and "targets the intended goal instead of the goal read at call time" (getGoalcalled 1 time, expected 0).heartbeatService.test.ts"activity event ignores transientGoalOnly snapshots" ("Expected: 300000, Received: 1791140599644").Plumbing tests:
agentSession.goalAutoPause.test.tschecks that the goal id reaches stream requests only for goal turns.aiService.test.tschecks that it reaches the goal tool context only on goal turns.chatCommands.test.tspinned the old retry and now asserts a single attempt plus a restore result.Visual evidence: none. No rendered UI changes. The only user-visible difference is that a goal edit racing a replacement shows the existing conflict error. The GoalTab stories take mocked handlers and do not exercise this path.
Risks
Low.
complete_goalwith a conflict instead of completing the replacement.agentSession.tsgains one forwarded argument per request-builder call. There is no control-flow change.Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:high