Skip to content

feat(threads): settle working threads once idle on V2 - #15648

Draft
piotrnowakowski wants to merge 8 commits into
pingdotgg:mainfrom
piotrnowakowski:feat/settle-running-thread-v2
Draft

piotrnowakowski wants to merge 8 commits into
pingdotgg:mainfrom
piotrnowakowski:feat/settle-running-thread-v2

Conversation

@piotrnowakowski

@piotrnowakowski piotrnowakowski commented Oct 4, 2026 •

Copy link
Copy Markdown

Problem

Settling a working thread fails, including when an agent calls t3_thread_organize with action: "settle" on its own thread as its final step. The turn is still running during that call. Users need to file the thread immediately while allowing its work to finish.

Change

Rebuilds #14495 on orchestrator V2. Settle on a working thread records a durable, one-shot settleWhenIdleAt intent instead of emitting thread.settled or stopping its provider session. Settle on an idle thread stays the command it is today.

  • A thread counts as working while a run is live, including a queued agent continuation, or while background work that wakes the agent is pending: subagents, monitors, and background tasks. A dev server or other command left running does not hold it, the same as idle Settle on main.
  • Web/desktop and mobile file the row with settled threads and label it Working. The chat status line reads "Settles when its work finishes". Undo, drag, and menu behavior match Settle.
  • The existing V2 settlement service settles the thread with the normal thread.settle, and its provider cleanup, once the orchestrator confirms that work finished. The thread keeps its place on the settled shelf. Startup recovery and retry work even with automatic settlement disabled or keep-active set.
  • A new user message, Un-settle, pin, snooze, or drag back cancels the intent. A question or approval request, a provider error, a failed run, or a turn stopped after it started returns the thread to Active and keeps its notification. A queued delivery dropped before it started does not.
  • The successful completion stays quiet: no sound, toast, desktop notification, or agent-awareness completed.
  • The MCP settle action reaches the same orchestrator path, including when the caller is the running agent. Only its tool description changed.
  • A pull request watch does not hold a filed thread. Since fix(threads): threads watching a PR stay in Working instead of bouncing to the inbox #16204 a watch is listed as background work, but Settle stops watches (fix(threads): stop pull request watches when settling #16095), so the filed thread settles once its agent work ends and the watch stops with it.
  • A held queue does not finish on its own, so it never holds a filed thread. A held user message still blocks Settle, as on main, instead of filing the thread with the message hidden on the shelf. Held automatic runs are cancelled when the thread settles.
  • When a filed thread settles, main's settle cleanup (idle terminals and the project's settle action) runs then, after the agent's work has finished.

Scope and approval

Addresses #13630 and its accepted scope. Replaces the V1 implementation in #14495 following the request to rebuild on V2, and covers the agent self-settle report.

The contract, server, shared client state, web/mobile UI, and notification changes implement this one lifecycle behavior. No new menu entry or automatic-settlement setting is introduced.

Two details differ from the triage text:

  • The triage lists background liveness as blocking. A dev server or a Grok persistent monitor does not hold a filed thread, because idle Settle on main already settles past both. Work that will wake the agent does hold it.
  • Agent awareness omits completed for every settled thread, not only filed ones. Without that, the real settle at the end would still announce the completion that the intent is meant to keep quiet.

Verification

Tested locally on Windows 11 at 8a0df2f6d, which merges main at 611132c17.

  • 948 tests passed across 25 suites: the 17 focused suites plus runtimeLayer and the main suites around settling, pull request sync, and pull request watches. New cases cover a filed thread with a pull request watch settling and ending the watch, a held automatic run not holding a filed thread, and a held user message blocking Settle on a working thread. The earlier cases still cover filing without stopping the session, settling at the filed time, dev servers, subagent and monitor work, failed and interrupted runs, recovery with automatic settlement disabled, optimistic filing, mobile, and notification suppression.
  • main's rejects settling a thread while a run is active test now expects the thread to be filed, which is the behavior this PR adds.
  • I reverted each post-merge fix separately: the held-queue rule in the orchestrator, the PR-watch rule in the settlement sweep, and the PR-watch rule in the client. Each reversion failed only its own tests.
  • Web production build (pnpm exec vp run --filter @t3tools/web build) and server bundle (pnpm exec vp run --filter t3 build:bundle) passed.
  • Scoped TypeScript checks passed for contracts, shared, client-runtime, server, web, and mobile. Touched-file formatting and lint passed.
  • The 8 suites that fail in a full orchestration-v2 run give the same 11 failures on this branch and on main at d8d037eae (attachments, ACP teardown on Windows, mobile branch checkout), none in code this PR touches.
Test command
pnpm exec vp test run apps/server/src/orchestration-v2/SettleWhenIdle.test.ts apps/server/src/orchestration-v2/ThreadSettlementService.test.ts apps/server/src/orchestration-v2/runtimeLayer.test.ts apps/server/src/orchestration-v2/FoundationPersistence.test.ts apps/server/src/orchestration-v2/ProjectionSettlement.test.ts apps/server/src/orchestration-v2/ProjectionStore.test.ts apps/server/src/orchestration-v2/WireProjection.test.ts apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts apps/server/src/orchestration-v2/ShellStream.test.ts apps/server/src/orchestration-v2/legacy/LegacyV1ThreadImporter.test.ts apps/server/src/mcp/toolkits/core.test.ts apps/server/src/pullRequest/PullRequestService.test.ts packages/client-runtime/src/state/threadCommands.test.ts packages/client-runtime/src/state/threadSort.test.ts packages/client-runtime/src/state/orchestrationV2Projection.test.ts packages/client-runtime/src/state/entities.test.ts packages/client-runtime/src/operations/commands.test.ts packages/contracts/src/orchestrationV2.test.ts packages/shared/src/agentAwareness.test.ts packages/shared/src/orchestrationV2PendingBackgroundWork.test.ts apps/web/src/components/Sidebar.logic.test.ts apps/web/src/components/ThreadNotificationCoordinator.test.tsx apps/mobile/src/features/threads/threadListV2.test.ts apps/mobile/src/features/threads/threadOrderAvailability.test.ts

Visual evidence

Web client on Windows 11, against isolated dev servers (vp run dev in each worktree) with real Codex turns that run Start-Sleep in a scratch Git project. Re-recorded after merging main. All files are in this gist.

Before: main at 611132c17 After: this PR at 8a0df2f6d
Settle on a running thread fails on main The running thread is filed on the settled shelf as Working
Settle on a running thread fails with "has active or blocked work and cannot be settled", and the thread stays in Active. The running thread leaves Active and appears on the settled shelf labelled Working.
Filed thread while it works After the turn finished
The filed thread keeps running and shows when it settles The thread settled without a notification
The agent keeps running. The status line under the last message reads "Settles when its work finishes". The thread settled on the shelf ("Settled just now"), and no completion toast appeared.

Settle while working, 2x speed. Full-speed MP4.

Settle a working thread

Agent settles its own thread, 2x speed. Full-speed MP4. The agent called t3-code.t3_thread_organize with {"action":"settle"} during its own turn. The thread was filed while the turn ended and settled once it completed. This is the case from the agent self-settle report.

Agent settles its own thread

Filed thread with a pull request watch, 2x speed. Full-speed MP4. The agent watched this PR with watch_pull_request, then ran Start-Sleep. Settle filed the thread while it worked. When the turn ended, the watch did not hold it: the thread settled and the watch stopped, with the PR still linked.

Filed while watching Settled, watch stopped
Filed thread watching a pull request Settled thread with the watch stopped

Settle a thread that watches a pull request

In an earlier take the watch woke the agent once before the turn ended. The filed thread waited for that queued continuation, then settled and stopped the watch.

Server events for the recorded threads
Settle while working
17:26:23 run.updated                  running
17:26:25 thread.settle-when-idle-set  settleWhenIdleAt 17:26:25
17:27:15 run.updated                  completed
17:27:16 thread.settled               settledAt 17:26:25

Agent settles its own thread
17:28:14 run.updated                  running
17:28:44 thread.settle-when-idle-set  settleWhenIdleAt 17:28:44
17:28:48 run.updated                  completed
17:28:49 thread.settled               settledAt 17:28:44

Filed thread with a pull request watch
17:29:35 run.updated                  running
17:29:48 thread.pull-request-synced   PR #15648 watched
17:29:57 thread.settle-when-idle-set  settleWhenIdleAt 17:29:57, PR #15648 watched
17:30:25 run.updated                  completed
17:30:26 thread.settled               settledAt 17:29:57, PR #15648 not watched

Not checked: native iOS or Android, and the Electron desktop shell, which wraps the same web UI.

Agents: GPT-6 using the Codex harness for the initial implementation; Claude Opus 5.5 using the Claude Code harness for the review fixes, the merge of current main, and visual evidence.

🤖 Generated with Claude Code

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Oct 4, 2026
piotrnowakowski and others added 3 commits October 4, 2026 23:35
Idle Settle settles again when only a dev server is left running; a
thread is filed only while a run is live or background work will wake
the agent. Dropping a queued delivery that never started no longer
returns a filed thread to Active. The intent token, persistent-monitor
and PR-watch holds, and per-item sweep triggers are gone; a filed thread
keeps its place on the shelf when it settles.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…hread-v2

# Conflicts:
#	apps/server/src/mcp/toolkits/thread/tools.ts
unmanbearpig added a commit to unmanbearpig/t3code-unmbp that referenced this pull request Oct 5, 2026
Port the deferred settlement feature from pingdotgg#15648 at
5751538 onto the fork. Manual settlement
records durable intent while work continues; the server fulfills it when
idle, independently of automatic settlement settings and PR discovery.

Keep relay activity visible while background work remains, update runtime
coverage for active and held runs, and verify that explicit settlement
skips branch PR lookup. Preserve the fork's MCP tool scope and keybindings.

Validation: 616 focused tests; server, shared, contracts, client-runtime,
web, and mobile typechecks; scoped lint and export checks compared with
baseline. No new lint or export findings. Browser/device checks not run.

Co-authored-by: piotrnowakowski <61783713+piotrnowakowski@users.noreply.github.com>
piotrnowakowski and others added 4 commits October 7, 2026 12:11
…hread-v2

Keeps both sides in Orchestrator.ts: archive and settle clear the
settle-when-idle intent and end pull request watches. Updates
SettleWhenIdle.test.ts for main's renamed SQL client, persistence and
replay harness modules.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A pull request watch now counts as background work (pingdotgg#16204), but Settle
stops watches (pingdotgg#16095), so a filed thread no longer waits on one until
the PR closes. A held queue only resumes when the user resumes it: a held
user message still blocks Settle, and a filed thread does not wait on
held automatic runs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A filed thread with a pull request watch settles once its run ends and
the watch stops. A held automatic run does not keep a filed thread and is
cancelled when it settles. A held user message blocks Settle on a working
thread instead of filing it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…hread-v2

Settling now runs cleanUpSettledThread (idle terminals and the project's
settle action); the settle-when-idle-set case stays beside it, so a filed
thread runs that cleanup when it really settles. The settled composer
banner became a status line after the last message on main; a filed thread
shows "Settles when its work finishes" there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant