Skip to content

Integrate 8 upstream commits (security, Claude git text gen, UI fixes)#35

Merged
aaditagrawal merged 11 commits intomainfrom
upstream-sync-8commits
Mar 26, 2026
Merged

Integrate 8 upstream commits (security, Claude git text gen, UI fixes)#35
aaditagrawal merged 11 commits intomainfrom
upstream-sync-8commits

Conversation

@aaditagrawal
Copy link
Copy Markdown
Owner

@aaditagrawal aaditagrawal commented Mar 26, 2026

Summary

  • Cherry-picks 8 commits from pingdotgg/t3code main into the fork
  • Resolves conflicts with the fork's 8-provider architecture (codex, copilot, claudeAgent, cursor, opencode, geminiCli, amp, kilo)

Upstream commits integrated

PR Description Conflict resolution
pingdotgg#1328 docs: properly linked CLAUDE.md to AGENTS.md Clean
pingdotgg#1416 Add copy action to error toast messages Clean
pingdotgg#1423 fix(desktop): prevent git actions and terminal toggle from overlapping Clean
pingdotgg#1382 fix(web): clear composer border after picker dismissal Clean
pingdotgg#1415 fix(web): add pointer cursor to composer send button Clean
pingdotgg#1398 fix(security): send sensitive config over a bootstrap fd Minor — merged channel constants in desktop main/preload
pingdotgg#1403 fix(web): prevent provider model submenu overlap Manual — ported sideOffset={4} to fork's multi-provider picker + added upstream test
pingdotgg#1323 feat(git): Add Claude as a TextGenerationProvider Heavy — 14 file conflicts. Kept fork's 8-provider architecture; accepted new files (ClaudeTextGeneration, RoutingTextGeneration, Prompts, Utils); modelSelection.ts re-exports from appSettings.ts; widened RoutingTextGeneration routing to accept all ProviderKind values

Test plan

  • bun typecheck passes (0 errors across all 7 packages)
  • bun lint passes (0 errors)
  • Verify existing provider functionality (codex, copilot, claude, cursor, opencode, geminiCli, amp, kilo) is unaffected
  • Verify Claude git text generation (commit messages, PR content, branch names) works via claude CLI
  • Verify error toast copy button works
  • Verify composer send button shows pointer cursor
  • Verify provider model submenu has visible gap from parent menu

Summary by CodeRabbit

  • New Features

    • Claude model support plus routing so users can pick Codex or Claude; model selection included on git/text-generation requests and defaults updated
    • Secure one-shot bootstrap ingestion for safer startup and improved desktop IPC to reliably obtain websocket URL
  • UI

    • Copy-to-clipboard for error toasts; expanded picker/traits trigger styling and variant/trigger class customization
  • Documentation

    • CLI docs updated with bootstrap-FD and auth-token guidance
  • Tests

    • Expanded coverage for bootstrap, providers, model-selection and related flows

jakob1379 and others added 8 commits March 26, 2026 09:46
Co-authored-by: Jakob Guldberg Aaes <jakob1379@gmali.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
pingdotgg#1423)

Co-authored-by: morgan ye <morganye@morgans-MacBook-Pro.local>
)

Co-authored-by: codex <codex@users.noreply.github.com>
Port upstream sideOffset={4} fix and submenu gap test to fork's
multi-provider picker structure.
Co-authored-by: keyzou <keyzou@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 779d7236-d3c4-4586-8b10-40586838cd10

📥 Commits

Reviewing files that changed from the base of the PR and between 6b03780 and 77a7335.

📒 Files selected for processing (1)
  • apps/server/src/git/Layers/GitManager.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/server/src/git/Layers/GitManager.ts

📝 Walkthrough

Walkthrough

Adds a one-shot FD-based bootstrap envelope mechanism for startup config, integrates per-request ModelSelection routing between Codex and Claude providers, centralizes prompt/schema utilities, threads modelSelection through server/web/orchestration layers, and updates desktop spawn/stdio to deliver the bootstrap envelope.

Changes

Cohort / File(s) Summary
Bootstrap envelope & docs
REMOTE.md, CLAUDE.md, apps/server/src/bootstrap.ts, apps/server/src/bootstrap.test.ts
Add FD-based bootstrap reader (platform-aware FD resolution, line-delimited JSON, decoding, timeout); docs updated with --bootstrap-fd / T3CODE_BOOTSTRAP_FD guidance; tests for availability/timeout/decoding.
Desktop spawn & IPC
apps/desktop/src/main.ts, apps/desktop/src/preload.ts
Spawn backend with --bootstrap-fd 3 and adjusted stdio wiring; parent writes JSON envelope to FD 3 then closes; added desktop:get-ws-url IPC for synchronous renderer retrieval.
Server CLI & bootstrap wiring
apps/server/src/main.ts, apps/server/src/main.test.ts
Consume bootstrap envelope in CLI/env precedence resolution, add --bootstrap-fd flag, validate mode/port, unify precedence (CLI → env → bootstrap), and tests for precedence and bootstrap behavior.
Text-generation providers & routing
apps/server/src/git/Layers/ClaudeTextGeneration.ts, apps/server/src/git/Layers/CodexTextGeneration.ts, apps/server/src/git/Layers/RoutingTextGeneration.ts, apps/server/src/serverLayers.ts
Introduce Claude CLI-backed layer; refactor Codex to accept modelSelection and shared utilities; add routing layer to dispatch per-request to Claude or Codex and swap into server wiring.
Prompts & shared utils
apps/server/src/git/Prompts.ts, apps/server/src/git/Utils.ts, apps/server/src/git/Prompts.test.ts
Centralize prompt builders, JSON-schema conversion, truncation/sanitizers, and CLI error normalization; add tests validating prompt contents and CLI error normalization.
Contracts & model-selection propagation
apps/server/src/git/Services/TextGeneration.ts, packages/contracts/src/git.ts, packages/contracts/src/model.ts, packages/contracts/src/git.test.ts
Add required modelSelection to generation input contracts, introduce TextGenerationProvider, add default claudeAgent model, and update contract tests.
Git manager & orchestration updates
apps/server/src/git/Layers/GitManager.ts, apps/server/src/git/Layers/GitManager.test.ts, apps/server/src/orchestration/...
Thread modelSelection through runStackedAction flows and branch/PR generation; import isGitRepository from new utils and pass default modelSelection where needed.
Web app API & model selection facade
apps/web/src/modelSelection.ts, apps/web/src/appSettings.ts, apps/web/src/lib/gitReactQuery.ts, apps/web/src/wsNativeApi.test.ts, apps/web/src/...
Add modelSelection re-export module, update web APIs/React Query helpers to accept and forward modelSelection, adjust imports and tests accordingly.
Web components & UX adjustments
apps/web/src/components/... (ProviderModelPicker, TraitsPicker, ChatView, GitActionsControl, ui/toast, tests...)
Add triggerVariant/triggerClassName props, remove required threadId in TraitsPicker (support external persistence), adjust styling and toast copy button, ensure modelSelection flows into git actions; update tests.
Dev-runner / desktop env behavior
scripts/dev-runner.ts, scripts/dev-runner.test.ts
Special-case dev:desktop to strip backend/bootstrap-related T3CODE_* env vars while preserving renderer ports/URLs; add tests.
Misc small changes & tests
apps/server/src/bootstrap.test.ts, apps/server/src/git/*/*.test.ts, packages/contracts/src/*.test.ts, other test tweaks
Add/modify multiple tests to cover bootstrap FD, modelSelection propagation, fake CLI behaviors, and updated expectations.

Sequence Diagram(s)

sequenceDiagram
  participant Parent as Desktop Main
  participant OS as Operating System
  participant Child as Backend Process
  Parent->>OS: spawn backend with FD 3 included in stdio
  Note right of OS: FD 3 inherited by Child
  Parent->>Child: wait for child ready
  Parent->>OS: write one-line JSON envelope to FD 3 and close
  OS->>Child: deliver envelope on FD 3
  Child->>Child: read line, decode schema, apply bootstrap config
  alt write to FD fails or FD closed
    Child->>Parent: exit / no bootstrap -> Parent schedules restart
  end
Loading
sequenceDiagram
  participant Client as Requester (service)
  participant Router as RoutingTextGeneration
  participant Codex as CodexTextGeneration
  participant Claude as ClaudeTextGeneration
  Client->>Router: generateCommitMessage(input with modelSelection)
  Router->>Router: inspect input.modelSelection.provider
  alt provider == "claudeAgent"
    Router->>Claude: forward request (propagate modelSelection)
    Claude->>Router: structured JSON response
  else
    Router->>Codex: forward request (propagate modelSelection)
    Codex->>Router: structured JSON response
  end
  Router->>Client: validate, sanitize, return output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped an envelope through FD three,
a tiny JSON, one-shot, just for me.
Codex and Claude take turns in the light,
prompts snipped tidy, routing polite.
Desktop hums softly — hop, build, delight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: integrating 8 upstream commits covering security, Claude text generation, and UI fixes.
Description check ✅ Passed The description provides a comprehensive summary with upstream commits table, conflict resolution notes, and detailed test plan. It covers what changed, why, and how to verify.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch upstream-sync-8commits

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 and usage tips.

@github-actions github-actions bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). labels Mar 26, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

Caution

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

⚠️ Outside diff range comments (3)
apps/server/src/serverLayers.ts (1)

111-156: ⚠️ Potential issue | 🟠 Major

Don't wire this router globally until it can actually dispatch all supported providers.

git.runStackedAction now carries modelSelection for every ProviderKind, and the web client forwards the active provider. Since this router only special-cases claudeAgent and falls back to Codex for everything else, requests from Cursor/Copilot/Gemini/Amp/Kilo will either hit Codex with unsupported model IDs or silently lose provider-specific options.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/serverLayers.ts` around lines 111 - 156, The global wiring of
the Git router should be gated until it can route by ProviderKind and honor
modelSelection; stop registering the router globally in GitManagerLive /
gitManagerLayer (and any initialization that exposes git.runStackedAction) and
instead only wire it behind a feature-flag or provider-aware facade. Update the
code paths that currently special-case claudeAgent and fallback to Codex so they
instead validate ProviderKind + modelSelection and either dispatch to a
provider-specific handler or return a clear unsupported-provider error; locate
symbols GitManagerLive, gitManagerLayer, and any usages of git.runStackedAction
to implement the guard and defer global registration until all ProviderKinds are
supported. Ensure the web client’s forwarded provider is used to select the
correct handler rather than defaulting to Codex.
apps/server/src/git/Layers/GitManager.ts (1)

1276-1289: ⚠️ Potential issue | 🟡 Minor

Fix the CI formatting failure here.

Line 1287 is still a single long runPrStep(...) call, and CI is already failing oxfmt --check on this file. Please reflow this block before merge.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/git/Layers/GitManager.ts` around lines 1276 - 1289, The long
single-line call to runPrStep inside the pr assignment breaks the oxfmt layout;
reflow the nested Effect.flatMap/Effect.gen block so the runPrStep call and its
arguments are split across multiple lines to satisfy formatter rules. Locate the
pr construction (the wantsPr ? yield* progress.emit(...).pipe(Effect.flatMap(()
=> Effect.gen(function* () { currentPhase = "pr"; return yield*
runPrStep(input.modelSelection, input.cwd, currentBranch, input.provider,
input.model); }), ...)) and reformat the inner generator to place currentPhase =
"pr"; on its own line and the return yield* runPrStep(...) with each argument on
its own line (or otherwise wrapped) so oxfmt --check passes. Ensure you only
change formatting, preserving the use of runPrStep, progress.emit,
Effect.flatMap, and Effect.gen.
apps/server/src/git/Layers/CodexTextGeneration.ts (1)

142-159: ⚠️ Potential issue | 🟠 Major

Use the normalized reasoning effort here.

Lines 142-159 compute normalizedOptions, but reasoningEffort still comes from raw modelSelection.options. If the normalizer downgrades or strips unsupported effort values for a given model, this code will still emit the invalid one and make Codex requests fail.

♻️ Proposed fix
         const normalizedOptions = normalizeCodexModelOptions(
           modelSelection.model,
           modelSelection.options,
         );
         const reasoningEffort =
-          modelSelection.options?.reasoningEffort ?? CODEX_GIT_TEXT_GENERATION_REASONING_EFFORT;
+          normalizedOptions?.reasoningEffort ?? CODEX_GIT_TEXT_GENERATION_REASONING_EFFORT;
         const command = ChildProcess.make(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/git/Layers/CodexTextGeneration.ts` around lines 142 - 159,
The code computes normalizedOptions via normalizeCodexModelOptions but still
reads reasoningEffort from modelSelection.options; update the reasoningEffort
assignment to use the normalized value (e.g., normalizedOptions?.reasoningEffort
?? CODEX_GIT_TEXT_GENERATION_REASONING_EFFORT) so the ChildProcess.make call
(the `command` construction) emits the validated/normalized effort; keep the
existing fallback constant and leave the fastMode handling
(normalizedOptions?.fastMode) unchanged.
🧹 Nitpick comments (4)
apps/web/src/components/ui/toast.tsx (1)

309-317: Consider extracting the repeated toast-header + copy-button block.

The same header/copy logic appears in both non-anchored and anchored branches. A small shared component/helper would reduce duplication and future drift risk.

Also applies to: 403-411

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/toast.tsx` around lines 309 - 317, Extract the
duplicated header+copy-button markup into a small reusable component (e.g.,
ToastHeader or ToastHeaderWithCopy) that renders the container div, Toast.Title
and conditionally the CopyErrorButton when toast.type === "error" and typeof
toast.description === "string"; replace the two occurrences (the block
containing Toast.Title and CopyErrorButton in the non-anchored and anchored
branches) with this new component and pass the toast object or the minimal props
(title slot and description/type) to keep behavior identical; ensure the new
component uses the same classes ("flex items-center justify-between gap-1" and
"min-w-0 break-words font-medium") and exports from the same file so imports
remain straightforward.
apps/web/src/components/chat/TraitsPicker.tsx (1)

121-140: Keep trigger-only props off TraitsMenuContent's public API.

triggerVariant and triggerClassName are added to TraitsMenuContentProps, but TraitsMenuContent never reads them. Since the component is exported, this widens the API with no-op props and also lets those fields leak into the ...persistence rest object. I'd split picker trigger props into a separate TraitsPickerProps type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/chat/TraitsPicker.tsx` around lines 121 - 140, The
public props interface TraitsMenuContentProps incorrectly includes trigger-only
props (triggerVariant, triggerClassName) that TraitsMenuContent does not consume
and which leak into the rest/persistence object; remove these two props from
TraitsMenuContentProps and instead define a separate TraitsPickerProps (or
extend an existing picker prop type) that owns triggerVariant and
triggerClassName, update any call sites to pass trigger props to the picker
component rather than to TraitsMenuContent, and ensure TraitsMenuContentImpl
only receives the intended props (provider, model, prompt, onPromptChange,
modelOptions, allowPromptInjectedEffort and TraitsPersistence) so the
...persistence rest no longer contains triggerVariant/triggerClassName.
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts (1)

462-465: Use provider-aware model selection for worktree branch generation.

Hardcoding Codex here bypasses the thread’s chosen model/provider. Prefer using thread selection when supported, with Codex as fallback.

Suggested refactor
+    const branchNameModelSelection =
+      thread.modelSelection.provider === "claudeAgent"
+        ? thread.modelSelection
+        : {
+            provider: "codex",
+            model: DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER.codex,
+          };
+
     yield* textGeneration
       .generateBranchName({
         cwd,
         message: input.messageText,
         ...(attachments.length > 0 ? { attachments } : {}),
-        modelSelection: {
-          provider: "codex",
-          model: DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER.codex,
-        },
+        modelSelection: branchNameModelSelection,
       })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/orchestration/Layers/ProviderCommandReactor.ts` around lines
462 - 465, The modelSelection block currently hardcodes provider "codex" and
DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER.codex; change it to prefer the
thread's chosen model/provider when available (use the thread or context
modelSelection object used elsewhere in ProviderCommandReactor or the worktree
branch generation code) and only fall back to
DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER.codex if the thread selection is
missing or unsupported; update references to modelSelection so provider and
model are derived from the thread's selection first, then fallback to the codex
defaults.
packages/contracts/src/git.ts (1)

80-85: Avoid keeping two sources of truth for the model selection.

Now that modelSelection is required, leaving top-level provider and model in the same payload lets callers send contradictory values. apps/web/src/components/GitActionsControl.tsx already sends both forms, so this contract is ambiguous until one of them is removed or clearly deprecated.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/contracts/src/git.ts` around lines 80 - 85, The schema currently
exposes two sources of truth—provider and model plus modelSelection—which allows
contradictory payloads; remove the top-level provider and model fields from the
exported schema and require callers to use modelSelection only (update any
callers like GitActionsControl.tsx to stop sending provider/model), or if you
need a transitional step, add a validation in the schema (in the file where
provider, model, modelSelection are defined) that rejects payloads where
provider/model are present alongside modelSelection or enforces they match
modelSelection; update the Schema definitions for provider, model, and
modelSelection accordingly and update callers to emit only modelSelection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/preload.ts`:
- Around line 22-25: The getWsUrl implementation currently returns empty string
from ipcRenderer.sendSync(GET_WS_URL_CHANNEL) as-is, which should be treated as
the “not ready” sentinel; update getWsUrl (in the preload export for
GET_WS_URL_CHANNEL) to normalize the result by returning null when result is not
a non-empty string (e.g., if typeof result !== "string" or result.trim() ===
""), otherwise return the string—this ensures callers expecting null for “not
ready yet” will receive null instead of "".

In `@apps/server/src/bootstrap.test.ts`:
- Around line 50-95: The two fd-related tests in bootstrap.test.ts that call
NFS.openSync("/dev/null"), execFileSync("mkfifo"), spawn("sh", ...) and use
readBootstrapEnvelope/TestEnvelopeSchema should be skipped on Windows; add a
platform guard at the start of each test body (check process.platform ===
"win32") and return an immediate Effect (e.g. Effect.unit) when on Windows so
the test is effectively no-op there, leaving the rest of the POSIX logic
(mkfifo, spawn, TestClock.adjust, Fiber.join) unchanged.

In `@apps/server/src/bootstrap.ts`:
- Around line 21-22: When a bootstrap FD was requested but unreadable the code
currently returns Option.none(), making an explicit bootstrap failure
indistinguishable from “no bootstrap requested”; change the behavior in the
isFdReady(fd) check (the fdReady/Option.none() branch) so that if a bootstrap FD
was explicitly provided (detect via the bootstrap FD flag / presence of fd) and
isFdReady(fd) returns false you fail closed (throw an Error or exit with
non‑zero) and log the failure, instead of returning Option.none(); apply the
same change to the other branches that collapse failures into Option.none() (the
blocks referencing Option.none() around the bootstrap FD handling).

In `@apps/server/src/git/Layers/ClaudeTextGeneration.test.ts`:
- Around line 19-60: The generated fake Claude shim in makeFakeClaudeBinary is
currently a POSIX shell script requiring chmod and using ':' as PATH delimiter
which breaks on Windows; replace it with a cross-platform Node-based shim (or
create both a Unix executable and a Windows .cmd wrapper) that reads env vars,
stdin and writes stdout/stderr/exit code accordingly, write the shim using the
existing FileSystem/FileSystem and Path/Path handles, only call fs.chmod for
non-Windows platforms, and compute the PATH delimiter via path.delimiter (or
Node's Path.path.delimiter) instead of hardcoding ':' so the tests run on
Windows and Unix.

In `@apps/server/src/git/Layers/ClaudeTextGeneration.ts`:
- Around line 89-103: The runClaudeCommand call constructs the Claude CLI args
incorrectly: ChildProcess.make currently passes JSON.stringify(settings) to the
--settings flag (which expects a file path) and uses
--dangerously-skip-permissions (too-broad). Fix by persisting settings to a temp
file and passing that filepath to --settings (or drop the flag if settings is
empty/not needed) and replace --dangerously-skip-permissions with explicit tool
restrictions e.g. add --allowedTools "Read" and --disallowedTools
"Edit,Bash,Write" when building the args in runClaudeCommand/ChildProcess.make
so the CLI receives a settings filepath and least-privilege permissions.

In `@apps/server/src/git/Layers/GitManager.test.ts`:
- Around line 37-40: DEFAULT_TEST_MODEL_SELECTION is forcing Codex
modelSelection into tests and masking provider/model routing regressions; update
the test helpers so runStackedAction does not unconditionally inject
DEFAULT_TEST_MODEL_SELECTION. Specifically, modify runStackedAction to accept an
optional modelSelection parameter and merge it so any modelSelection passed by a
test overrides the default (or remove the default entirely), update usages that
relied on the constant (e.g., places referencing DEFAULT_TEST_MODEL_SELECTION)
so tests explicitly pass the intended provider/model when needed, and ensure
test fixtures in GitManager.test.ts and the other affected block now assert
behavior with the actual modelSelection they intend to exercise.

In `@apps/server/src/git/Prompts.ts`:
- Around line 123-125: The attachment lines currently embed attachment.name and
attachment.mimeType directly in the prompt (see attachmentLines and
input.attachments), which can include control characters or newlines; add a
sanitizer that normalizes each string to a single-line safe representation
(e.g., replace all control characters and newlines with spaces, collapse
multiple whitespace to a single space, trim) and use it when building the list
(sanitize attachment.name and attachment.mimeType before interpolation);
optionally convert sizeBytes to a safe number/string format as well and
centralize this logic in a small helper like sanitizeSingleLine used by the map.
- Around line 148-150: The outputSchema currently allows any string for branch;
update the Schema.Struct definition for outputSchema to enforce a git-safe
branch format by either (1) replacing Schema.String for the branch field with a
refined/regex string that matches allowed git branch names, or (2) apply an
in-schema transform that runs the existing sanitizeFeatureBranchName() or
sanitizeBranchFragment() on the parsed value so branch is always normalized;
target the outputSchema symbol and ensure callers can rely on branch being
git-safe without additional sanitization.

In `@apps/server/src/main.test.ts`:
- Around line 158-164: openBootstrapFd currently opens a raw file descriptor via
FileSystem.FileSystem -> makeTempFileScoped -> fs.open and returns fd without
closing it, leaking FDs; change it to use Effect.acquireRelease (or an
equivalent acquire/release pattern) so the acquisition opens the fd and the
release calls fs.close(fd) (or fs.closeFile) to always close the descriptor
after runCli finishes, or alternatively ensure callers of openBootstrapFd call
fs.close(fd) in a finally/release step; reference openBootstrapFd,
FileSystem.FileSystem, makeTempFileScoped, fs.open, and fs.close when
implementing the acquire/release.

In `@apps/server/src/main.ts`:
- Around line 125-128: The bootstrapFd config currently accepts any integer (via
Config.int("T3CODE_BOOTSTRAP_FD").pipe(Config.option,
Config.map(Option.getOrUndefined)), which allows negatives and stdio fds);
change the config boundary to validate the FD (e.g., require non-negative or >=3
depending on allowed pipes) by refining the Config.int schema before turning it
into an option so invalid values are rejected early; update the same pattern
used elsewhere (the other bootstrapFd occurrence) so both use the validated
schema while keeping the Option.getOrUndefined mapping and the
T3CODE_BOOTSTRAP_FD symbol.
- Around line 107-112: The current CliEnvConfig mode mapping
(Config.string("T3CODE_MODE") -> Option.map((value) => (value === "desktop" ?
"desktop" : "web"))) incorrectly coerces any non-"desktop" string to "web";
change the pipeline for mode in Config.all so only the allowed literals are
accepted (e.g., use Option.filter or an explicit check that returns "desktop"
for "desktop", "web" for "web", and otherwise maps to Option.none/undefined)
instead of defaulting unknown values to "web" — update the
Config.string("T3CODE_MODE") -> pipe so invalid values are filtered out like the
bootstrap validation does.

In `@apps/web/src/components/chat/ProviderModelPicker.tsx`:
- Around line 9-12: The file ProviderModelPicker.tsx is failing the oxfmt check;
run the formatter (oxfmt) on
apps/web/src/components/chat/ProviderModelPicker.tsx to reformat the imports and
JSX (affecting the import block and the JSX regions around lines 255-287 and
343-429), then stage and commit the reformatted file so CI passes; ensure no
semantic changes to ProviderModelPicker, ProviderPickerKind, or any referenced
components like Button/buttonVariants while formatting.

In `@apps/web/src/components/ChatView.tsx`:
- Around line 131-135: Run the project's formatter on this file to satisfy CI:
run oxfmt (or the project's formatting command) against the ChatView component
and commit the changes so the import block and entire file conform to oxfmt
style; no code logic changes needed, just reformat the file that imports
getProviderStartOptions, useAppSettings, getCustomModelsByProvider, and
resolveAppModelSelection (and also reformat the other reported ranges) before
merging.

In `@apps/web/src/components/ui/toast.tsx`:
- Around line 45-49: The icon-only copy button lacks an explicit accessible
name; update the button element that calls copyToClipboard(text) in the toast
component to include an aria-label (e.g., aria-label="Copy error") alongside the
existing title so screen readers announce it reliably; ensure you add the
attribute on the same button element that has onClick={() =>
copyToClipboard(text)} in the toast UI component.

In `@REMOTE.md`:
- Around line 25-26: The phrase "command line arguments" in the REMOTE.md note
should be hyphenated; update the sentence that mentions `--bootstrap-fd <fd>` to
read "command-line arguments" (replace the unhyphenated phrase) so the note now
says the auth token can be delivered without putting it in process environment
or command-line arguments.

In `@scripts/dev-runner.ts`:
- Around line 156-174: The forwarded env object output (created from baseEnv in
scripts/dev-runner.ts) may unintentionally pass a parent T3CODE_BOOTSTRAP_FD
into the child; remove that descriptor from output after cloning by deleting the
T3CODE_BOOTSTRAP_FD key (e.g. add delete output.T3CODE_BOOTSTRAP_FD) so it never
survives into the turbo child regardless of isDesktopMode branch handling; apply
this near where output is constructed (alongside the existing deletes for
T3CODE_* in the else branch) to ensure the child process cannot inherit the FD.

---

Outside diff comments:
In `@apps/server/src/git/Layers/CodexTextGeneration.ts`:
- Around line 142-159: The code computes normalizedOptions via
normalizeCodexModelOptions but still reads reasoningEffort from
modelSelection.options; update the reasoningEffort assignment to use the
normalized value (e.g., normalizedOptions?.reasoningEffort ??
CODEX_GIT_TEXT_GENERATION_REASONING_EFFORT) so the ChildProcess.make call (the
`command` construction) emits the validated/normalized effort; keep the existing
fallback constant and leave the fastMode handling (normalizedOptions?.fastMode)
unchanged.

In `@apps/server/src/git/Layers/GitManager.ts`:
- Around line 1276-1289: The long single-line call to runPrStep inside the pr
assignment breaks the oxfmt layout; reflow the nested Effect.flatMap/Effect.gen
block so the runPrStep call and its arguments are split across multiple lines to
satisfy formatter rules. Locate the pr construction (the wantsPr ? yield*
progress.emit(...).pipe(Effect.flatMap(() => Effect.gen(function* () {
currentPhase = "pr"; return yield* runPrStep(input.modelSelection, input.cwd,
currentBranch, input.provider, input.model); }), ...)) and reformat the inner
generator to place currentPhase = "pr"; on its own line and the return yield*
runPrStep(...) with each argument on its own line (or otherwise wrapped) so
oxfmt --check passes. Ensure you only change formatting, preserving the use of
runPrStep, progress.emit, Effect.flatMap, and Effect.gen.

In `@apps/server/src/serverLayers.ts`:
- Around line 111-156: The global wiring of the Git router should be gated until
it can route by ProviderKind and honor modelSelection; stop registering the
router globally in GitManagerLive / gitManagerLayer (and any initialization that
exposes git.runStackedAction) and instead only wire it behind a feature-flag or
provider-aware facade. Update the code paths that currently special-case
claudeAgent and fallback to Codex so they instead validate ProviderKind +
modelSelection and either dispatch to a provider-specific handler or return a
clear unsupported-provider error; locate symbols GitManagerLive,
gitManagerLayer, and any usages of git.runStackedAction to implement the guard
and defer global registration until all ProviderKinds are supported. Ensure the
web client’s forwarded provider is used to select the correct handler rather
than defaulting to Codex.

---

Nitpick comments:
In `@apps/server/src/orchestration/Layers/ProviderCommandReactor.ts`:
- Around line 462-465: The modelSelection block currently hardcodes provider
"codex" and DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER.codex; change it to
prefer the thread's chosen model/provider when available (use the thread or
context modelSelection object used elsewhere in ProviderCommandReactor or the
worktree branch generation code) and only fall back to
DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER.codex if the thread selection is
missing or unsupported; update references to modelSelection so provider and
model are derived from the thread's selection first, then fallback to the codex
defaults.

In `@apps/web/src/components/chat/TraitsPicker.tsx`:
- Around line 121-140: The public props interface TraitsMenuContentProps
incorrectly includes trigger-only props (triggerVariant, triggerClassName) that
TraitsMenuContent does not consume and which leak into the rest/persistence
object; remove these two props from TraitsMenuContentProps and instead define a
separate TraitsPickerProps (or extend an existing picker prop type) that owns
triggerVariant and triggerClassName, update any call sites to pass trigger props
to the picker component rather than to TraitsMenuContent, and ensure
TraitsMenuContentImpl only receives the intended props (provider, model, prompt,
onPromptChange, modelOptions, allowPromptInjectedEffort and TraitsPersistence)
so the ...persistence rest no longer contains triggerVariant/triggerClassName.

In `@apps/web/src/components/ui/toast.tsx`:
- Around line 309-317: Extract the duplicated header+copy-button markup into a
small reusable component (e.g., ToastHeader or ToastHeaderWithCopy) that renders
the container div, Toast.Title and conditionally the CopyErrorButton when
toast.type === "error" and typeof toast.description === "string"; replace the
two occurrences (the block containing Toast.Title and CopyErrorButton in the
non-anchored and anchored branches) with this new component and pass the toast
object or the minimal props (title slot and description/type) to keep behavior
identical; ensure the new component uses the same classes ("flex items-center
justify-between gap-1" and "min-w-0 break-words font-medium") and exports from
the same file so imports remain straightforward.

In `@packages/contracts/src/git.ts`:
- Around line 80-85: The schema currently exposes two sources of truth—provider
and model plus modelSelection—which allows contradictory payloads; remove the
top-level provider and model fields from the exported schema and require callers
to use modelSelection only (update any callers like GitActionsControl.tsx to
stop sending provider/model), or if you need a transitional step, add a
validation in the schema (in the file where provider, model, modelSelection are
defined) that rejects payloads where provider/model are present alongside
modelSelection or enforces they match modelSelection; update the Schema
definitions for provider, model, and modelSelection accordingly and update
callers to emit only modelSelection.
🪄 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

Run ID: c70641d9-5f54-4ea5-8535-55c1af11060d

📥 Commits

Reviewing files that changed from the base of the PR and between e396ca1 and 8caad87.

📒 Files selected for processing (44)
  • CLAUDE.md
  • REMOTE.md
  • apps/desktop/src/main.ts
  • apps/desktop/src/preload.ts
  • apps/server/src/bootstrap.test.ts
  • apps/server/src/bootstrap.ts
  • apps/server/src/git/Layers/ClaudeTextGeneration.test.ts
  • apps/server/src/git/Layers/ClaudeTextGeneration.ts
  • apps/server/src/git/Layers/CodexTextGeneration.test.ts
  • apps/server/src/git/Layers/CodexTextGeneration.ts
  • apps/server/src/git/Layers/GitManager.test.ts
  • apps/server/src/git/Layers/GitManager.ts
  • apps/server/src/git/Layers/RoutingTextGeneration.ts
  • apps/server/src/git/Prompts.test.ts
  • apps/server/src/git/Prompts.ts
  • apps/server/src/git/Services/TextGeneration.ts
  • apps/server/src/git/Utils.ts
  • apps/server/src/git/isRepo.ts
  • apps/server/src/main.test.ts
  • apps/server/src/main.ts
  • apps/server/src/orchestration/Layers/CheckpointReactor.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
  • apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts
  • apps/server/src/serverLayers.ts
  • apps/server/src/wsServer.test.ts
  • apps/web/src/appSettings.test.ts
  • apps/web/src/appSettings.ts
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/GitActionsControl.tsx
  • apps/web/src/components/chat/ProviderModelPicker.browser.tsx
  • apps/web/src/components/chat/ProviderModelPicker.tsx
  • apps/web/src/components/chat/TraitsPicker.browser.tsx
  • apps/web/src/components/chat/TraitsPicker.tsx
  • apps/web/src/components/ui/toast.tsx
  • apps/web/src/composerDraftStore.ts
  • apps/web/src/lib/gitReactQuery.test.ts
  • apps/web/src/lib/gitReactQuery.ts
  • apps/web/src/modelSelection.ts
  • apps/web/src/wsNativeApi.test.ts
  • packages/contracts/src/git.test.ts
  • packages/contracts/src/git.ts
  • packages/contracts/src/model.ts
  • scripts/dev-runner.test.ts
  • scripts/dev-runner.ts
💤 Files with no reviewable changes (2)
  • apps/server/src/git/isRepo.ts
  • apps/web/src/appSettings.ts

Comment on lines +50 to +95
it.effect("returns none when the fd is unavailable", () =>
Effect.gen(function* () {
const fd = NFS.openSync("/dev/null", "r");
NFS.closeSync(fd);

const payload = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, { timeoutMs: 100 });
assertNone(payload);
}),
);

it.effect("returns none when the bootstrap read times out before any value arrives", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const tempDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-bootstrap-" });
const fifoPath = path.join(tempDir, "bootstrap.pipe");

yield* Effect.sync(() => execFileSync("mkfifo", [fifoPath]));

const _writer = yield* Effect.acquireRelease(
Effect.sync(() =>
spawn("sh", ["-c", 'exec 3>"$1"; sleep 60', "sh", fifoPath], {
stdio: ["ignore", "ignore", "ignore"],
}),
),
(writer) =>
Effect.sync(() => {
writer.kill("SIGKILL");
}),
);

const fd = yield* Effect.acquireRelease(
Effect.sync(() => NFS.openSync(fifoPath, "r")),
(fd) => Effect.sync(() => NFS.closeSync(fd)),
);

const fiber = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, {
timeoutMs: 100,
}).pipe(Effect.forkScoped);

yield* Effect.yieldNow;
yield* TestClock.adjust(Duration.millis(100));

const payload = yield* Fiber.join(fiber);
assertNone(payload);
}).pipe(Effect.provide(TestClock.layer())),
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard these fd tests on Windows.

These cases use /dev/null, mkfifo, and sh, so they will fail on win32 even though Lines 20-22 already acknowledge that fd-path handling is platform-specific there. Please skip or branch these tests for non-POSIX platforms.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/bootstrap.test.ts` around lines 50 - 95, The two fd-related
tests in bootstrap.test.ts that call NFS.openSync("/dev/null"),
execFileSync("mkfifo"), spawn("sh", ...) and use
readBootstrapEnvelope/TestEnvelopeSchema should be skipped on Windows; add a
platform guard at the start of each test body (check process.platform ===
"win32") and return an immediate Effect (e.g. Effect.unit) when on Windows so
the test is effectively no-op there, leaving the rest of the POSIX logic
(mkfifo, spawn, TestClock.adjust, Fiber.join) unchanged.

Comment on lines +21 to +22
const fdReady = yield* isFdReady(fd);
if (!fdReady) return Option.none();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fail closed when --bootstrap-fd is configured but unreadable.

Lines 21-22, 73-75, and 82 all collapse “bootstrap FD missing/closed/timed out” into Option.none(). In the new desktop flow, that makes “an explicit bootstrap request failed” indistinguishable from “no bootstrap requested”, so the child can silently fall back to web-mode defaults on a different port with no auth token instead of aborting startup.

Also applies to: 73-82

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/bootstrap.ts` around lines 21 - 22, When a bootstrap FD was
requested but unreadable the code currently returns Option.none(), making an
explicit bootstrap failure indistinguishable from “no bootstrap requested”;
change the behavior in the isFdReady(fd) check (the fdReady/Option.none()
branch) so that if a bootstrap FD was explicitly provided (detect via the
bootstrap FD flag / presence of fd) and isFdReady(fd) returns false you fail
closed (throw an Error or exit with non‑zero) and log the failure, instead of
returning Option.none(); apply the same change to the other branches that
collapse failures into Option.none() (the blocks referencing Option.none()
around the bootstrap FD handling).

Comment on lines +19 to +60
function makeFakeClaudeBinary(dir: string) {
return Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const binDir = path.join(dir, "bin");
const claudePath = path.join(binDir, "claude");
yield* fs.makeDirectory(binDir, { recursive: true });

yield* fs.writeFileString(
claudePath,
[
"#!/bin/sh",
'args="$*"',
'stdin_content="$(cat)"',
'if [ -n "$T3_FAKE_CLAUDE_ARGS_MUST_CONTAIN" ]; then',
' printf "%s" "$args" | grep -F -- "$T3_FAKE_CLAUDE_ARGS_MUST_CONTAIN" >/dev/null || {',
' printf "%s\\n" "args missing expected content" >&2',
" exit 2",
" }",
"fi",
'if [ -n "$T3_FAKE_CLAUDE_ARGS_MUST_NOT_CONTAIN" ]; then',
' if printf "%s" "$args" | grep -F -- "$T3_FAKE_CLAUDE_ARGS_MUST_NOT_CONTAIN" >/dev/null; then',
' printf "%s\\n" "args contained forbidden content" >&2',
" exit 3",
" fi",
"fi",
'if [ -n "$T3_FAKE_CLAUDE_STDIN_MUST_CONTAIN" ]; then',
' printf "%s" "$stdin_content" | grep -F -- "$T3_FAKE_CLAUDE_STDIN_MUST_CONTAIN" >/dev/null || {',
' printf "%s\\n" "stdin missing expected content" >&2',
" exit 4",
" }",
"fi",
'if [ -n "$T3_FAKE_CLAUDE_STDERR" ]; then',
' printf "%s\\n" "$T3_FAKE_CLAUDE_STDERR" >&2',
"fi",
'printf "%s" "$T3_FAKE_CLAUDE_OUTPUT"',
'exit "${T3_FAKE_CLAUDE_EXIT_CODE:-0}"',
"",
].join("\n"),
);
yield* fs.chmod(claudePath, 0o755);
return binDir;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make the fake Claude shim portable.

Lines 30-55 generate a POSIX shell script, Line 59 relies on chmod, and Line 89 hardcodes : as the PATH delimiter. That works on Unix, but it will not execute on Windows. Please switch this to a Node-based shim (or add a .cmd wrapper) and use the platform path delimiter.

Also applies to: 88-90

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/git/Layers/ClaudeTextGeneration.test.ts` around lines 19 -
60, The generated fake Claude shim in makeFakeClaudeBinary is currently a POSIX
shell script requiring chmod and using ':' as PATH delimiter which breaks on
Windows; replace it with a cross-platform Node-based shim (or create both a Unix
executable and a Windows .cmd wrapper) that reads env vars, stdin and writes
stdout/stderr/exit code accordingly, write the shim using the existing
FileSystem/FileSystem and Path/Path handles, only call fs.chmod for non-Windows
platforms, and compute the PATH delimiter via path.delimiter (or Node's
Path.path.delimiter) instead of hardcoding ':' so the tests run on Windows and
Unix.

Comment on lines +89 to +103
const runClaudeCommand = Effect.gen(function* () {
const command = ChildProcess.make(
"claude",
[
"-p",
"--output-format",
"json",
"--json-schema",
jsonSchemaStr,
"--model",
modelSelection.model,
...(normalizedOptions?.effort ? ["--effort", normalizedOptions.effort] : []),
...(Object.keys(settings).length > 0 ? ["--settings", JSON.stringify(settings)] : []),
"--dangerously-skip-permissions",
],
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Official Claude CLI documentation for: claude -p, --output-format json, --json-schema, --settings, --effort, and --dangerously-skip-permissions. Does --json-schema accept inline JSON text, and what least-privilege non-interactive mode is recommended for read-only automation?

💡 Result:

Official Claude Code CLI (from Anthropic) documentation for the specified flags, based on code.claude.com/docs and docs.anthropic.com/en/docs/claude-code: - claude -p (or --print): Runs a single non-interactive query via the Agent SDK and exits (print/headless mode). Ideal for scripting, CI/CD, and automation. Example: claude -p "explain this function". All other CLI flags work with -p. Recommended with --bare for faster startup in scripts by skipping auto-discovery. - --output-format json: Specifies JSON output format for print mode (options: text (default), json, stream-json). Returns structured JSON with result, session ID, and metadata. Example: claude -p "query" --output-format json. - --json-schema: Validates JSON output against a provided JSON Schema in print mode (requires --output-format json). Accepts inline JSON schema text as a CLI argument. Example: claude -p --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' "Extract function names". The agent completes its workflow, then returns validated JSON matching the schema. - --settings: Path to a settings JSON file to override configuration for the session. Example: claude --settings ./my-settings.json. - --effort: Sets session effort level (low, medium, high, max for Opus 4.6 only). Session-scoped, does not persist. Example: claude --effort high. - --dangerously-skip-permissions: Skips all permission prompts, allowing unrestricted tool use (file edits, Bash commands, etc.). Use with extreme caution; intended for sandboxed environments like Docker/CI. Does not bypass all checks (e.g., protected directories). Related: --allow-dangerously-skip-permissions enables it as an option for composing with --permission-mode. For read-only automation (least-privilege non-interactive mode): Use claude -p (or -p --bare for speed) combined with --allowedTools "Read" (or "Read,Grep") to auto-approve only read tools, and --disallowedTools "Edit,Bash,Write" (etc.) to block mutations. Limits Claude to analysis without file changes or command execution. Add --output-format json for parseable output. Example: claude -p --allowedTools "Read" --disallowedTools "Edit,Bash" --output-format json "Analyze the codebase for security issues". Use --max-turns N and --max-budget-usd X for cost/safety limits. --bare recommended for reproducible scripted runs.

Citations:


Fix two CLI contract violations: --settings format and overly broad permissions.

Line 96: --settings expects a file path, not inline JSON. The current code passes JSON.stringify(settings) which will fail at runtime. Either write settings to a temp file first or remove this flag if not actually needed.

Line 99: --dangerously-skip-permissions grants unrestricted tool access (file edits, Bash commands, etc.) and is not appropriate for read-only git text generation. Per the official Claude CLI docs, use --allowedTools "Read" and --disallowedTools "Edit,Bash,Write" instead to enforce least-privilege access.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/git/Layers/ClaudeTextGeneration.ts` around lines 89 - 103,
The runClaudeCommand call constructs the Claude CLI args incorrectly:
ChildProcess.make currently passes JSON.stringify(settings) to the --settings
flag (which expects a file path) and uses --dangerously-skip-permissions
(too-broad). Fix by persisting settings to a temp file and passing that filepath
to --settings (or drop the flag if settings is empty/not needed) and replace
--dangerously-skip-permissions with explicit tool restrictions e.g. add
--allowedTools "Read" and --disallowedTools "Edit,Bash,Write" when building the
args in runClaudeCommand/ChildProcess.make so the CLI receives a settings
filepath and least-privilege permissions.

- RoutingTextGeneration: explicitly document fallback behavior for
  providers without dedicated CLI text-gen layers, add supported
  provider set for clarity
- preload.ts: normalize empty backendWsUrl string to null during
  pre-bootstrap window
- toast.tsx: add aria-label to icon-only copy button for accessibility
- REMOTE.md: hyphenate "command-line arguments"
- dev-runner.ts: strip T3CODE_BOOTSTRAP_FD from forwarded env to
  prevent child processes reading stale/closed descriptors
- Run oxfmt on ProviderModelPicker.tsx and ChatView.tsx
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
scripts/dev-runner.ts (1)

156-175: ⚠️ Potential issue | 🟠 Major

Strip T3CODE_BOOTSTRAP_FD before branching on mode.

output is cloned from baseEnv at Line 156, so dev, dev:web, and dev:server still inherit any ambient T3CODE_BOOTSTRAP_FD. That leaves turbo descendants able to read a stale or unrelated descriptor; this cleanup needs to be unconditional, not desktop-only.

Suggested fix
     const output: NodeJS.ProcessEnv = {
       ...baseEnv,
       PORT: String(webPort),
       ELECTRON_RENDERER_PORT: String(webPort),
       VITE_DEV_SERVER_URL: devUrl?.toString() ?? `http://localhost:${webPort}`,
       T3CODE_HOME: resolvedBaseDir,
     };
+    delete output.T3CODE_BOOTSTRAP_FD;
 
     if (!isDesktopMode) {
       output.T3CODE_PORT = String(serverPort);
       output.VITE_WS_URL = `ws://localhost:${serverPort}`;
     } else {
       delete output.T3CODE_PORT;
       delete output.VITE_WS_URL;
       delete output.T3CODE_AUTH_TOKEN;
       delete output.T3CODE_MODE;
       delete output.T3CODE_NO_BROWSER;
       delete output.T3CODE_HOST;
-      delete output.T3CODE_BOOTSTRAP_FD;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/dev-runner.ts` around lines 156 - 175, The cloned environment object
output still inherits T3CODE_BOOTSTRAP_FD from baseEnv, so remove any
T3CODE_BOOTSTRAP_FD entry unconditionally immediately after creating output
(before the isDesktopMode branch) to prevent turbo descendants reading a stale
descriptor; update the code near the output creation (where output is built from
baseEnv and webPort) to delete output.T3CODE_BOOTSTRAP_FD right after that
assignment, and keep the existing deletions inside the isDesktopMode branch for
other desktop-only keys.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@scripts/dev-runner.ts`:
- Around line 156-175: The cloned environment object output still inherits
T3CODE_BOOTSTRAP_FD from baseEnv, so remove any T3CODE_BOOTSTRAP_FD entry
unconditionally immediately after creating output (before the isDesktopMode
branch) to prevent turbo descendants reading a stale descriptor; update the code
near the output creation (where output is built from baseEnv and webPort) to
delete output.T3CODE_BOOTSTRAP_FD right after that assignment, and keep the
existing deletions inside the isDesktopMode branch for other desktop-only keys.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 44a04ff7-ac08-46a7-9b6d-9b529de99db8

📥 Commits

Reviewing files that changed from the base of the PR and between 8caad87 and 9597c26.

📒 Files selected for processing (7)
  • REMOTE.md
  • apps/desktop/src/preload.ts
  • apps/server/src/git/Layers/RoutingTextGeneration.ts
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/chat/ProviderModelPicker.tsx
  • apps/web/src/components/ui/toast.tsx
  • scripts/dev-runner.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/desktop/src/preload.ts
  • apps/web/src/components/ui/toast.tsx
  • apps/server/src/git/Layers/RoutingTextGeneration.ts
  • REMOTE.md
  • apps/web/src/components/ChatView.tsx

Move the delete above the mode branch so it applies to all dev modes,
not just desktop. Prevents turbo children from inheriting a stale fd.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
scripts/dev-runner.ts (1)

208-220: Consider consolidating T3CODE_DESKTOP_WS_URL deletion.

T3CODE_DESKTOP_WS_URL is deleted across all four modes (dev, dev:server, dev:web, dev:desktop) in three separate conditionals. Similar to the T3CODE_BOOTSTRAP_FD fix, this could be consolidated into a single unconditional deletion for clarity.

♻️ Suggested consolidation
     // Always strip bootstrap fd — it refers to the parent's descriptor and
     // must never leak into turbo/child processes regardless of mode.
     delete output.T3CODE_BOOTSTRAP_FD;
+    // Desktop WS URL is always cleared — desktop computes it dynamically.
+    delete output.T3CODE_DESKTOP_WS_URL;

     if (!isDesktopMode) {

Then remove the individual delete output.T3CODE_DESKTOP_WS_URL; statements from lines 210, 215, and 219.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/dev-runner.ts` around lines 208 - 220, The deletion of
output.T3CODE_DESKTOP_WS_URL is repeated in multiple conditionals; consolidate
by adding a single unconditional delete output.T3CODE_DESKTOP_WS_URL after the
mode/isDesktopMode logic and remove the repeated delete statements inside the
blocks that set output.T3CODE_MODE (the occurrences referencing
output.T3CODE_DESKTOP_WS_URL in the branches checking mode === "dev", mode ===
"dev:server" || mode === "dev:web", and isDesktopMode). Ensure the existing
logic that sets output.T3CODE_MODE (e.g., the blocks using mode and
isDesktopMode) remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/dev-runner.ts`:
- Around line 208-220: The deletion of output.T3CODE_DESKTOP_WS_URL is repeated
in multiple conditionals; consolidate by adding a single unconditional delete
output.T3CODE_DESKTOP_WS_URL after the mode/isDesktopMode logic and remove the
repeated delete statements inside the blocks that set output.T3CODE_MODE (the
occurrences referencing output.T3CODE_DESKTOP_WS_URL in the branches checking
mode === "dev", mode === "dev:server" || mode === "dev:web", and isDesktopMode).
Ensure the existing logic that sets output.T3CODE_MODE (e.g., the blocks using
mode and isDesktopMode) remains unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c070a4d7-199f-47ad-be88-e5102acc580c

📥 Commits

Reviewing files that changed from the base of the PR and between 9597c26 and 6b03780.

📒 Files selected for processing (1)
  • scripts/dev-runner.ts

@aaditagrawal aaditagrawal merged commit f4a4d6e into main Mar 26, 2026
8 checks passed
aaditagrawal added a commit that referenced this pull request Mar 26, 2026
Establishes git ancestry with upstream commits b3bca04..37cf0c1
(8 commits) that were cherry-picked and conflict-resolved in PR #35.
No file changes — this merge only records the relationship so future
upstream syncs start from the correct base.
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.

7 participants