feat(mcp): organize thread state#8676

closed
t3code/codex-turn-mapping
agents/mcp-thread-state/organization
openedAug 29, 2026byJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 5d ago

Agents can inspect thread run state through MCP, but they cannot see or change the organization state users manage in the sidebar.

This change adds shell-backed organization fields and filters to thread list/read, plus typed tools for pinning, snoozing, settling, archiving, read state, and deletion. Mutations use the existing serialized V2 commands and return durable resulting state. Archived discovery stays on the shell snapshot path without loading transcripts.

Behavior remains current-project scoped. The V2 decider owns lifecycle rules, and MCP refuses to archive a thread with a pending approval or user-input request. The current V2 settlement behavior, including snooze wake-up ordering, is preserved.

Focused validation:

  • 51 focused tests across contracts, MCP service/toolkit, runtime wiring, presentation, and ThreadSettlementService
  • vp run --filter t3 typecheck
  • vp run --filter @t3tools/contracts typecheck
  • vp run --filter @t3tools/shared typecheck
  • Targeted vp lint, vp fmt --check, and git diff --check

Review base: t3code/codex-turn-mapping at 415ed0f73b97f1655b6282492f81d0b2bba3a9cc. Native stack: #8676 → #8683.

Implemented by GPT-5.6-Sol via Codex in T3 Code.

Note

Add t3_thread_organize and t3_thread_delete MCP tools and extend thread listing with organization state

  • Adds MCP tools for pin, snooze, settle, archive, reorder, and mark-read/unread actions on threads, supporting single or batched requests (max 20 items) with per-item success/failure outcomes
  • Adds t3_thread_delete with idempotent handling and calling-project scope enforcement
  • Extends t3_thread_list and t3_thread_read responses with branch, workspace, pin, settlement, snooze, archive, and read-state fields; read state is computed from the latest run completion watermark without loading transcripts
  • Adds a dispatch guard in makeOrchestrator that rejects constrained thread.archive commands when pending runtime requests exist, leaving archive state and request status unchanged
  • Behavioral Change: listProjectThreads in ThreadManagementService.ts gains an optional includeArchived parameter; default listing still returns active shells only. Archive commands carrying requireNoPendingRuntimeRequests will now raise OrchestratorDispatchError when pending approval or user-input runtime requests exist.

Macroscope summarized 35dfd51.

Synced from GitHub

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 46576861-3c0d-4774-aad4-500d34de2b1d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


Comment @coderabbitai help to get the list of available commands.

packages/contracts/src/orchestratorMcp.tsline 275Outdated · 2b4e46cOpen thread
……@@ -268,6 +269,12 @@ export const OrchestratorMcpThreadListInput = Schema.Struct({
272+ archived: Schema.optional(Schema.Boolean),
273+ pinned: Schema.optional(Schema.Boolean),
274+ settled: Schema.optional(Schema.Boolean),
275+ snoozed: Schema.optional(Schema.Boolean),
MmacroscopeappBoton line 275Aug 29, 2026

🟡 Medium src/orchestratorMcp.ts:275

snoozed: true returns threads whose snoozedUntil is already in the past, so expired snoozes are reported as snoozed instead of active. The list implementation filters only for a non-null snoozedUntil, even though snooze state is time-derived and the field remains populated after wake; filter against the current time when applying this option.

✅ Resolved in 7c0ede794e86d8dbfc36fd58e0f8aa6a870b59fc

apps/server/src/mcp/OrchestratorMcpService.tslines 613–616Outdated · 2b4e46cOpen thread
……@@ -551,11 +602,84 @@ function threadDetail(projection: OrchestrationV2ThreadProjection): Orchestrator
613+ readState: readState({
614+ completedAt: projection.runs.findLast((run) => run.status === "completed")?.completedAt,
615+ lastVisitedAt: projection.thread.lastVisitedAt,
616+ }),
MmacroscopeappBoton lines 613–616Aug 29, 2026

🟡 Medium mcp/OrchestratorMcpService.ts:613

threadDetail reports readState: "read" after a failed, cancelled, or interrupted run follows an earlier completed run, while list state reports the thread as unread. This happens because readState selects only the last run with status === "completed" instead of the newest run's completedAt; use latest?.completedAt so all MCP views use the same watermark.

✅ Resolved in 7c0ede794e86d8dbfc36fd58e0f8aa6a870b59fc

Synced from GitHub

Thread transfer impact

⚠️ The latest CI run did not produce a thread transfer result for 35dfd51.

This comment will update automatically after the next completed run.

docs/orchestration-v2/orchestrator-mcp-server.mdlines 266–267Outdated · 7c0ede7Open thread
……@@ -254,9 +254,17 @@ retry the request.
264+`explicitlySettled` checks only `settledOverride`; it does not reproduce
265+client-local automatic settlement. `hasSnoozeMarker` reports stored snooze
266+timestamps, which remain after timer expiry or an early wake and do not mean
267+the sidebar still hides the thread.
MmacroscopeappBoton lines 266–267Aug 29, 2026

🟢 Low orchestration-v2/orchestrator-mcp-server.md:266

The documentation incorrectly states that snoozedUntil and snoozedAt remain after an early wake, so MCP clients can incorrectly treat hasSnoozeMarker as true after the thread has been unsnoozed. A message.dispatch to a snoozed thread emits thread.unsnoozed and clears both timestamps; please document that early wakes clear the stored marker.

No longer relevant as of 29d7c1e3a885efa566b2218ce6682f26440c77eb

Addressed in 48ed246b88. The docs now distinguish derived timer/raised-hand wakes, which retain the marker, from explicit unsnooze and message dispatch, which clear it.

Synced from GitHub

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — The PR adds agent-facing thread organization and permanent deletion, including operations that can alter durable state, archive work, cancel active activity, or remove threads. It also expands the shared MCP contract and orchestration runtime, making this broader than a low-risk metadata or documentation change.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Macroscope has since reviewed this pull request. An earlier review was skipped by a cost limit; a review has now completed, so that notice no longer applies.

Synced from GitHub

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

Superseded as the active MCP proposal by #10554 in the rebuilt MCP stack, following the maintainer request to review thin service wrappers separately from service hardening.

This is not a full feature-parity replacement. This bottom layer also adds the shared caller/project/mode checks, fixed public errors and core MCP tests reused above. No lifecycle or command-service implementation changes. Deferred organization is not included.

Closing this mixed proposal without merging. The remote branch agents/mcp-thread-state/organization and exact head 35dfd51a7db2f9d98233becc64909798ad848e89 are retained so the service changes can be extracted for separate review later.