Skip to content

sync: port upstream provider/orchestration foundations (#3740–#4168)#181

Merged
cursor[bot] merged 31 commits into
mainfrom
codex/sync-20260725-l1-foundation-daf2
Jul 25, 2026
Merged

sync: port upstream provider/orchestration foundations (#3740–#4168)#181
cursor[bot] merged 31 commits into
mainfrom
codex/sync-20260725-l1-foundation-daf2

Conversation

@aaditagrawal

@aaditagrawal aaditagrawal commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Summary

Layer 1 of the Jul 25 upstream sync (110 commits on pingdotgg/t3code since v0.36.0 ancestry).

Ports early provider, orchestration, CLI, and web fixes through preserve-custom-model-slugs (#4168).

Highlights

  • Claude SDK Windows executable resolution, Bedrock auth, OpenCode titles/resume/health-check
  • Codex launch args, web-search tool details, reset-credit protocol regen, approval details
  • Orchestration stale-turn cleanup; mobile shell snapshot sync; iOS glass capability gate
  • Archive threads from context menu; force-remove projects; sidebar shrink; preview localhost
  • Fork adaptation: update Amp/Copilot/Droid/Gemini/Kilo/Cursor SDK callers for providerModelsFromSettings without provider-kind arg

Test plan

  • Cherry-picks apply on fork main
  • CI Check/Test green
  • Merge only while base is main
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Added native Liquid Glass support and improved mobile headers, insets, and navigation.
    • Added forced project deletion, including associated threads.
    • Added Codex launch-argument configuration and improved Claude, Cursor, and OpenCode provider support.
    • Added resumable synchronization with clearer loading and syncing statuses.
    • Added bulk thread archiving, improved preview URL handling, and visible approval details.
    • Improved composer chip selection highlighting and minimap interactions.
  • Bug Fixes

    • Improved browser permission consistency and sidebar keyboard shortcut handling.
    • Preserved custom model names when they conflict with provider aliases.

nsxdavid and others added 25 commits July 25, 2026 08:37
…ingdotgg#3751)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 749baec)
)

Co-authored-by: Shoaib Ansari <shoaibansari@Shoaibs-Mac-mini.local>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
(cherry picked from commit d266c06)
…otgg#2093)

Co-authored-by: Julius Marminge <julius0216@outlook.com>
(cherry picked from commit 33f1cb4)
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: Julius Marminge <jmarminge@gmail.com>
Co-authored-by: root <root@localhost.localdomain>
(cherry picked from commit 40c0ab0)
…pingdotgg#3159)

Co-authored-by: Julius Marminge <julius0216@outlook.com>
(cherry picked from commit 501ce27)
Co-authored-by: codex <codex@users.noreply.github.com>
(cherry picked from commit 08993a5)
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
(cherry picked from commit 2fae0d0)
…tgg#4163)

Co-authored-by: codex <codex@users.noreply.github.com>
(cherry picked from commit 8e3467f)
Co-authored-by: codex <codex@users.noreply.github.com>
(cherry picked from commit b6d9ce3)
…tarting an empty one (pingdotgg#3617)

Co-authored-by: codex <codex@users.noreply.github.com>
(cherry picked from commit f4da4f3)
…gdotgg#3869)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 946b867)
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
(cherry picked from commit c710167)
(cherry picked from commit fa69f05)

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
…ut provider kind

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Jul 25, 2026
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@cursor[bot], you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: addff070-2818-40b2-b978-62ce579a275f

📥 Commits

Reviewing files that changed from the base of the PR and between fdc38a7 and 04a9536.

📒 Files selected for processing (7)
  • apps/server/src/provider/Layers/ClaudeProvider.ts
  • apps/server/src/provider/Layers/CursorProvider.ts
  • apps/server/src/provider/cursor/CursorSdkMappings.ts
  • apps/server/src/server.test.ts
  • apps/server/src/ws.ts
  • apps/web/src/components/chat/MessagesTimeline.test.tsx
  • apps/web/src/hooks/useSettings.ts
📝 Walkthrough

Walkthrough

This pull request updates preview permissions, mobile Liquid Glass handling, provider runtimes, orchestration synchronization, web interactions, shared parsing, and protocol contracts. It adds completion markers, durable OpenCode sessions, Codex launch arguments, Cursor ACP probing, and several UI behavior changes.

Changes

Desktop preview permissions

Layer / File(s) Summary
Preview permission allow-list
apps/desktop/src/preview/*
Preview permission requests and checks use one shared allow-list, with coverage for permitted and denied Electron permissions.

Mobile native glass and synchronization UI

Layer / File(s) Summary
Capability-based mobile chrome
apps/mobile/src/native/*, apps/mobile/src/lib/*, apps/mobile/src/Stack.tsx, apps/mobile/src/features/*
Headers, insets, navigation items, and thread styling now depend on native Liquid Glass support rather than iOS detection alone.
Workspace synchronization status
apps/mobile/src/features/home/*
Pending shell snapshots produce loading or syncing labels and control empty-state indicators.

Server provider and orchestration updates

Layer / File(s) Summary
Provider runtime integrations
apps/server/src/provider/*, apps/server/src/textGeneration/*
Codex launch arguments, Claude executable resolution, Cursor ACP probing, OpenCode CLI inventory, durable sessions, and custom model normalization are updated.
Runtime activity projection
apps/server/src/orchestration/Layers/*
Approval details are preserved, task titles are reconstructed, and inactive session states clear active turns.
Project removal
apps/server/src/cli/project.ts, apps/server/src/bin.test.ts
project remove --force forwards force deletion and verifies project and thread deletion.

Client synchronization

Layer / File(s) Summary
Completion-marker synchronization
packages/contracts/src/*, packages/client-runtime/src/*, apps/server/src/ws.ts
Shell and thread streams buffer live events, emit optional synchronized markers, and resubscribe on foreground wakeups.

Web UI updates

Layer / File(s) Summary
Sidebar actions and composer UI
apps/web/src/components/Sidebar*, apps/web/src/hooks/*, apps/web/src/components/Composer*, apps/web/src/index.css
Bulk archive handling, selected-entry deletion, composer chip selection, and complete approval details are added.
Preview, scripts, and timeline
apps/web/src/components/preview/*, apps/web/src/projectScripts*, apps/web/src/components/chat/MessagesTimeline*
Preview navigation, project script construction, minimap interactions, work-group labels, and timeline scrolling are revised.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: juliusmarminge

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely summarizes the main upstream-sync port and matches the changeset.
Description check ✅ Passed It covers the main changes and test plan, though it doesn't follow the template's exact section headings.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/sync-20260725-l1-foundation-daf2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added the size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). label Jul 25, 2026
Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fdc38a7bb8

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/server/src/ws.ts Outdated
Comment on lines +1092 to +1093
yield* Effect.forkScoped(
Stream.fromSubscription(liveSubscription).pipe(
Stream.mapEffect(toShellStreamEvent),
Stream.flatMap((event) =>
Option.isSome(event) ? Stream.succeed(event.value) : Stream.empty,
),
Stream.runForEach((item) => Queue.offer(liveBuffer, item)),
),
liveStream.pipe(Stream.runForEach((item) => Queue.offer(liveBuffer, item))),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Acquire the live subscription before forking

When an event is published after forkScoped schedules this consumer but before Stream.fromPubSub actually starts, the event has no subscriber and is lost; this can leave shell or thread projections permanently stale after snapshot/replay. OrchestrationEngineShape.streamDomainEvents explicitly documents this race and provides subscribeDomainEvents so the subscription can be acquired synchronously before forking, as the previous implementation did.

AGENTS.md reference: AGENTS.md:L25-L25

Useful? React with 👍 / 👎.

Comment thread apps/server/src/ws.ts Outdated
Comment on lines +1185 to +1188
const isThisThreadDetailEvent = (event: OrchestrationEvent) =>
event.aggregateKind === "thread" &&
event.aggregateId === input.threadId &&
(isThreadDetailEvent(event) ||
event.type === "thread.deleted" ||
event.type === "thread.archived");
isThreadDetailEvent(event);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward thread lifecycle events to detail subscribers

When the subscribed thread is deleted or archived, this predicate now drops the lifecycle event because isThreadDetailEvent excludes both types. Consequently EnvironmentThreadState.applyItem never calls setDeleted or applies the archive timestamp, leaving its detail atom and persisted cache stale; this also invalidates the HTTP loader's explicit 404 fallback assumption that the socket subscription will surface deletion.

AGENTS.md reference: AGENTS.md:L25-L25

Useful? React with 👍 / 👎.

Comment on lines 1950 to +1956
const rawPreview = workEntryPreview(workEntry, workspaceRoot);
// When showCommandOutput is off, suppress command/detail previews but still show changed-file previews.
const gatedPreview = showCommandOutput
? rawPreview
: !workEntry.command && !workEntry.detail
? rawPreview
: null;
// Suppress previews that are effectively the same label as the heading.
const preview =
gatedPreview &&
normalizeCompactToolLabel(gatedPreview).toLowerCase() ===
rawPreview &&
normalizeCompactToolLabel(rawPreview).toLowerCase() ===
normalizeCompactToolLabel(heading).toLowerCase()
? null
: gatedPreview;
: rawPreview;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor the command-output visibility setting

For users with persisted showCommandOutput: false, command and detail previews are now always copied into displayText. The timeline no longer reads this setting anywhere—the only remaining repository reference is its schema default—so a preference that previously suppressed these collapsed previews has no effect.

Useful? React with 👍 / 👎.

Comment on lines 598 to 600
function compactMinimapPreview(text: string | null | undefined) {
const compact =
text?.slice(0, TIMELINE_MINIMAP_PREVIEW_MAX_CHARS).replace(/\s+/g, " ").trim() ?? "";
const compact = text?.replace(/\s+/g, " ").trim() ?? "";
return compact.length > 0 ? compact : null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound the minimap preview text

On threads containing long user prompts or assistant responses, this now normalizes and retains the complete message for every minimap item, even though the preview renders only a few lines. Large generated responses can therefore cause substantial CPU, memory, and DOM-text overhead whenever rows change; retain the prior pre-normalization character cap.

AGENTS.md reference: AGENTS.md:L56-L60

Useful? React with 👍 / 👎.

Comment on lines +759 to +763
const readCursorCliConfigChannel = Effect.fn("readCursorCliConfigChannel")(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const configPath = path.join(NodeOS.homedir(), ".cursor", "cli-config.json");
const raw = yield* fileSystem.readFileString(configPath).pipe(Effect.orElseSucceed(() => ""));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Read Cursor channel state from the instance environment

When a custom Cursor instance overrides HOME or USERPROFILE, its CLI probe runs with the merged per-instance environment but this channel check still reads the server process user's home. A lab-channel instance can therefore be rejected based on an unrelated host cli-config.json (or a stable-channel instance can bypass the check); resolve the config path using the same environment passed to checkCursorProviderStatus.

AGENTS.md reference: AGENTS.md:L24-L24

Useful? React with 👍 / 👎.

Comment on lines 493 to 496
maintainScrollAtEnd={
anchoredEndSpace || foldToggleSettling
anchoredEndSpace
? false
: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Suspend end following while toggling disclosures

When the timeline is currently considered at the end and the user expands or collapses a turn, the row resize now occurs while maintainScrollAtEnd remains enabled, so LegendList follows the new end instead of preserving the disclosure the user clicked. The removed foldToggleSettling guard disabled end following during this layout transition; keep an equivalent guard, particularly for turn folds that lack the work-group anchor compensation.

Useful? React with 👍 / 👎.

Comment on lines 372 to 375
export function resolveClaudeApiModelId(modelSelection: ModelSelection): string {
const contextWindow = getModelSelectionStringOptionValue(modelSelection, "contextWindow");
if (modelSelection.model === "claude-sonnet-5") {
return contextWindow === "1m" ? "sonnet[1m]" : modelSelection.model;
}

switch (contextWindow) {
switch (getModelSelectionStringOptionValue(modelSelection, "contextWindow")) {
case "1m":
return `${modelSelection.model}[1m]`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the Sonnet 5 gateway model alias

When a gateway-backed Claude instance selects Sonnet 5 with the 1M context option, this generic branch now sends claude-sonnet-5[1m] to query.setModel. The gateway integration requires the special sonnet[1m] identifier, so starting or switching such a turn fails instead of activating the requested context window; retain the previous Sonnet-specific mapping.

AGENTS.md reference: AGENTS.md:L24-L24

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/src/components/chat/MessagesTimeline.tsx (1)

702-752: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make a zero-width minimap strip unfocusable.

pointer-events-none does not remove this button from tab order. Focusing it sets activeItem, expands it to 22rem, and permits keyboard selection even though a zero width is documented as disabled/inert.

Proposed fix
         <button
           aria-label={`Jump to message: ${activeItem?.userText ?? "User message"}`}
+          disabled={hitStripWidth <= 0}
           className={cn(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/chat/MessagesTimeline.tsx` around lines 702 - 752,
Make the minimap button rendered by the visible timeline control unfocusable
when hitStripWidth is zero by conditionally setting its tabIndex to -1, while
preserving normal keyboard focus when the strip has usable width. Keep the
existing pointer-event behavior and keyboard handlers unchanged.
apps/web/src/hooks/useThreadActions.ts (1)

91-134: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve archive-success status in the single-thread path.

archiveThread calls opts.onArchived after a successful mutation but still returns the later navigation failure. Only archiveSelectedThreadEntries tracks didArchive; attemptArchiveThread calls archiveThread(threadRef) directly and always shows the generic "Failed to archive thread" toast when navigation fails. Have attemptArchiveThread detect this followup case using onArchived and show a softer message.

Also ensure focused tests cover the new archiveThread behavior (opts.onArchived tracking and the unconditional refresh ordering).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/hooks/useThreadActions.ts` around lines 91 - 134, Update
attemptArchiveThread to pass an onArchived callback to archiveThread and track
whether archiving succeeded before any follow-up navigation failure; show the
softer post-archive message when that callback ran instead of the generic
failure toast. Preserve archiveThread’s unconditional
refreshArchivedThreadsForEnvironment and callback ordering after mutation
success. Add focused tests covering onArchived tracking and refresh occurring
unconditionally after a successful archive.

Source: Coding guidelines

🧹 Nitpick comments (11)
packages/client-runtime/src/state/shell.ts (1)

110-118: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

setReady no longer sets a ready state.

Post-change it only ever yields "synchronizing", making it behaviorally identical to setSynchronizing except for the live short-circuit. Consider renaming (e.g. setConnectedAwaitingSync) so the connection-phase mapping at Line 229 reads correctly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/client-runtime/src/state/shell.ts` around lines 110 - 118, Rename
setReady to reflect that it transitions non-live state to "synchronizing" rather
than a ready state, using a name such as setConnectedAwaitingSync. Update all
references, including the connection-phase mapping around the relevant call
site, while preserving the existing live short-circuit and state update
behavior.
apps/server/src/ws.ts (1)

1110-1118: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the completion-marker concat into one helper.

The same four-line marker/buffer composition is repeated in all four subscription paths (shell resume, shell fallback, thread resume, thread fallback). A single helper keeps the ordering contract in one place.

As per coding guidelines, "extract shared logic instead of duplicating local implementations".

♻️ Suggested helper (define near the other stream helpers)
const withCompletionMarker = <A extends { readonly kind: string }, E>(
  liveBuffer: Queue.Queue<A>,
  requested: boolean,
): Stream.Stream<A, E> => {
  const buffered = Stream.fromQueue(liveBuffer);
  return requested
    ? Stream.concat(
        Stream.fromEffect(Queue.offer(liveBuffer, { kind: "synchronized" } as A)).pipe(
          Stream.drain,
        ),
        buffered,
      )
    : buffered;
};

Then each site becomes:

-                    const afterCatchUp =
-                      input.requestCompletionMarker === true
-                        ? Stream.concat(
-                            Stream.fromEffect(
-                              Queue.offer(liveBuffer, { kind: "synchronized" as const }),
-                            ).pipe(Stream.drain),
-                            Stream.fromQueue(liveBuffer),
-                          )
-                        : Stream.fromQueue(liveBuffer);
+                    const afterCatchUp = withCompletionMarker(
+                      liveBuffer,
+                      input.requestCompletionMarker === true,
+                    );

Also applies to: 1145-1153, 1238-1246, 1269-1277

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/ws.ts` around lines 1110 - 1118, Extract the repeated
completion-marker and live-buffer composition into a shared withCompletionMarker
helper near the other stream helpers. Have it accept the liveBuffer and
requested flag, preserve the marker-before-buffer ordering, and replace the
duplicated logic in the shell resume/fallback and thread resume/fallback
subscription paths.

Source: Coding guidelines

apps/web/src/components/Sidebar.tsx (1)

1789-1791: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated "is thread running" predicate risks divergence.

thread.session?.status === "running" && thread.session.activeTurnId != null mirrors the guard in useThreadActions.ts's archiveThread (ThreadArchiveBlockedError check). If one side changes (e.g. a new session status is added), the UI-disabled state and the actual mutation guard can silently disagree.

Consider extracting a shared isThreadBlockingArchive(thread)/isThreadRunning(thread) helper used by both files.

As per coding guidelines, "extract shared logic instead of duplicating local implementations."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/Sidebar.tsx` around lines 1789 - 1791, Extract the
duplicated running-thread predicate into a shared helper, such as
isThreadBlockingArchive or isThreadRunning, and use it in both Sidebar’s
hasRunningThread calculation and useThreadActions.ts archiveThread’s
ThreadArchiveBlockedError guard. Preserve the existing condition and behavior
while ensuring both UI and mutation checks rely on the same implementation.

Source: Coding guidelines

apps/server/src/provider/opencodeRuntime.ts (2)

659-659: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Nit: ({} as {}) cast is redundant.

-      const env = input.environment !== undefined ? { environment: input.environment } : ({} as {});
+      const env = input.environment !== undefined ? { environment: input.environment } : {};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/opencodeRuntime.ts` at line 659, Remove the
redundant `as {}` cast from the fallback object in the environment assignment,
leaving the existing conditional behavior unchanged.

232-273: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

JSON.parse result is assigned to Agent["permission"] unchecked.

permission comes back as any and is pushed straight into the Agent shape, so malformed-but-parseable CLI output (e.g. an object instead of a rule array) propagates into the inventory typed as valid. Consider a light shape guard (array check) before accepting the agent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/opencodeRuntime.ts` around lines 232 - 273, Update
parseAgentListCliOutput’s flushAgent JSON parsing to validate that the parsed
permission value is an array before adding the agent. Skip
malformed-but-parseable values that are not arrays, while preserving the
existing handling for valid permission arrays and unparseable JSON.
apps/server/src/provider/Layers/OpenCodeAdapter.ts (1)

1243-1248: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Nit: use an explicit undefined recovery instead of Effect.void.

Effect.void as the not-found branch makes adopted's type Session | void, so "no resume" reads as an accident rather than intent.

-                      Effect.catchIf(
-                        (cause) => isOpenCodeNotFound(cause),
-                        () => Effect.void,
-                      ),
+                      Effect.catchIf(
+                        (cause) => isOpenCodeNotFound(cause),
+                        () => Effect.succeed(undefined),
+                      ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/Layers/OpenCodeAdapter.ts` around lines 1243 - 1248,
Update the not-found recovery branch in the adopted session flow using
Effect.catchIf so it explicitly returns undefined instead of Effect.void.
Preserve the isOpenCodeNotFound predicate and ensure adopted has the intended
Session | undefined type.
apps/server/src/provider/Layers/OpenCodeProvider.test.ts (1)

98-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The double never fails, so "degrades gracefully on CLI failure" only exercises the empty-inventory path.

loadInventoryFromCli returns a successful empty inventory when inventoryError is set, so checkOpenCodeProviderStatus's inventoryExit._tag === "Failure" fallback branch stays uncovered. Add a case where the double actually fails (e.g. a separate cliInventoryError flag) to pin the error-status path too.

Also applies to: 215-222

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/Layers/OpenCodeProvider.test.ts` around lines 98 -
104, Update the test double for loadInventoryFromCli and the related runtimeMock
state to support an actual failed Effect, using a distinct flag such as
cliInventoryError. Add or adjust a test case that enables this flag and verifies
checkOpenCodeProviderStatus handles inventoryExit._tag === "Failure" with the
expected degraded error status, while preserving the existing empty-inventory
scenario.
apps/web/src/components/settings/ProviderModelsSection.tsx (1)

113-118: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prop doc for driverKind is now stale.

Normalization no longer depends on driverKind (only the placeholder does), so the JSDoc at Lines 43-47 ("Driver kind for slug normalization + input placeholder") should drop the normalization mention.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/settings/ProviderModelsSection.tsx` around lines 113
- 118, Update the JSDoc for the driverKind prop in ProviderModelsSection to
describe its use only for the input placeholder, removing the outdated reference
to slug normalization. Keep the prop behavior and normalization logic unchanged.
apps/server/src/provider/Layers/CursorProvider.ts (2)

686-694: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

toTitleCaseWords duplicates the identical helper in ClaudeProvider.ts.

apps/server/src/provider/Layers/ClaudeProvider.ts Lines 686-694 define a byte-identical function. Extract it into a shared module (e.g. @t3tools/shared/model or a provider-local util) and import in both places.

As per coding guidelines: "extract shared logic instead of duplicating local implementations."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/Layers/CursorProvider.ts` around lines 686 - 694,
Extract the duplicated toTitleCaseWords helper into a shared module, then remove
the local implementations from CursorProvider and ClaudeProvider and import the
shared function in both providers. Preserve its existing whitespace, underscore,
hyphen splitting, casing, and joining behavior.

Source: Coding guidelines


279-304: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Category matching is inconsistent with getCursorConfigOptionCategory.

findCursorEffortConfigOption compares categories via the trimming/lowercasing helper, while the context/fast/thinking lookups use raw option.category === "model_config". Any casing or whitespace variation from the ACP payload would silently drop these descriptors. Consider reusing the normalizer here.

♻️ Suggested normalization
-  const contextOption = configOptions.find(
-    (option) => option.category === "model_config" && isCursorContextConfigOption(option),
-  );
+  const contextOption = configOptions.find(
+    (option) =>
+      getCursorConfigOptionCategory(option) === "model_config" &&
+      isCursorContextConfigOption(option),
+  );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/Layers/CursorProvider.ts` around lines 279 - 304,
Update the context, fast, and thinking configuration lookups in the surrounding
Cursor provider logic to compare option categories using the same
trimming/lowercasing normalization as getCursorConfigOptionCategory and
findCursorEffortConfigOption. Preserve the existing option predicates and
descriptor construction while ensuring category casing or surrounding whitespace
does not exclude valid ACP options.
packages/shared/src/model.test.ts (1)

150-157: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Restore test coverage for the remaining exported model helpers.

resolveSelectableModel, resolveModelSlugForProvider, isClaudeUltrathinkPrompt, applyClaudePromptEffortPrefix, and trimOrNull are still exported from packages/shared/src/model.ts, but they no longer have focused tests here. Add back the covered cases for those helpers; while here, add null/whitespace-only cases for normalizeCustomModelSlug to cover its documented return contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/shared/src/model.test.ts` around lines 150 - 157, Extend the “model
slug normalization” tests in model.test.ts with focused coverage for the
exported helpers resolveSelectableModel, resolveModelSlugForProvider,
isClaudeUltrathinkPrompt, applyClaudePromptEffortPrefix, and trimOrNull,
covering their documented behaviors. Also add normalizeCustomModelSlug
assertions for null and whitespace-only input, verifying the documented return
contract while preserving the existing exact-slug case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/mobile/src/features/home/HomeScreen.tsx`:
- Line 392: Update the HomeScreen safe-area handling around the paddingTop
calculation and HomeTopContentSpacer at the referenced inset/empty-state paths
so iOS devices without native Liquid Glass retain the existing top inset beneath
the solid header. Preserve the Liquid Glass behavior while adding an equivalent
fallback spacer or content padding for unsupported iOS devices, including the
empty-state layout.

In `@apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts`:
- Around line 736-737: Update rememberTaskDescription to cap description length
before Cache.set, using the task-description schema’s defined maximum if
available or a clearly specified truncation limit. Preserve the existing task
key and cache behavior while ensuring long provider descriptions cannot retain
disproportionate memory.

In `@apps/server/src/provider/Layers/ClaudeAdapter.ts`:
- Around line 1361-1364: Update makeClaudeAdapter and its Claude driver
construction path to provide the services required by
resolveClaudeSdkExecutablePath: HostProcessPlatform, SpawnExecutableResolution,
and ClaudeExecutableFileCheck. Prefer exporting a small runtime layer from the
Claude adapter and merge it into the layer used by ClaudeDriver.create(), or
explicitly extend ClaudeDriverEnv and supply these services at the production
construction site.

In `@apps/server/src/provider/opencodeRuntime.ts`:
- Around line 657-691: Bound both inventory command effects created by
runModelsCli and runAgentsCli with the existing
DEFAULT_OPENCODE_SERVER_TIMEOUT_MS, ensuring timed-out attempts produce failures
handled by the current retry and graceful-degradation flow. Apply the timeout to
each attempt, including retries, without changing the parallel execution or
retry conditions.

In `@apps/web/src/components/ComposerPromptEditor.tsx`:
- Around line 1237-1241: Update the cleanup returned by the effect in
ComposerPromptEditor to remove data-composer-chip-selected from all currently
selected DOM nodes before or alongside unregistering the update, focus, and blur
listeners, ensuring remounts and Strict Mode replays cannot retain stale
highlights.

In `@apps/web/src/components/preview/PreviewView.tsx`:
- Around line 116-153: Update handleSubmitUrl so normalizePreviewUrl runs
outside the try/catch that handles navigateToResolvedUrl failures. Catch
normalization errors separately and surface them through the existing
toast/error-notification mechanism, while preserving the current silent handling
for server-side navigation failures.

In `@apps/web/src/components/Sidebar.tsx`:
- Around line 1783-1788: Derive the displayed and confirmation count from
selectedThreadEntries.length rather than the raw threadKeys.length. Update the
count used by the archive, delete, and mark-unread labels/dialogs after the
parseScopedThreadKey/readThreadShell filtering in the surrounding selection
flow, while keeping the action loops based on selectedThreadEntries.

---

Outside diff comments:
In `@apps/web/src/components/chat/MessagesTimeline.tsx`:
- Around line 702-752: Make the minimap button rendered by the visible timeline
control unfocusable when hitStripWidth is zero by conditionally setting its
tabIndex to -1, while preserving normal keyboard focus when the strip has usable
width. Keep the existing pointer-event behavior and keyboard handlers unchanged.

In `@apps/web/src/hooks/useThreadActions.ts`:
- Around line 91-134: Update attemptArchiveThread to pass an onArchived callback
to archiveThread and track whether archiving succeeded before any follow-up
navigation failure; show the softer post-archive message when that callback ran
instead of the generic failure toast. Preserve archiveThread’s unconditional
refreshArchivedThreadsForEnvironment and callback ordering after mutation
success. Add focused tests covering onArchived tracking and refresh occurring
unconditionally after a successful archive.

---

Nitpick comments:
In `@apps/server/src/provider/Layers/CursorProvider.ts`:
- Around line 686-694: Extract the duplicated toTitleCaseWords helper into a
shared module, then remove the local implementations from CursorProvider and
ClaudeProvider and import the shared function in both providers. Preserve its
existing whitespace, underscore, hyphen splitting, casing, and joining behavior.
- Around line 279-304: Update the context, fast, and thinking configuration
lookups in the surrounding Cursor provider logic to compare option categories
using the same trimming/lowercasing normalization as
getCursorConfigOptionCategory and findCursorEffortConfigOption. Preserve the
existing option predicates and descriptor construction while ensuring category
casing or surrounding whitespace does not exclude valid ACP options.

In `@apps/server/src/provider/Layers/OpenCodeAdapter.ts`:
- Around line 1243-1248: Update the not-found recovery branch in the adopted
session flow using Effect.catchIf so it explicitly returns undefined instead of
Effect.void. Preserve the isOpenCodeNotFound predicate and ensure adopted has
the intended Session | undefined type.

In `@apps/server/src/provider/Layers/OpenCodeProvider.test.ts`:
- Around line 98-104: Update the test double for loadInventoryFromCli and the
related runtimeMock state to support an actual failed Effect, using a distinct
flag such as cliInventoryError. Add or adjust a test case that enables this flag
and verifies checkOpenCodeProviderStatus handles inventoryExit._tag ===
"Failure" with the expected degraded error status, while preserving the existing
empty-inventory scenario.

In `@apps/server/src/provider/opencodeRuntime.ts`:
- Line 659: Remove the redundant `as {}` cast from the fallback object in the
environment assignment, leaving the existing conditional behavior unchanged.
- Around line 232-273: Update parseAgentListCliOutput’s flushAgent JSON parsing
to validate that the parsed permission value is an array before adding the
agent. Skip malformed-but-parseable values that are not arrays, while preserving
the existing handling for valid permission arrays and unparseable JSON.

In `@apps/server/src/ws.ts`:
- Around line 1110-1118: Extract the repeated completion-marker and live-buffer
composition into a shared withCompletionMarker helper near the other stream
helpers. Have it accept the liveBuffer and requested flag, preserve the
marker-before-buffer ordering, and replace the duplicated logic in the shell
resume/fallback and thread resume/fallback subscription paths.

In `@apps/web/src/components/settings/ProviderModelsSection.tsx`:
- Around line 113-118: Update the JSDoc for the driverKind prop in
ProviderModelsSection to describe its use only for the input placeholder,
removing the outdated reference to slug normalization. Keep the prop behavior
and normalization logic unchanged.

In `@apps/web/src/components/Sidebar.tsx`:
- Around line 1789-1791: Extract the duplicated running-thread predicate into a
shared helper, such as isThreadBlockingArchive or isThreadRunning, and use it in
both Sidebar’s hasRunningThread calculation and useThreadActions.ts
archiveThread’s ThreadArchiveBlockedError guard. Preserve the existing condition
and behavior while ensuring both UI and mutation checks rely on the same
implementation.

In `@packages/client-runtime/src/state/shell.ts`:
- Around line 110-118: Rename setReady to reflect that it transitions non-live
state to "synchronizing" rather than a ready state, using a name such as
setConnectedAwaitingSync. Update all references, including the connection-phase
mapping around the relevant call site, while preserving the existing live
short-circuit and state update behavior.

In `@packages/shared/src/model.test.ts`:
- Around line 150-157: Extend the “model slug normalization” tests in
model.test.ts with focused coverage for the exported helpers
resolveSelectableModel, resolveModelSlugForProvider, isClaudeUltrathinkPrompt,
applyClaudePromptEffortPrefix, and trimOrNull, covering their documented
behaviors. Also add normalizeCustomModelSlug assertions for null and
whitespace-only input, verifying the documented return contract while preserving
the existing exact-slug case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a3877e85-4f3b-49bf-b7ed-32a64810de14

📥 Commits

Reviewing files that changed from the base of the PR and between 937f301 and fdc38a7.

⛔ Files ignored due to path filters (3)
  • packages/effect-codex-app-server/src/_generated/meta.gen.ts is excluded by !**/_generated/**
  • packages/effect-codex-app-server/src/_generated/namespaces.gen.ts is excluded by !**/_generated/**
  • packages/effect-codex-app-server/src/_generated/schema.gen.ts is excluded by !**/_generated/**
📒 Files selected for processing (94)
  • apps/desktop/src/preview/BrowserSession.test.ts
  • apps/desktop/src/preview/BrowserSession.ts
  • apps/mobile/src/Stack.tsx
  • apps/mobile/src/components/CompactBrandTitle.tsx
  • apps/mobile/src/features/files/FileTreeBrowser.tsx
  • apps/mobile/src/features/home/HomeRouteScreen.tsx
  • apps/mobile/src/features/home/HomeScreen.tsx
  • apps/mobile/src/features/home/WorkspaceConnectionStatus.test.ts
  • apps/mobile/src/features/home/WorkspaceConnectionStatus.tsx
  • apps/mobile/src/features/home/workspace-connection-status.ts
  • apps/mobile/src/features/threads/ThreadDetailScreen.tsx
  • apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
  • apps/mobile/src/features/threads/ThreadRouteScreen.tsx
  • apps/mobile/src/features/threads/sidebar-navigation-shell.tsx
  • apps/mobile/src/lib/native-glass-capability.test.ts
  • apps/mobile/src/lib/native-glass-capability.ts
  • apps/mobile/src/native/native-glass.ts
  • apps/server/src/bin.test.ts
  • apps/server/src/cli/project.ts
  • apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.approval.test.ts
  • apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts
  • apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts
  • apps/server/src/provider/Drivers/ClaudeExecutable.test.ts
  • apps/server/src/provider/Drivers/ClaudeExecutable.ts
  • apps/server/src/provider/Layers/AmpProvider.ts
  • apps/server/src/provider/Layers/ClaudeAdapter.ts
  • apps/server/src/provider/Layers/ClaudeProvider.ts
  • apps/server/src/provider/Layers/CodexAdapter.test.ts
  • apps/server/src/provider/Layers/CodexAdapter.ts
  • apps/server/src/provider/Layers/CodexProvider.ts
  • apps/server/src/provider/Layers/CodexSessionRuntime.test.ts
  • apps/server/src/provider/Layers/CodexSessionRuntime.ts
  • apps/server/src/provider/Layers/CopilotProvider.ts
  • apps/server/src/provider/Layers/CursorProvider.ts
  • apps/server/src/provider/Layers/DroidProvider.ts
  • apps/server/src/provider/Layers/GeminiCliProvider.ts
  • apps/server/src/provider/Layers/GrokProvider.ts
  • apps/server/src/provider/Layers/KiloProvider.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.ts
  • apps/server/src/provider/Layers/OpenCodeProvider.test.ts
  • apps/server/src/provider/Layers/OpenCodeProvider.ts
  • apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts
  • apps/server/src/provider/Layers/ProviderRegistry.test.ts
  • apps/server/src/provider/Layers/codexLaunchArgs.test.ts
  • apps/server/src/provider/Layers/codexLaunchArgs.ts
  • apps/server/src/provider/cursor/CursorSdkMappings.ts
  • apps/server/src/provider/opencodeRuntime.cliParsers.test.ts
  • apps/server/src/provider/opencodeRuntime.ts
  • apps/server/src/provider/providerSnapshot.test.ts
  • apps/server/src/provider/providerSnapshot.ts
  • apps/server/src/server.test.ts
  • apps/server/src/serverSettings.test.ts
  • apps/server/src/textGeneration/CodexTextGeneration.test.ts
  • apps/server/src/textGeneration/CodexTextGeneration.ts
  • apps/server/src/textGeneration/OpenCodeTextGeneration.test.ts
  • apps/server/src/ws.ts
  • apps/web/src/components/AppSidebarLayout.tsx
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/ComposerPromptEditor.tsx
  • apps/web/src/components/Sidebar.logic.test.ts
  • apps/web/src/components/Sidebar.logic.ts
  • apps/web/src/components/Sidebar.tsx
  • apps/web/src/components/chat/ComposerPendingApprovalPanel.test.tsx
  • apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx
  • apps/web/src/components/chat/MessagesTimeline.logic.ts
  • apps/web/src/components/chat/MessagesTimeline.test.tsx
  • apps/web/src/components/chat/MessagesTimeline.tsx
  • apps/web/src/components/preview/PreviewView.test.tsx
  • apps/web/src/components/preview/PreviewView.tsx
  • apps/web/src/components/settings/KeybindingsSettings.tsx
  • apps/web/src/components/settings/ProviderModelsSection.tsx
  • apps/web/src/components/settings/ProviderSettingsForm.test.ts
  • apps/web/src/hooks/useThreadActions.ts
  • apps/web/src/index.css
  • apps/web/src/modelSelection.test.ts
  • apps/web/src/modelSelection.ts
  • apps/web/src/projectScripts.test.ts
  • apps/web/src/projectScripts.ts
  • packages/client-runtime/src/rpc/client.ts
  • packages/client-runtime/src/state/shell-sync.test.ts
  • packages/client-runtime/src/state/shell.ts
  • packages/client-runtime/src/state/threads-sync.test.ts
  • packages/client-runtime/src/state/threads.ts
  • packages/contracts/src/orchestration.ts
  • packages/contracts/src/server.ts
  • packages/contracts/src/settings.test.ts
  • packages/contracts/src/settings.ts
  • packages/effect-codex-app-server/scripts/generate.ts
  • packages/effect-codex-app-server/src/protocol.test.ts
  • packages/shared/src/cliArgs.test.ts
  • packages/shared/src/cliArgs.ts
  • packages/shared/src/model.test.ts
  • packages/shared/src/model.ts
💤 Files with no reviewable changes (7)
  • apps/mobile/src/components/CompactBrandTitle.tsx
  • apps/server/src/provider/Layers/AmpProvider.ts
  • apps/server/src/provider/Layers/GeminiCliProvider.ts
  • apps/server/src/provider/Layers/DroidProvider.ts
  • apps/server/src/provider/cursor/CursorSdkMappings.ts
  • apps/server/src/provider/Layers/CopilotProvider.ts
  • apps/server/src/provider/Layers/KiloProvider.ts

style={{
paddingBottom: Math.max(insets.bottom, 24),
paddingTop: Platform.OS === "ios" ? insets.top + 72 : 0,
paddingTop: NATIVE_LIQUID_GLASS_SUPPORTED ? insets.top + 72 : 0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve safe-area insets on iOS without Liquid Glass.

When NATIVE_LIQUID_GLASS_SUPPORTED is false on an iOS device, Line 425 still suppresses HomeTopContentSpacer, but these changes also disable automatic content inset adjustment and remove empty-state top padding. Older or unsupported iOS devices can therefore render content beneath the native header. Keep the existing iOS inset behavior for the solid-header fallback, or add an equivalent fallback spacer/inset path.

Also applies to: 473-474

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/mobile/src/features/home/HomeScreen.tsx` at line 392, Update the
HomeScreen safe-area handling around the paddingTop calculation and
HomeTopContentSpacer at the referenced inset/empty-state paths so iOS devices
without native Liquid Glass retain the existing top inset beneath the solid
header. Preserve the Liquid Glass behavior while adding an equivalent fallback
spacer or content padding for unsupported iOS devices, including the empty-state
layout.

Comment on lines +736 to +737
const rememberTaskDescription = (threadId: ThreadId, taskId: string, description: string) =>
Cache.set(taskDescriptionByTaskKey, providerTaskKey(threadId, taskId), description);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 3 'task\.started|task\.progress|description' packages/contracts/src/providerRuntime.ts
rg -n -C 3 'taskDescriptionByTaskKey|rememberTaskDescription' apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts

Repository: aaditagrawal/t3code

Length of output: 3913


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ProviderRuntimeIngestion relevant sizes/config/cache =="
sed -n '700,760p' apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts
sed -n '1720,1770p' apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts
rg -n "TASK_DESCRIPTION_BY_TASK_CACHE_CAPACITY|TASK_DESCRIPTION_BY_TASK_TTL|Cache.make|make<" apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts packages apps/server/src 2>/dev/null | rg "TASK_DESCRIPTION_BY_TASK|Cache.make|make<" || true

echo "== description schema definitions and length constraints =="
sed -n '420,505p' packages/contracts/src/providerRuntime.ts
rg -n "TrimmedNonEmptyStringSchema|NonEmptyStringSchema|StringSchema|length|maxLength|maxLengthBytes" packages/contracts/src apps/server/src -g "*.ts"

Repository: aaditagrawal/t3code

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== TrimmedNonEmptyString definition =="
rg -n -C 4 "const TrimmedNonEmptyString|TrimmedNonEmptyStringSchema|type TrimmedNonEmptyString|NonEmptyString" packages/contracts/src/providerRuntime.ts packages/contracts/src -g '*.ts' | head -120

echo "== focused length cap occurrences =="
rg -n --max-count 100 "NonEmptyStringSchema|StringSchema|trimmed.*length|maxLength|length:" packages/contracts/src/providerRuntime.ts packages/contracts/src packages -g '*.ts' | head -200

Repository: aaditagrawal/t3code

Length of output: 28234


Cap cached task-description payloads before storing them.

TaskStartedPayload.description uses TrimmedNonEmptyStringSchema, while TaskProgressPayload.description is required with the same schema. The cache lets 10,000 task entries stay in memory for 120 minutes, so enforce a specific maximum on the task-description schema or truncate before Cache.set to avoid disproportionate memory retention from long provider descriptions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` around
lines 736 - 737, Update rememberTaskDescription to cap description length before
Cache.set, using the task-description schema’s defined maximum if available or a
clearly specified truncation limit. Preserve the existing task key and cache
behavior while ensuring long provider descriptions cannot retain
disproportionate memory.

Source: Coding guidelines

Comment on lines +1361 to +1364
const claudeSdkExecutablePath = yield* resolveClaudeSdkExecutablePath(
claudeSettings.binaryPath,
claudeEnvironment,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP -C4 '\bmakeClaudeAdapter\s*\(' --type=ts
rg -nP -C3 '(HostProcessPlatform|SpawnExecutableResolution|ClaudeExecutableFileCheck)' --type=ts -g '!**/*.test.ts'

Repository: aaditagrawal/t3code

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -u

echo "Files named ClaudeAdapter.ts:"
fd -a 'ClaudeAdapter\.ts$' . | sed 's#^\./##'

echo
echo "Git tracked TS files containing makeClaudeAdapter:"
git ls-files '*.ts' '*.tsx' | xargs rg -n -C4 '\bmakeClaudeAdapter\s*\(' || true

echo
echo "Git tracked TS files containing makeClaudeAdapter (no context):"
git ls-files '*.ts' '*.tsx' | xargs rg -n '\bmakeClaudeAdapter\s*\(' || true

echo
echo "Target file outline/size:"
if [ -f apps/server/src/provider/Layers/ClaudeAdapter.ts ]; then
  wc -l apps/server/src/provider/Layers/ClaudeAdapter.ts
  ast-grep outline apps/server/src/provider/Layers/ClaudeAdapter.ts --match makeClaudeAdapter --view expanded || true
  sed -n '1320,1385p' apps/server/src/provider/Layers/ClaudeAdapter.ts
fi

echo
echo "Relevant ClaudeExecutable imports/types/const:"
if [ -f apps/server/src/provider/Drivers/ClaudeExecutable.ts ]; then
  wc -l apps/server/src/provider/Drivers/ClaudeExecutable.ts
  ast-grep outline apps/server/src/provider/Drivers/ClaudeExecutable.ts --view expanded || true
  rg -n -C3 'HostProcessPlatform|SpawnExecutableResolution|ClaudeExecutableFileCheck|resolveClaudeSdkExecutablePath|export const ClaudeExecutableFileCheck|interface|class' apps/server/src/provider/Drivers/ClaudeExecutable.ts
  sed -n '1,120p' apps/server/src/provider/Drivers/ClaudeExecutable.ts
fi

echo
echo "All non-test TS matches for required tokens:"
git ls-files '*.ts' '*.tsx' | xargs rg -n -C3 'HostProcessPlatform|SpawnExecutableResolution|ClaudeExecutableFileCheck' || true

Repository: aaditagrawal/t3code

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -u

echo "ClaudeAdapter.ts relevant definitions/call sites:"
rg -n -C5 'export const ClaudeAdapter|ClaudeAdapter\(\)|makeClaudeAdapter|ClaudeExecutableFileCheck|SpawnExecutableResolution|HostProcessPlatform' apps/server/src/provider/Layers/ClaudeAdapter.ts

echo
echo "ClaudDriver.ts relevant context:"
sed -n '120,170p' apps/server/src/provider/Drivers/ClaudeDriver.ts
rg -n -C4 'ClaudeDriver|ClaudeAdapter|ClaudeExecutableFileCheck|SpawnExecutableResolution|HostProcessPlatform' apps/server/src/provider/Drivers/ClaudeDriver.ts

echo
echo "Other non-test Claude-related references in provider area (concise):"
rg -n -C2 'ClaudeExecutableFileCheck|SpawnExecutableResolution|resolveClaudeSdkExecutablePath|makeClaudeAdapter' apps/server/src/provider -g '!**/*.test.ts'

echo
echo "All non-test TS references to SpawnExecutableResolution in server/provider/apps:"
rg -n '(SpawnExecutableResolution|ClaudeExecutableFileCheck)' apps/server/src/provider apps/server/src -g '!**/*.test.ts'

Repository: aaditagrawal/t3code

Length of output: 12244


🏁 Script executed:

#!/bin/bash
set -u

echo "ClaudeProvider.ts imports and ClaudeProvider layer/provider block:"
sed -n '1,90p' apps/server/src/provider/Layers/ClaudeProvider.ts
sed -n '560,680p' apps/server/src/provider/Layers/ClaudeProvider.ts

echo
echo "All non-test TS lines mentioning ClaudeProvider:"
rg -n -C3 'ClaudeProvider|makeClaudeProvider|new ClaudeProvider|ProviderDriver|ProviderDriver<ClaudeSettings' apps/server/src/provider apps/server/src -g '!**/*.test.ts'

echo
echo "Exact resolveClaudeSdkExecutablePath call sites in app/source TS:"
rg -n -C4 'resolveClaudeSdkExecutablePath' apps/server/src --glob '*.ts' --glob '!**/*.test.ts'

Repository: aaditagrawal/t3code

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -u

echo "ClaudeProvider.ts imports and ClaudeProvider driver exports:"
sed -n '1,95p' apps/server/src/provider/Layers/ClaudeProvider.ts
sed -n '600,640p' apps/server/src/provider/Layers/ClaudeProvider.ts
rg -n 'export const ClaudeProvider|ClaudeProviderLive|makeClaudeProvider|ClaudeDriver|Driver kind' apps/server/src/provider/Layers/ClaudeProvider.ts

echo
echo "Registry/driver construction around ClaudeDriver:"
sed -n '100,180p' apps/server/src/provider/Drivers/ClaudeDriver.ts
rg -n -C2 'ClaudeDriver|drivers:|new ClaudeProvider|ProviderRegistry|ProviderInstanceRegistry' apps/server/src/provider -g '*.ts' --glob '!**/*.test.ts'

Repository: aaditagrawal/t3code

Length of output: 50375


Provide the Claude SDK resolver services in the Claude driver layer.

makeClaudeAdapter() is called from ClaudeDriver.create(), but that driver only declares ClaudeDriverEnv and ClaudeDriverEnv does not include HostProcessPlatform, SpawnExecutableResolution, or ClaudeExecutableFileCheck (apps/server/src/provider/Drivers/ClaudeDriver.ts:84-92). The one production construction site at apps/server/src/provider/Drivers/ClaudeDriver.ts:149 will not satisfy the new services required by resolveClaudeSdkExecutablePath; export a small runtime layer or add these services to the driver’s context.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/Layers/ClaudeAdapter.ts` around lines 1361 - 1364,
Update makeClaudeAdapter and its Claude driver construction path to provide the
services required by resolveClaudeSdkExecutablePath: HostProcessPlatform,
SpawnExecutableResolution, and ClaudeExecutableFileCheck. Prefer exporting a
small runtime layer from the Claude adapter and merge it into the layer used by
ClaudeDriver.create(), or explicitly extend ClaudeDriverEnv and supply these
services at the production construction site.

Comment on lines +657 to +691
const loadInventoryFromCli: OpenCodeRuntimeShape["loadInventoryFromCli"] = (input) =>
Effect.gen(function* () {
const env = input.environment !== undefined ? { environment: input.environment } : ({} as {});

const runModelsCli = () =>
runOpenCodeCommand({
binaryPath: input.binaryPath,
args: ["models", "--verbose"],
...env,
}).pipe(Effect.exit);
const runAgentsCli = () =>
runOpenCodeCommand({ binaryPath: input.binaryPath, args: ["agent", "list"], ...env }).pipe(
Effect.exit,
);

// First attempt — run both in parallel
let [modelsResult, agentsResult] = yield* Effect.all([runModelsCli(), runAgentsCli()], {
concurrency: "unbounded",
});

// Retry once after 1s on transient failures (e.g. SQLite "database is locked")
const needsModelsRetry = modelsResult._tag === "Failure" || modelsResult.value.code !== 0;
const needsAgentsRetry = agentsResult._tag === "Failure" || agentsResult.value.code !== 0;
if (needsModelsRetry || needsAgentsRetry) {
yield* Effect.sleep("1 second");
const [m2, a2] = yield* Effect.all(
[
needsModelsRetry ? runModelsCli() : Effect.succeed(modelsResult),
needsAgentsRetry ? runAgentsCli() : Effect.succeed(agentsResult),
],
{ concurrency: "unbounded" },
);
modelsResult = m2;
agentsResult = a2;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound the CLI inventory commands with a timeout.

runOpenCodeCommand waits on child.exitCode with no deadline, so a wedged opencode models --verbose (or agent list) blocks the provider health check indefinitely — and the retry path can double that. The previous external-server flow at least had DEFAULT_OPENCODE_SERVER_TIMEOUT_MS. Add a timeout so the graceful-degradation branch below is actually reachable.

🛡️ Suggested fix
       const runModelsCli = () =>
         runOpenCodeCommand({
           binaryPath: input.binaryPath,
           args: ["models", "--verbose"],
           ...env,
-        }).pipe(Effect.exit);
+        }).pipe(Effect.timeout(OPENCODE_CLI_INVENTORY_TIMEOUT), Effect.exit);
       const runAgentsCli = () =>
         runOpenCodeCommand({ binaryPath: input.binaryPath, args: ["agent", "list"], ...env }).pipe(
-          Effect.exit,
+          Effect.timeout(OPENCODE_CLI_INVENTORY_TIMEOUT),
+          Effect.exit,
         );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const loadInventoryFromCli: OpenCodeRuntimeShape["loadInventoryFromCli"] = (input) =>
Effect.gen(function* () {
const env = input.environment !== undefined ? { environment: input.environment } : ({} as {});
const runModelsCli = () =>
runOpenCodeCommand({
binaryPath: input.binaryPath,
args: ["models", "--verbose"],
...env,
}).pipe(Effect.exit);
const runAgentsCli = () =>
runOpenCodeCommand({ binaryPath: input.binaryPath, args: ["agent", "list"], ...env }).pipe(
Effect.exit,
);
// First attempt — run both in parallel
let [modelsResult, agentsResult] = yield* Effect.all([runModelsCli(), runAgentsCli()], {
concurrency: "unbounded",
});
// Retry once after 1s on transient failures (e.g. SQLite "database is locked")
const needsModelsRetry = modelsResult._tag === "Failure" || modelsResult.value.code !== 0;
const needsAgentsRetry = agentsResult._tag === "Failure" || agentsResult.value.code !== 0;
if (needsModelsRetry || needsAgentsRetry) {
yield* Effect.sleep("1 second");
const [m2, a2] = yield* Effect.all(
[
needsModelsRetry ? runModelsCli() : Effect.succeed(modelsResult),
needsAgentsRetry ? runAgentsCli() : Effect.succeed(agentsResult),
],
{ concurrency: "unbounded" },
);
modelsResult = m2;
agentsResult = a2;
}
const loadInventoryFromCli: OpenCodeRuntimeShape["loadInventoryFromCli"] = (input) =>
Effect.gen(function* () {
const env = input.environment !== undefined ? { environment: input.environment } : ({} as {});
const runModelsCli = () =>
runOpenCodeCommand({
binaryPath: input.binaryPath,
args: ["models", "--verbose"],
...env,
}).pipe(Effect.timeout(OPENCODE_CLI_INVENTORY_TIMEOUT), Effect.exit);
const runAgentsCli = () =>
runOpenCodeCommand({ binaryPath: input.binaryPath, args: ["agent", "list"], ...env }).pipe(
Effect.timeout(OPENCODE_CLI_INVENTORY_TIMEOUT),
Effect.exit,
);
// First attempt — run both in parallel
let [modelsResult, agentsResult] = yield* Effect.all([runModelsCli(), runAgentsCli()], {
concurrency: "unbounded",
});
// Retry once after 1s on transient failures (e.g. SQLite "database is locked")
const needsModelsRetry = modelsResult._tag === "Failure" || modelsResult.value.code !== 0;
const needsAgentsRetry = agentsResult._tag === "Failure" || agentsResult.value.code !== 0;
if (needsModelsRetry || needsAgentsRetry) {
yield* Effect.sleep("1 second");
const [m2, a2] = yield* Effect.all(
[
needsModelsRetry ? runModelsCli() : Effect.succeed(modelsResult),
needsAgentsRetry ? runAgentsCli() : Effect.succeed(agentsResult),
],
{ concurrency: "unbounded" },
);
modelsResult = m2;
agentsResult = a2;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/opencodeRuntime.ts` around lines 657 - 691, Bound
both inventory command effects created by runModelsCli and runAgentsCli with the
existing DEFAULT_OPENCODE_SERVER_TIMEOUT_MS, ensuring timed-out attempts produce
failures handled by the current retry and graceful-degradation flow. Apply the
timeout to each attempt, including retries, without changing the parallel
execution or retry conditions.

Comment on lines +1237 to +1241
return () => {
unregisterUpdate();
unregisterFocus();
unregisterBlur();
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear mirrored chip attributes during cleanup.

The effect unregisters its listeners but does not remove data-composer-chip-selected from currently selected DOM nodes. After a plugin remount or React Strict Mode effect replay, stale highlights can remain because the new selectedKeys set starts empty.

Proposed fix
    return () => {
+     applyKeys(new Set());
      unregisterUpdate();
      unregisterFocus();
      unregisterBlur();
    };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return () => {
unregisterUpdate();
unregisterFocus();
unregisterBlur();
};
return () => {
applyKeys(new Set());
unregisterUpdate();
unregisterFocus();
unregisterBlur();
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/ComposerPromptEditor.tsx` around lines 1237 - 1241,
Update the cleanup returned by the effect in ComposerPromptEditor to remove
data-composer-chip-selected from all currently selected DOM nodes before or
alongside unregistering the update, focus, and blur listeners, ensuring remounts
and Strict Mode replays cannot retain stale highlights.

Comment on lines +116 to +153
const navigateToResolvedUrl = useCallback(
async (resolvedUrl: string) => {
if (tabId && previewBridge) {
// Drive the webview imperatively; `usePreviewBridge` mirrors the
// resolved URL back to the server so other clients stay in sync.
await previewBridge.navigate(tabId, resolvedUrl);
rememberPreviewUrl(threadRef, resolvedUrl);
} else {
await openPreviewSession({
openPreview: open,
threadRef,
url: resolvedUrl,
});
}
},
[open, tabId, threadRef],
);

const handleSubmitUrl = useCallback(
async (next: string) => {
try {
const resolvedUrl = resolveDiscoveredServerUrl(threadRef.environmentId, next);
if (tabId && previewBridge) {
// Drive the webview imperatively; `usePreviewBridge` mirrors the
// resolved URL back to the server so other clients stay in sync.
await previewBridge.navigate(tabId, resolvedUrl);
rememberPreviewUrl(threadRef, resolvedUrl);
} else {
await openPreviewSession({
openPreview: open,
threadRef,
url: resolvedUrl,
});
}
await navigateToResolvedUrl(normalizePreviewUrl(next));
} catch {
// Server-side `failed` event renders the unreachable view.
}
},
[open, tabId, threadRef],
[navigateToResolvedUrl],
);

const handleOpenServerUrl = useCallback(
async (next: string) => {
try {
await navigateToResolvedUrl(resolveDiscoveredServerUrl(threadRef.environmentId, next));
} catch {
// Server-side `failed` event renders the unreachable view.
}
},
[navigateToResolvedUrl, threadRef.environmentId],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Invalid URL bar submissions now fail silently.

handleSubmitUrl normalizes next inside the same try/catch that wraps navigateToResolvedUrl. Since normalizePreviewUrl throws before any bridge/server call is made on invalid input (empty, unparsable, non-http(s)), the catch swallows it — but its comment ("Server-side failed event renders the unreachable view") no longer applies here: no server call happens, so no unreachable view ever renders. The user gets no feedback at all when submitting a malformed URL, whereas handleOpenServerUrl's resolveDiscoveredServerUrl falls back to the raw URL on normalization failure instead of leaving the user hanging.

🛡️ Proposed fix: surface normalization failures with a toast
   const handleSubmitUrl = useCallback(
     async (next: string) => {
+      let resolvedUrl: string;
+      try {
+        resolvedUrl = normalizePreviewUrl(next);
+      } catch {
+        toastManager.add({
+          type: "error",
+          title: "Invalid URL",
+          description: "Enter a valid http(s) URL.",
+        });
+        return;
+      }
       try {
-        await navigateToResolvedUrl(normalizePreviewUrl(next));
+        await navigateToResolvedUrl(resolvedUrl);
       } catch {
         // Server-side `failed` event renders the unreachable view.
       }
     },
     [navigateToResolvedUrl],
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/preview/PreviewView.tsx` around lines 116 - 153,
Update handleSubmitUrl so normalizePreviewUrl runs outside the try/catch that
handles navigateToResolvedUrl failures. Catch normalization errors separately
and surface them through the existing toast/error-notification mechanism, while
preserving the current silent handling for server-side navigation failures.

Comment on lines 1783 to +1788
const count = threadKeys.length;
const selectedThreadEntries = threadKeys.flatMap((threadKey) => {
const threadRef = parseScopedThreadKey(threadKey);
const thread = threadRef ? readThreadShell(threadRef) : null;
return threadRef && thread ? [{ threadKey, threadRef, thread }] : [];
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Displayed/confirmed count can outnumber threads actually archived/deleted/marked.

count is taken from the raw selection before selectedThreadEntries filters out keys whose readThreadShell/parseScopedThreadKey lookup fails (e.g. a selected thread was removed via sync while still selected). The menu label ("Archive (N)"), the confirm dialog text, and the delete confirm text all use this pre-filter count, while the actual archive/delete/mark-unread loops only touch selectedThreadEntries. Users can see "Archive (3)" but only 2 threads get archived.

🐛 Proposed fix: derive count after filtering
       const threadKeys = [...useThreadSelectionStore.getState().selectedThreadKeys];
       if (threadKeys.length === 0) return;
-      const count = threadKeys.length;
       const selectedThreadEntries = threadKeys.flatMap((threadKey) => {
         const threadRef = parseScopedThreadKey(threadKey);
         const thread = threadRef ? readThreadShell(threadRef) : null;
         return threadRef && thread ? [{ threadKey, threadRef, thread }] : [];
       });
+      const count = selectedThreadEntries.length;
+      if (count === 0) return;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const count = threadKeys.length;
const selectedThreadEntries = threadKeys.flatMap((threadKey) => {
const threadRef = parseScopedThreadKey(threadKey);
const thread = threadRef ? readThreadShell(threadRef) : null;
return threadRef && thread ? [{ threadKey, threadRef, thread }] : [];
});
const threadKeys = [...useThreadSelectionStore.getState().selectedThreadKeys];
if (threadKeys.length === 0) return;
const selectedThreadEntries = threadKeys.flatMap((threadKey) => {
const threadRef = parseScopedThreadKey(threadKey);
const thread = threadRef ? readThreadShell(threadRef) : null;
return threadRef && thread ? [{ threadKey, threadRef, thread }] : [];
});
const count = selectedThreadEntries.length;
if (count === 0) return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/Sidebar.tsx` around lines 1783 - 1788, Derive the
displayed and confirmation count from selectedThreadEntries.length rather than
the raw threadKeys.length. Update the count used by the archive, delete, and
mark-unread labels/dialogs after the parseScopedThreadKey/readThreadShell
filtering in the surrounding selection flow, while keeping the action loops
based on selectedThreadEntries.

cursoragent and others added 5 commits July 25, 2026 09:03
…4168

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
normalizeCustomModelSlugs no longer takes a provider kind after pingdotgg#4168.

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
…call

Match the post-pingdotgg#4168 three-argument signature used by other adapters.

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
Acquire PubSub subscriptions synchronously for shell/thread streams so
events published during snapshot load are not lost to the fork race.
Also forward thread deleted/archived lifecycle events to detail subscribers.

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
The shell fallback snapshot test must acquire the same PubSub
subscription the production path uses after the fork-race fix.

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.