Uh oh!
There was an error while loading. Please reload this page.
feat(orchestrator): introduce new orchestrator - #2829
Conversation
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Your free Security trial is over. An organization admin can activate Security or dismiss this notice. Comment |
Uh oh!
There was an error while loading. Please reload this page.
| return decodeTranscript({ | ||
| ...metadata, | ||
| entries, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🟢 Lowtestkit/ReplayTranscriptNdjson.ts:116
The call to decodeTranscript at line 116 invokes Schema.decodeUnknownSync, which throws on validation failure. Since this isn't wrapped in Effect.try, any validation error becomes an uncaught exception (defect) instead of a typed ProviderReplayNdjsonParseError. This breaks the function's declared error contract. Consider wrapping the call in Effect.try to catch the exception and convert it to the declared error type.
- return decodeTranscript({- ...metadata,- entries,- });+ return yield* Effect.try({+ try: () =>+ decodeTranscript({+ ...metadata,+ entries,+ }),+ catch: (cause) =>+ new ProviderReplayNdjsonLineParseError({+ lineNumber: lines.length,+ line: "<transcript validation>",+ cause,+ }),+ });🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file apps/server/src/orchestration-v2/testkit/ReplayTranscriptNdjson.ts around lines 116-120:
The call to `decodeTranscript` at line 116 invokes `Schema.decodeUnknownSync`, which throws on validation failure. Since this isn't wrapped in `Effect.try`, any validation error becomes an uncaught exception (defect) instead of a typed `ProviderReplayNdjsonParseError`. This breaks the function's declared error contract. Consider wrapping the call in `Effect.try` to catch the exception and convert it to the declared error type.
Evidence trail:
apps/server/src/orchestration-v2/testkit/ReplayTranscriptNdjson.ts lines 50-53: `decodeTranscript = Schema.decodeUnknownSync(ProviderReplayTranscript)` — throws on failure.
Line 116-118: `return decodeTranscript({...metadata, entries})` — called directly inside Effect.gen without Effect.try wrapper.
Lines 55-68 (`parseReplayRecord`): same pattern but correctly wrapped in `Effect.try`.
Line 80: function declares error type `ProviderReplayNdjsonParseError`.
packages/contracts/src/orchestrationV2.ts lines 1561-1568: `ProviderReplayTranscript` schema with `TrimmedNonEmptyString` fields that can fail validation.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…der adapters (t3-29f.6) Assessment of upstream PR pingdotgg#2829 (pingdotgg/t3code) from juliusmarminge: WIP wire orchestration v2 provider adapters with Codex and Claude adapters, event sourcing, provider session management, and replay testkit. Relevance to target issues: - pingdotgg#2838 (session resume): HIGH — ProviderSessionManager persists session IDs and separates startSession/resumeSession operations - pingdotgg#2778 (subagent hang): MEDIUM — ProviderEventIngestor provides infrastructure to forward permission events, but UI plumbing not yet wired - pingdotgg#2886 (thread stuck working): HIGH — event-sourced projections replace mutable state flags, eliminating sticky "working" states The PR is a draft (34 commits, not merged). No OpenCode ACP adapter exists yet in v2 — OpenCode would need its own adapter wired into the ProviderAdapterRegistry. Recommend watching for merge and adding an OpenCode adapter post-merge.
…n v2 provider adapters)
The upstream PR pingdotgg#2829 added orchestrationV2 methods to the WsRpcClient interface. The test mock in service.threadSubscriptions.test.ts was missing the orchestrationV2 property, causing a typecheck failure: 'Property orchestrationV2 is missing in type...' Added orchestrationV2 mock with dispatchCommand, getThreadProjection, subscribeShell, and subscribeThread as vi.fn() stubs.
The upstream PR pingdotgg#2829 targets a newer Effect version than our fork's pinned effect@4.0.0-beta.73. Fixes: - Replace Random.nextUUIDv4 with Crypto.randomUUIDv4 (beta.73 API) - Fix deterministic Service tag keys to match fork convention (include file path segments; e.g. Adapters/ClaudeAdapterV2/...) - Replace Schema.decodeSync with Schema.decodeUnknownEffect inside Effect.gen generators (tsgo schemaSyncInEffect rule) - Replace inline Schema.encodeUnknownSync with module-level wrappers to avoid schemaSyncInEffect rule inside generators
🚀 Expo continuous deployment is ready!
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| Effect.gen(function* () { | ||
| const threadId = payloadInput.threadId ?? input.threadId; | ||
| const eventId = yield* idAllocator.allocate.event({ | ||
| threadId, | ||
| providerSessionId: input.providerSessionId, | ||
| }); | ||
| const occurredAt = yield* DateTime.now; | ||
| return yield* Schema.decodeUnknownEffect(OrchestrationV2DomainEvent)( | ||
| compactUndefined({ | ||
| id: eventId, | ||
| type: payloadInput.type, | ||
| threadId, | ||
| runId: payloadInput.runId ?? input.runId, | ||
| nodeId: payloadInput.nodeId ?? input.nodeId, | ||
| provider: input.event.provider, | ||
| rawEventId: input.rawEventId, | ||
| occurredAt, | ||
| payload: payloadInput.payload, | ||
| }), | ||
| ); |
There was a problem hiding this comment.
🟡 Mediumorchestration-v2/ProviderEventIngestor.ts:109
In makeDomainEvent, the ?? operator on lines 121 and 123 treats explicit null as equivalent to undefined, causing payloadInput.runId ?? input.runId to fall back to input.runId when payloadInput.runId is explicitly null. Since the type is readonly runId?: RunId | null, this means explicit null values from the caller (e.g., input.event.node.runId being null on line 172) are incorrectly overwritten instead of preserved. Consider using === undefined checks like lines 161-162 and 210-211, or use payloadInput.runId === undefined ? input.runId : payloadInput.runId.
const threadId = payloadInput.threadId ?? input.threadId;
- const runId = payloadInput.runId ?? input.runId;- const nodeId = payloadInput.nodeId ?? input.nodeId;+ const runId = payloadInput.runId === undefined ? input.runId : payloadInput.runId;+ const nodeId = payloadInput.nodeId === undefined ? input.nodeId : payloadInput.nodeId;🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/orchestration-v2/ProviderEventIngestor.ts around lines 109-128:
In `makeDomainEvent`, the `??` operator on lines 121 and 123 treats explicit `null` as equivalent to `undefined`, causing `payloadInput.runId ?? input.runId` to fall back to `input.runId` when `payloadInput.runId` is explicitly `null`. Since the type is `readonly runId?: RunId | null`, this means explicit `null` values from the caller (e.g., `input.event.node.runId` being `null` on line 172) are incorrectly overwritten instead of preserved. Consider using `=== undefined` checks like lines 161-162 and 210-211, or use `payloadInput.runId === undefined ? input.runId : payloadInput.runId`.
Evidence trail:
apps/server/src/orchestration-v2/ProviderEventIngestor.ts lines 105-106 (payloadInput type with `RunId | null`), line 121 (`runId: payloadInput.runId ?? input.runId`), line 122 (`nodeId: payloadInput.nodeId ?? input.nodeId`), lines 161-162 and 210-211 (codebase uses `=== undefined` pattern elsewhere). packages/contracts/src/orchestrationV2.ts line 358 (`runId: Schema.NullOr(RunId)` on ExecutionNode - confirms null is a valid value), line 857 (`runId: Schema.optional(RunId)` on EventBase - domain event uses optional/undefined, not null). apps/server/src/orchestration-v2/ProviderEventIngestor.ts lines 80-81 (compactUndefined only strips undefined, not null).
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
79031a1 to
4e68dcbCompareUh oh!
There was an error while loading. Please reload this page.
4e68dcb to
c7539b9CompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| function nativeThreadId(provider: ProviderKind, thread: OrchestrationV2ProviderThread): string { | ||
| const id = thread.nativeThreadRef?.nativeId; | ||
| if (id === null || id === undefined || id.trim().length === 0) { | ||
| throw new ProviderAdapterProtocolError({ |
There was a problem hiding this comment.
🟡 MediumAdapters/AcpAdapterV2.ts:271
When nativeThreadId is called inside Effect.gen generators (e.g., lines 899, 1813), the thrown ProviderAdapterProtocolError becomes an untyped defect instead of a typed failure. This bypasses Effect.mapError and other typed error handlers, causing the error to propagate as an unexpected defect. Consider converting nativeThreadId to return Effect<string, ProviderAdapterProtocolError> and yielding it at each call site, or inlining the validation with yield* new ProviderAdapterProtocolError(...) so the failure is properly typed.
Also found in 1 other location(s)
apps/server/src/orchestration-v2/ThreadManagementService.ts:278
The statement
return yield* managementError(...)cannot work correctly becausemanagementError()returns aThreadManagementErrorinstance, not anEffect. Theyield*operator inEffect.genexpects an Effect value. This should bereturn yield* Effect.fail(managementError(...)). The correct pattern is demonstrated elsewhere in this file (lines 241-246) whereEffect.fail(managementError(...))is properly used. This same bug pattern repeats at lines 291, 333, 357, 374, 390, 406, and 427.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts around line 271:
When `nativeThreadId` is called inside `Effect.gen` generators (e.g., lines 899, 1813), the thrown `ProviderAdapterProtocolError` becomes an untyped defect instead of a typed failure. This bypasses `Effect.mapError` and other typed error handlers, causing the error to propagate as an unexpected defect. Consider converting `nativeThreadId` to return `Effect<string, ProviderAdapterProtocolError>` and yielding it at each call site, or inlining the validation with `yield* new ProviderAdapterProtocolError(...)` so the failure is properly typed.
Evidence trail:
1. nativeThreadId function with throw: apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts lines 268-277
2. Call site inside Effect.gen: apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts line 899
3. Call site inside Effect.gen: apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts line 1813
4. Correct yield* pattern for comparison: apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts line 1808
5. ProviderAdapterProtocolError class definition: apps/server/src/orchestration-v2/ProviderAdapter.ts lines 316-327
6. Effect.gen implementation delegating to fromIteratorUnsafe: https://github.com/Effect-TS/effect-smol packages/effect/src/internal/effect.ts lines 1104-1125
7. fromIteratorUnsafe calling iter.next() without try/catch: https://github.com/Effect-TS/effect-smol packages/effect/src/internal/effect.ts lines 1285-1307
8. FiberImpl.runLoop catch block converting thrown errors to exitDie: https://github.com/Effect-TS/effect-smol packages/effect/src/internal/effect.ts lines 646-650
9. die = exitDie producing Effect<never> (untyped): https://github.com/Effect-TS/effect-smol packages/effect/src/internal/effect.ts line 947
Also found in 1 other location(s):
- apps/server/src/orchestration-v2/ThreadManagementService.ts:278 -- The statement `return yield* managementError(...)` cannot work correctly because `managementError()` returns a `ThreadManagementError` instance, not an `Effect`. The `yield*` operator in `Effect.gen` expects an Effect value. This should be `return yield* Effect.fail(managementError(...))`. The correct pattern is demonstrated elsewhere in this file (lines 241-246) where `Effect.fail(managementError(...))` is properly used. This same bug pattern repeats at lines 291, 333, 357, 374, 390, 406, and 427.
R12 follow-up Insert persistent feedback blocks by their timestamp within the canonical timeline while preserving projected row order. Keep real optimistic sends appended and suppress duplicate local messages already committed by the server. Implemented by GPT-5.6 Sol via Codex.
R20 follow-up Give inspector reasoning markdown its projected source thread and retain the explicit environment fallback for proposed plans without a thread reference. Workspace links and images now resolve through their owning environment after removal of the active-environment fallback. Implemented by GPT-5.6 Sol via Codex.
Read the inclusive cursor, a full history page, and a look-behind row so older history does not terminate after one page. Finding: P01 pagination termination Model: GPT-5.6 Sol via Codex
Cancel pending SDK requests before aborting the native session. Preserve per-admission cancellation state and treat stopped initial prompts as interruption instead of provider failure. Finding: R14 prompt cancellation Model: GPT-5.6 Sol via Codex
Keep the original cursor owner through ancestor traversal and preserve the history budget across empty intermediate forks. Verify exact paged history against the complete nested projection. Finding: P01 nested lineage Model: GPT-5.6 Sol via Codex
Retain pending admission after transient status failures and use one generation-owned retry worker. Ignore stale timers and duplicate evidence so older prompts cannot finish newer steering. Finding: R14 status reconciliation Model: GPT-5.6 Sol via Codex
Keep hidden local and inherited rows from consuming history pages. Preserve stop-request dependencies, source-run cutoffs, and imported history while loading related metadata from the selected cohort and using indexed watermark lookups. Finding: P01 bounded history visibility Model: GPT-5.6 Sol via Codex
Adapt grouped tool summaries and the floating working timer to V2 run, attempt, and queue state. Bring over the composer, keyboard, and disclosure transitions while retaining the V2 activity inspector and queue controls. Keep OV2 web composer and grouping behavior intact; share only the existing command label parser with mobile.
Restores main features dropped by the policy replay: #8569 theme wiring, settings search rework, #8803 workspace-mutation refresh (v2-adapted), video + image previews (web and mobile, v2-adapted), #8862 Expo glass, and the round's docs. Timeline thinking rows (#8984) stay on the v2 work-live system. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The v2 equivalents of main's #8984 and #8922: a "Working for ..." header anchors the active run, the trailing live tool row survives between actions in past tense instead of vanishing, and a shimmering Thinking row marks reasoning gaps. During workspace preparation the header shows "Setting up worktree..." (driven by the local dispatch flag or the v2 run's preparing status, so remote viewers see it too), the composer footer span is gone, and draft promotion waits until the run starts or startup fails instead of navigating mid-preparation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adopts the round's main features into the v2 architecture: the #9023 media rework (streamed videos, media-file assets, protocol-relative links), #9098 shared live-activity row folded into the v2 working and thinking rows, the #9084/#9078 Claude model catalog for v2 consumers, a native #9005 OpenCode child-session abort in the v2 adapter, #9013's landed LegendList patch, and per-environment sidebar provider entries. For #8600 the server-side pieces land, but auto-settle evaluation stays client-side (reading the new server-owned settings) until the v2 orchestrator grows its own settlement reactor; main's v1-only reactor and coalescer additions are dropped with the rest of the v1 path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
composerVideos and composerOtherFiles are computed but never rendered — the attachment list below still maps composerFiles (line 4034) into the generic FileIcon row, so ComposerVideoThumbnail (line 176), the new PlayIcon import (line 305), and attachVideoThumbnail / isPreviewableComposerVideo have no live consumer here. The result is a split treatment for the same attachment: a staged video is a plain file row in the composer, while the sent message renders it as a playable tile (MessagesTimeline.tsxuserVideos). The three unused locals will also trip unused-symbol checks.
Smallest fix: render composerVideos as thumbnail tiles (ComposerVideoThumbnail + buildExpandedImagePreview, matching the timeline tile) and feed the existing file-row list from composerOtherFiles; if the composer treatment is not landing in this PR, drop the two locals, ComposerVideoThumbnail, and the PlayIcon import instead so the file does not carry a half-wired path.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
composerVideos and composerOtherFiles are computed but never rendered — the attachment list below still maps composerFiles (line 4034) into the generic FileIcon row, so ComposerVideoThumbnail (line 176), the new PlayIcon import (line 305), and attachVideoThumbnail / isPreviewableComposerVideo have no live consumer here. The result is a split treatment for the same attachment: a staged video is a plain file row in the composer, while the sent message renders it as a playable tile (MessagesTimeline.tsxuserVideos). The three unused locals will also trip unused-symbol checks.
Smallest fix: render composerVideos as thumbnail tiles (ComposerVideoThumbnail + buildExpandedImagePreview, matching the timeline tile) and feed the existing file-row list from composerOtherFiles; if the composer treatment is not landing in this PR, drop the two locals, ComposerVideoThumbnail, and the PlayIcon import instead so the file does not carry a half-wired path.
Posted via Macroscope — UI Consistency
| !hasContent && | ||
| (props.selectedThread.session?.status === "running" || | ||
| props.selectedThread.session?.status === "starting"); | ||
| const showStopAction = props.canStopThread; |
There was a problem hiding this comment.
Stop hides send while composing
High Severity
showStopAction now follows canStopThread alone and no longer requires an empty composer. While a run is interruptible, typed text or attachments still replace Send/Queue with Stop, so follow-ups cannot be sent or queued until the run is interrupted.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 7fee604. Configure here.
| selectedThreadFeed, | ||
| selectedThreadKey, | ||
| ]); | ||
| }, [props.onSendMessage, selectedThreadKey]); |
There was a problem hiding this comment.
Send always re-anchors the feed
Medium Severity
handleSendMessage now always sets anchorMessageId to the just-sent id. The previous first-message-only policy in resolveThreadFeedSubmissionAnchor is no longer applied, so follow-ups and second outbox sends retarget the live-follow scroll.
Reviewed by Cursor Bugbot for commit 7fee604. Configure here.
| // Dictation keeps that focus while the composer switches to its compact pill. | ||
| const composerBottomInset = ( | ||
| Platform.OS === "android" ? isKeyboardVisible : composerExpanded || composerFocused | ||
| ) |
There was a problem hiding this comment.
iOS composer inset ignores focus
Medium Severity
iOS composerBottomInset now keys only on composerExpanded, and the editor no longer updates composerFocused. Focusing the collapsed pill still applies home-indicator padding above the keyboard, leaving a gap between the composer and the IME.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 7fee604. Configure here.
…ator Ports #8600's server-owned settlement to orchestration v2 instead of keeping client-side evaluation. A ThreadSettlementService sweep runs at startup, on auto-settle settings changes, and once per minute: it evaluates inactivity and merged or closed pull requests over v2 thread shells and dispatches the new guarded thread.auto-settle command, which rejects threads that changed after the sweep's snapshot or carry any explicit override, then reuses the orchestrator's settle lifecycle. With the server deciding, the clients drop their effectiveSettled evaluation and partition on the persisted settledOverride like main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(Posted at PR level: an inline comment could not be anchored on this file in the current diff.) The The conventions ask that failures be distinguished by stable structural attributes with the message derived only from them, and that a translation boundary wrap only genuinely lower-level failures rather than flatten known ones. Consider having Posted via Macroscope — Effect Service Conventions Posted via Macroscope — Effect Service Conventions |
(Posted at PR level: an inline comment could not be anchored on this file in the current diff.) The The conventions ask that failures be distinguished by stable structural attributes with the message derived only from them, and that a translation boundary wrap only genuinely lower-level failures rather than flatten known ones. Consider having Posted via Macroscope — Effect Service Conventions Posted via Macroscope — Effect Service Conventions |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Bugbot Autofix is ON, but a cloud agent failed to start.
Reviewed by Cursor Bugbot for commit 6081201. Configure here.
| @@ -2085,12 +2062,9 @@ export default function Sidebar() { | |||
| // memo exactly at the next wake boundary. | |||
| void snoozeWakeTick; | |||
| const preciseNow = new Date().toISOString(); | |||
| const visible = threads.filter( | |||
There was a problem hiding this comment.
Web sidebar pinned threads partition completely broken
High Severity
The partition loop declares const pinned: EnvironmentThreadShell[] = [] at line 2068 but never pushes anything into it. The old code had an else if (thread.pinnedAt != null) { pinned.push(thread); } branch between the snoozed and settled checks, but it was removed in this refactor. All pinned threads now fall into active instead. The mobile equivalent in threadListV2.ts correctly retains else if (thread.pinnedAt != null) { pinned.push(thread); } at line 421. This means pinnedThreads is always empty, the pinned section never renders, DnD reordering is inert, and reorderablePinnedKeys is an empty set.
Reviewed by Cursor Bugbot for commit 6081201. Configure here.
| thread.session?.status === "running" && | ||
| thread.session.activeTurnId != null | ||
| ) { | ||
| if (action === "archive" && threadRuntimeIsActive(thread.runtime)) { |
There was a problem hiding this comment.
Archive guard stricter than old threadCanArchive behavior
Medium Severity
The comment says "Archive keeps its original, narrower guard" but threadRuntimeIsActive is actually wider (more restrictive) than the replaced threadCanArchive. threadCanArchive allowed archiving queued threads without an activeRunId and all waiting threads; threadRuntimeIsActive blocks both. The old threadCanArchive is still imported at line 21 but now unused, confirming a mistaken substitution rather than an intentional tightening.
Reviewed by Cursor Bugbot for commit 6081201. Configure here.


Summary
Validation
Notes
Closes
Verified against the branch with code/commit evidence.
High confidence
Closes#4952
Closes#4873
Closes#4775
Closes#4795
Closes#4710
Closes#4668
Closes#4619
Closes#4584
Closes#4561
Closes#4713
Closes#4198
Closes#4452
Closes#3797
Closes#4232
Closes#3666
Closes#3580
Closes#2785
Closes#2789
Closes#3138
Closes#1404
Closes#231
Closes#216
Medium confidence (under review)
Closes#4568
Closes#4766
Closes#4495
Closes#4456
Closes#4399
Closes#3744
Closes#2921
Closes#3624
Closes#3149
Closes#2336
Closes#538
Closes#2173
Closes#2065
Note
Introduce new Orchestration V2 runtime with provider adapters, persistence, and client support
session/latestTurnfields are replaced byruntime/latestRunacross web and mobile thread state; out-of-tree consumers of the old shell snapshot schema or V1 RPC methods will break. Cache schema version bumped to 3, discarding V1 cache envelopes on decode failure. Orchestration protocol version 2 is now required for client connections.Macroscope summarized 6081201.
Note
High Risk
Mobile thread persistence, archive/stop guards, and approval/user-input handling now depend on V2 runtime semantics and a bumped cache schema, alongside removal of PR transfer regression reporting.
Overview
Removes the automated “thread transfer impact” PR comment pipeline by deleting the
thread-transfer-reportworkflow and its trusted publisher scripts/tests, while CI still may emit transfer budget artifacts from the main workflow.Mobile is wired to orchestration V2 end-to-end: SQLite shell/thread caches now use the shared
ORCHESTRATION_CACHE_SCHEMA_VERSIONand V2 snapshot shapes; the connection runtime loads bounded thread history plus a history controller instead of the old unbounded loader. Thread list/detail flows switch from legacy session/turn fields to runtime summaries (thread.runtime,threadRuntimeIsActive, dedicated archive eligibility), runtime request IDs for approvals and user input (including read-only UI whenresponseCapabilityis not live), and checkpoint/review data derived from thread projections rather than embedded checkpoint lists.Thread UX additions and polish include visit watermark dispatch on open, a queue control and relationships banner on the feed, progressive history controls, a thread activity inspector with rollback, shared brand mark assets, extra adaptive theme tokens, and copy tweaks (“coding runtime”). CI installs
build-essentialso ACP process-tree pthread fixtures compile instead of soft-skipping. Smaller follow-ons: desktop environment user-data dir test expectations, Tailscale Effect typing, defaultpersistComposerContextStrip, README appearance doc link, and marketing Cursor harness label.Reviewed by Cursor Bugbot for commit 6081201. Bugbot is set up for automated code reviews on this repo. Configure here.