Skip to content

🤖 fix: goal follow-ups yield to direct user sends; G4 follow-ups from #5536 - #5585

Merged
ThomasK33 merged 2 commits into
mainfrom
fix/goal-followups-5546-5506
Oct 3, 2026
Merged

ThomasK33 merged 2 commits into
mainfrom
fix/goal-followups-5546-5506

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Summary

Goal follow-ups from #5505 and #5536 (G4). One defect per item:

  1. 🤖 Goal follow-up idle probes miss user sends made directly on AgentSession #5506. Follow-up idle probes miss direct user sends. A user send made on AgentSession itself (for example xum run) arms no WorkspaceService ticket. A goal follow-up redispatched during that send's preflight was admitted, moved the turn, and the user's send was refused. The three idle probes in dispatchPendingCompactionFollowUpIfNeeded now also count the session's own manualSendsInPreflight.
  2. 🤖 G4 goal advancement follow-ups from #5536 #5546 item 1. Concurrent error records consume two resume attempts. recordGoalAdvancementAfterStreamError awaits the auto-retry preference read. If automatic successor work started and failed during that await, both failures handed over a resume. Each record that gets past its early returns now takes a generation number. A record that a later such record overtook is dropped after the read. A failure that records nothing (aborted, retried, plan or compact) does not make an earlier record stale.
  3. 🤖 G4 goal advancement follow-ups from #5536 #5546 item 3. A pre-stream runtime_not_ready failure stranded the goal. This failure takes the runtime branch of handleStreamWithHistoryFailure. That branch emits no stream error event, so handleStreamError's G4 settlement never ran. The failed kickoff then stayed installed and was eligible again at once, with no backoff and no bound. The branch now runs the same settlement: it retires the failed kickoff and records a bounded stream_error resume. runtime_start_failed takes the same branch, but RetryManager retries it, so it records nothing.
  4. 🤖 G4 goal advancement follow-ups from #5536 #5546 item 4. Test for a dequeued manual send that is still preparing. The new test holds the dequeued send in its preflight and checks that the advancement waits for it.

Fixes #5506

Background: the remaining #5546 items

This PR resolves items 1, 3 and 4 of #5546 and leaves that issue open (Refs #5546).

The maintainer's G4 rules are unchanged. A UI pause, an agent pause or completion, a budget or turn limit, a user Stop, and the auto-retry opt-out (errors only) still win over every resume. Resumes keep the bounded backoff: at most GOAL_STREAM_ERROR_RESUME_MAX_ATTEMPTS per failure episode.

Implementation notes

  • hasFollowUpBlockingSendPreflight() = manualSendsInPreflight > 0 || hasExternalSendPreflight(). Only user sends raise manualSendsInPreflight (synthetic !== true and origin manual). The redispatched follow-up is a synthetic automatic send, so its own probe cannot trip itself.
  • Item 4 finding: the preparingQueuedInput check in userInputBlocksGoalAdvancement cannot change the result today. completePreparation clears preparingQueuedInput before finishPreparation publishes idle, and reevaluateGoalAdvancement returns early for any non-idle phase. I instrumented it across 61 session and goal test files: it was only reached during dispose, where closing stops the hand-over anyway. So the new test passes when only that clause is removed. It fails when the preparation guards are removed (the phase check and that clause together):
error: expect(received).toBe(expected)
Expected: 0
Received: 1
(fail) ... G4 (#5546 item 4): a dequeued manual send still in its preflight blocks the advancement

I kept the clause as defense in depth.

Validation

Pre-fix failures (each new test, run against main):

#5506: the user's direct send is refused
585 |     expect((await sending).success).toBe(true);
error: expect(received).toBe(expected)
Expected: true
Received: false
(fail) AgentSession goal safety hooks > a direct session send in its preflight defers redispatched follow-ups (#5506)
#5546 item 1: a second resume is handed over
1003 |       expect(await waitForRequests(requestsBefore + 1, 150)).toBe(0);
error: expect(received).toBe(expected)
Expected: 0
Received: 1
(fail) ... G4 (#5546 item 1): an error record overtaken during its preference read hands over nothing
#5546 item 3: the failed kickoff stays eligible at once
250 |       expect(await service.checkGoalContinuationEligibility(workspaceId, Date.now())).toMatchObject(
error: expect(received).toMatchObject(expected)
-   "eligible": false,
-   "reason": "error_backoff",
+   "candidate": { ... "source": "kickoff", ... }
  • The independent final check found a race in the first version of item 1: the generation bump came before the early returns, so a failed plan or compact successor made the earlier record stale, and no resume was recorded. The second commit moves the bump below the early returns. Its new test failed before that commit (Expected: 1, Received: 0 in "a successor whose failure records nothing leaves the earlier record").
  • Targeted suites: goalAdvancement.test.ts, agentSession.*.test.ts, workspaceGoal*.test.ts, workspaceGoals.formalRepro.test.ts, workspaceService.sendMessage.test.ts: 1497 pass, 0 fail.
  • formal/workspace-goals/check.sh on the head commit (32cebd2): exit 0. Every result matches its expectation (MC_code still violates only NoHeartbeatWhenOff, the open G2 that 🤖 fix: no heartbeat turn after the heartbeat is unset or disabled (G2, G2b) #5549 fixes).

Risks

Medium-low, in goal advancement and the follow-up idle rule.


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high • Cost: $3.13

…5536

- #5506: follow-up idle probes count the session's own manual send preflight.
- #5546 item 1: drop a terminal-error record overtaken during its preference read.
- #5546 item 3: a pre-stream runtime_not_ready failure settles the G4 advancement.
- #5546 item 4: test that a dequeued manual send in preflight blocks the advancement.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-03T21:16:22.705916Z 32cebd2 New commits
🔒 Security Review ✅ Completed 2026-10-03T21:18:09.633263Z 32cebd2 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ThomasK33

Copy link
Copy Markdown
Member Author

Readiness record for head 32cebd2:

  • Review assessments used: 6 of 6. Codex normal and security reviews ran on 28b8581, and both were clean. One independent final check on 28b8581 found a race in item 1, which commit 32cebd2 fixes. Codex normal and security reviews ran on 32cebd2, and both were clean. A fresh independent final check on 32cebd2 recommended "ready with tracked follow-ups".
  • CI: green on this head (./scripts/wait_pr_checks.sh 5585 passed). Locally: make static-check passed, 1497 targeted tests passed, and formal/workspace-goals/check.sh exited 0.
  • Untracked notes from the final check, none blocking: no test sends a retryable runtime_start_failed with a pending retry through the runtime branch. The shared isRetryPending early return covers that case.
  • 🤖 G4 goal advancement follow-ups from #5536 #5546 stays open for item 2 (backlog). Item 5 is by design.

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 3, 2026
Merged via the queue into main with commit 033418c Oct 3, 2026
43 checks passed
@ThomasK33
ThomasK33 deleted the fix/goal-followups-5546-5506 branch October 3, 2026 21:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🤖 Goal follow-up idle probes miss user sends made directly on AgentSession

1 participant