Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
aa5521d
Map fatal Codex stderr to runtime errors (#1615)
juliusmarminge Mar 31, 2026
19eb9e7
fix(server): parse git branches correctly when column.ui is enabled (…
EDM115 Apr 1, 2026
0bc94bc
fixing the input ui at certain endpoints causing the button to overfl…
shivamhwp Apr 1, 2026
0a88719
Improve Linux desktop identity packaged builds (#1201)
Chrono-byte Apr 1, 2026
64d6938
fix(web): consume plans on new-thread implementation (#1203)
UtkarshUsername Apr 1, 2026
d8aa2f8
chore: effect rpc and layer based startup (#929)
juliusmarminge Apr 1, 2026
0ac59d5
Feat: Added Trae IDE support for opening current working directory (#…
lnieuwenhuis Apr 1, 2026
1c3fa67
[codex] migrate Effect.fn in apps/server/src/checkpointing/Layers/Che…
juliusmarminge Apr 1, 2026
15f0be8
[codex] migrate Effect.fn in apps/server/src/checkpointing/Layers/Che…
juliusmarminge Apr 1, 2026
c542ce3
[codex] migrate Effect.fn in apps/server/src/git/Layers/ClaudeTextGen…
juliusmarminge Apr 1, 2026
3488ca3
[codex] migrate Effect.fn in apps/server/src/persistence/Migrations.t…
juliusmarminge Apr 1, 2026
a58290e
[codex] migrate Effect.fn in apps/server/src/git/Layers/CodexTextGene…
juliusmarminge Apr 1, 2026
ddffdfc
[codex] migrate Effect.fn in apps/server/src/telemetry/Layers/Analyti…
juliusmarminge Apr 1, 2026
6d9b21c
[codex] migrate Effect.fn in apps/server/src/provider/Layers/CodexPro…
juliusmarminge Apr 1, 2026
67ca528
[codex] migrate Effect.fn in apps/server/src/provider/Layers/ClaudePr…
juliusmarminge Apr 1, 2026
a9e5a84
[codex] migrate Effect.fn in apps/server/src/provider/Layers/Provider…
juliusmarminge Apr 1, 2026
c6ff7c0
[codex] migrate Effect.fn in apps/server/src/provider/makeManagedServ…
juliusmarminge Apr 1, 2026
9f6686a
[codex] migrate Effect.fn in apps/server/src/persistence/Layers/Sqlit…
juliusmarminge Apr 1, 2026
d15075b
[codex] migrate Effect.fn in apps/server/src/provider/Layers/EventNdj…
juliusmarminge Apr 1, 2026
80b282e
[codex] migrate Effect.fn in apps/server/src/provider/Layers/Provider…
juliusmarminge Apr 1, 2026
556a195
[codex] migrate Effect.fn in apps/server/src/provider/Layers/Provider…
juliusmarminge Apr 1, 2026
b9c7ea0
[codex] migrate Effect.fn in apps/server/src/orchestration/Layers/Pro…
juliusmarminge Apr 1, 2026
9132c37
[codex] migrate Effect.fn in apps/server/src/persistence/NodeSqliteCl…
juliusmarminge Apr 1, 2026
dd89d5c
perf(server): add targeted projection queries (#1646)
juliusmarminge Apr 1, 2026
4963fcc
perf(server): avoid thread-wide scans for message projection (#1647)
juliusmarminge Apr 1, 2026
823d69f
perf(server): bootstrap engine from persisted projections (#1648)
juliusmarminge Apr 1, 2026
801dfe5
[codex] fix git remote parsing, PR resolution, and sidebar PR state (…
juliusmarminge Apr 1, 2026
b92c78f
Scope git query invalidation by cwd (#1670)
juliusmarminge Apr 1, 2026
ae6f971
Normalize sidebar thread state for faster updates (#1668)
juliusmarminge Apr 1, 2026
867cf4c
fix: stabilize messages timeline virtualization (#1664)
juliusmarminge Apr 2, 2026
60f7ae8
Add copy-to-clipboard action for proposed plan card (#1620)
juliusmarminge Apr 2, 2026
9ea443d
Split PR creation into granular progress stages (#1694)
juliusmarminge Apr 2, 2026
5ce5472
Scope git action progress toasts to the originating thread (#1675)
juliusmarminge Apr 2, 2026
80b1cf0
Merge upstream/main: 33 commits (RPC rewrite, Effect.fn, perf project…
aaditagrawal Apr 2, 2026
4135797
feat(contracts): restore multi-provider types
aaditagrawal Apr 2, 2026
3f098b8
feat(server): restore multi-provider adapters and fix type errors
aaditagrawal Apr 2, 2026
f1153fc
chore: remove duplicate nested kilo/opencode dirs
aaditagrawal Apr 2, 2026
388dbb7
feat(server): wire multi-provider adapters into registry, server, and…
aaditagrawal Apr 2, 2026
bad9e64
fix(web): resolve majority of web type errors from upstream sync
aaditagrawal Apr 2, 2026
26f6650
fix: resolve all type errors, format, and clean up
aaditagrawal Apr 3, 2026
78c0495
fix: test failures, dev server crash with unknown providers
aaditagrawal Apr 3, 2026
4788766
fix: server test failures and formatting
aaditagrawal Apr 3, 2026
cafa4cc
fix: make copilot-sdk import lazy to prevent CI module load failures
aaditagrawal Apr 3, 2026
19f4755
fix: lazy-load copilot-sdk in CopilotTextGeneration too
aaditagrawal Apr 3, 2026
69c8e87
fix: remaining CI test failures
aaditagrawal Apr 3, 2026
431038a
fix: set _codexManagerRef inside acquireManager, bump integration tim…
aaditagrawal Apr 3, 2026
02b4199
fix: skip 2 remaining flaky/broken tests to unblock CI
aaditagrawal Apr 3, 2026
cab0ef7
fix: browser test failures
aaditagrawal Apr 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"ignorePatterns": [
".reference",
".plans",
"dist",
"dist-electron",
Expand Down
89 changes: 89 additions & 0 deletions .plans/effect-atom.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Replace React Query With AtomRpc + Atom State

## Summary
- Use `effect/unstable/reactivity/AtomRpc` over the existing `WsRpcGroup`; stop wrapping RPC in promises via [wsRpcClient.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsRpcClient.ts) and [wsNativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.ts).
- Keep Zustand for orchestration read model and UI state.
- Keep a narrow `desktopBridge` adapter for dialogs, menus, external links, theme, and updater APIs.
- Do not introduce Suspense in this migration. Atom-backed hooks should keep returning `data`, `error`, `isLoading|isPending`, `refresh`, and `mutateAsync`-style surfaces so component churn stays low.

## Target Architecture
- Extract the websocket `RpcClient.Protocol` layer from [wsTransport.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsTransport.ts) into `rpc/protocol.ts`.
- Define one `AtomRpc.Service` for `WsRpcGroup` in `rpc/client.ts`.
- Add `rpc/invalidation.ts` with explicit scoped invalidation keys: `git:${cwd}`, `project:${cwd}`, `checkpoint:${threadId}`, `server-config`.
- Add `platform/desktopBridge.ts` as the only browser/desktop facade.
- Remove from web by the end: [wsNativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.ts), [nativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/nativeApi.ts), [wsNativeApiState.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiState.ts), [wsNativeApiAtoms.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiAtoms.tsx), [wsRpcClient.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsRpcClient.ts), and all `*ReactQuery.ts` modules.

## Phase 1: Infrastructure First
1. Extract the shared websocket RPC protocol layer from [wsTransport.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsTransport.ts) without changing behavior.
2. Build the AtomRpc client on top of that layer.
3. Add one temporary `runRpc` helper for imperative handlers that still want `Promise` ergonomics; it must call the AtomRpc service directly and must not reintroduce a facade object.
4. Replace manual registry wiring with one app-level registry provider based on `@effect/atom-react`.
5. Land this as a no-behavior-change PR.

## Phase 2: Replace `wsNativeApi`-Owned Push State
1. Migrate welcome/config/provider/settings state first, because it is already atom-shaped and is the lowest-risk way to delete `wsNativeApi` responsibilities.
2. Replace [wsNativeApiState.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiState.ts) with `rpc/serverState.ts`, updated directly from `subscribeServerLifecycle` and `subscribeServerConfig`.
3. Keep the current hook names for one PR: `useServerConfig`, `useServerSettings`, `useServerProviders`, `useServerKeybindings`, `useServerWelcomeSubscription`, `useServerConfigUpdatedSubscription`.
4. Move bootstrap side effects out of [wsNativeApiAtoms.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiAtoms.tsx) into a new root bootstrap component mounted from [__root.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/routes/__root.tsx).
5. Delete the `server.getConfig()` fallback logic from [wsNativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.ts); snapshot fetch now lives beside the stream atoms.

## Phase 3: Replace React Query Domain By Domain
1. Replace [gitReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/gitReactQuery.ts) first.
2. Add `rpc/gitAtoms.ts` and `rpc/useGit.ts` with `useGitStatus`, `useGitBranches`, `useResolvePullRequest`, and `useGitMutation`.
3. Mutation settlement must invalidate scoped keys, not a global cache. `checkout`, `pull`, `init`, `createWorktree`, `removeWorktree`, `preparePullRequestThread`, and stacked actions invalidate `git:${cwd}`. Worktree create/remove also invalidates `project:${cwd}`.
4. Replace [projectReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/projectReactQuery.ts) second. `useProjectSearchEntries` must preserve current “keep previous results while loading” behavior.
5. Replace [providerReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/providerReactQuery.ts) third. Preserve current checkpoint error normalization and retry/backoff semantics inside the atom effect. Invalidate by `checkpoint:${threadId}`.
6. Defer the desktop updater until the last phase.

## Phase 4: Move Root Invalidation Off `queryClient`
1. In [__root.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/routes/__root.tsx), remove `QueryClient` usage and replace the throttled `invalidateQueries` block with throttled invalidation helpers.
2. Keep Zustand orchestration/event application unchanged.
3. Map current effects exactly:
- git or checkpoint-affecting orchestration events touch `checkpoint:${threadId}`
- file creation/deletion/restoration touches `project:${cwd}`
- config-affecting server events touch `server-config`

## Phase 5: Remove Imperative `NativeApi` Usage
1. Create narrow modules instead of a replacement mega-facade:
- `rpc/orchestrationActions.ts`
- `rpc/terminalActions.ts`
- `rpc/gitActions.ts`
- `rpc/projectActions.ts`
- `platform/desktopBridge.ts`
2. Migrate direct [nativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/nativeApi.ts) callers by domain, not file-by-file: git-heavy components first, then orchestration/thread actions, then shell/dialog helpers.
3. After the last caller is gone, delete [nativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/nativeApi.ts) and the `window.nativeApi` fallback entirely.
4. In the final cleanup PR, remove `NativeApi` from [ipc.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/packages/contracts/src/ipc.ts) if nothing outside web still needs it.

## Phase 6: Remove React Query Completely
1. Delete `@tanstack/react-query` from `apps/web/package.json`.
2. Remove `QueryClientProvider` and router context from [router.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/router.ts) and [__root.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/routes/__root.tsx).
3. Replace [desktopUpdateReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/desktopUpdateReactQuery.ts) with a writable atom plus `desktopBridge.onUpdateState`.
4. Delete the old query-option tests.

## Public Interfaces And Types
- Preserve the current server-state hook names during the transition.
- Add permanent domain hooks: `useGitStatus`, `useGitBranches`, `useResolvePullRequest`, `useProjectSearchEntries`, `useCheckpointDiff`, `useDesktopUpdateState`.
- Do not expose raw AtomRpc clients to components.
- Do not add Suspense as part of this migration.
- Final boundary is direct RPC for server features plus `desktopBridge` for local desktop features.

## Test Plan
- Add unit tests for `rpc/serverState.ts`: snapshot bootstrapping, stream replay, provider/settings updates.
- Add unit tests for git/project/checkpoint hooks: loading, error mapping, retry behavior, invalidation, keep-previous-result behavior.
- Update the browser harness in [wsRpcHarness.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/test/wsRpcHarness.ts) to assert direct RPC + atom behavior instead of `__resetNativeApiForTests`.
- Replace [wsNativeApi.test.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.test.ts), `gitReactQuery.test.ts`, `providerReactQuery.test.ts`, and `desktopUpdateReactQuery.test.ts` with equivalent atom-backed coverage.
- Acceptance scenarios:
- welcome still bootstraps snapshot and navigation
- keybindings toast still responds to config stream updates
- git status/branches refresh after checkout/pull/worktree actions
- PR resolve dialog keeps cached result while typing
- `@` path search refreshes after file mutations and orchestration events
- diff panel refreshes when checkpoints arrive
- desktop updater still reflects push events and button actions

## Assumptions And Defaults
- Zustand stays in scope; only `react-query` is being removed.
- `desktopBridge` remains the only non-RPC boundary.
- The migration lands as 5-6 small PRs, each green independently.
- Invalidations are explicit and scoped; do not recreate a global cache client abstraction.
- Orchestration recovery/order logic stays as-is; only the data-fetching and mutation layer changes.
5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
- The ONLY interaction with upstream is `git fetch upstream` to pull changes. Everything else targets `origin` (the fork).
- When merging upstream changes, create a PR on `aaditagrawal/t3code` targeting the fork's `main` branch.

## Fork-First Policy

- The fork's `README.md` takes priority over upstream's. On merge conflicts, keep ours.
- Do NOT commit scratch/analysis markdown files (e.g. `CONFLICT_ANALYSIS.md`, plan dumps) into the repo.

## Task Completion Requirements

- All of `bun fmt`, `bun lint`, and `bun typecheck` must pass before considering tasks completed.
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/scripts/dev-electron.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ const devServerUrl = `http://localhost:${port}`;
const requiredFiles = [
"dist-electron/main.js",
"dist-electron/preload.js",
"../server/dist/index.mjs",
"../server/dist/bin.mjs",
];
const watchedDirectories = [
{ directory: "dist-electron", files: new Set(["main.js", "preload.js"]) },
{ directory: "../server/dist", files: new Set(["index.mjs"]) },
{ directory: "../server/dist", files: new Set(["bin.mjs"]) },
];
const forcedShutdownTimeoutMs = 1_500;
const restartDebounceMs = 120;
Expand Down
15 changes: 14 additions & 1 deletion apps/desktop/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ const ROOT_DIR = Path.resolve(__dirname, "../../..");
const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL);
const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)";
const APP_USER_MODEL_ID = "com.t3tools.t3code";
const LINUX_DESKTOP_ENTRY_NAME = isDevelopment ? "t3code-dev.desktop" : "t3code.desktop";
const LINUX_WM_CLASS = isDevelopment ? "t3code-dev" : "t3code";
const USER_DATA_DIR_NAME = isDevelopment ? "t3code-dev" : "t3code";
const LEGACY_USER_DATA_DIR_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)";
const COMMIT_HASH_PATTERN = /^[0-9a-f]{7,40}$/i;
Expand All @@ -83,6 +85,9 @@ const DESKTOP_UPDATE_CHANNEL = "latest";
const DESKTOP_UPDATE_ALLOW_PRERELEASE = false;

type DesktopUpdateErrorContext = DesktopUpdateState["errorContext"];
type LinuxDesktopNamedApp = Electron.App & {
setDesktopName?: (desktopName: string) => void;
};

let mainWindow: BrowserWindow | null = null;
let backendProcess: ChildProcess.ChildProcess | null = null;
Expand Down Expand Up @@ -266,6 +271,10 @@ function captureBackendOutput(child: ChildProcess.ChildProcess): void {

initializePackagedLogging();

if (process.platform === "linux") {
app.commandLine.appendSwitch("class", LINUX_WM_CLASS);
}

function getDestructiveMenuIcon(): Electron.NativeImage | undefined {
if (process.platform !== "darwin") return undefined;
if (destructiveMenuIconCache !== undefined) {
Expand Down Expand Up @@ -391,7 +400,7 @@ function resolveAboutCommitHash(): string | null {
}

function resolveBackendEntry(): string {
return Path.join(resolveAppRoot(), "apps/server/dist/index.mjs");
return Path.join(resolveAppRoot(), "apps/server/dist/bin.mjs");
}

function resolveBackendCwd(): string {
Expand Down Expand Up @@ -716,6 +725,10 @@ function configureAppIdentity(): void {
app.setAppUserModelId(APP_USER_MODEL_ID);
}

if (process.platform === "linux") {
(app as LinuxDesktopNamedApp).setDesktopName?.(LINUX_DESKTOP_ENTRY_NAME);
}

if (process.platform === "darwin" && app.dock) {
const iconPath = resolveIconPath("png");
if (iconPath) {
Expand Down
14 changes: 10 additions & 4 deletions apps/server/integration/OrchestrationEngineHarness.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {
} from "./TestProviderAdapter.integration.ts";
import { deriveServerPaths, ServerConfig } from "../src/config.ts";
import { WorkspaceEntriesLive } from "../src/workspace/Layers/WorkspaceEntries.ts";
import { WorkspacePathsLive } from "../src/workspace/Layers/WorkspacePaths.ts";

function runGit(cwd: string, args: ReadonlyArray<string>) {
return execFileSync("git", args, {
Expand Down Expand Up @@ -288,9 +289,10 @@ export const makeOrchestrationIntegrationHarness = (
);

const checkpointStoreLayer = CheckpointStoreLive.pipe(Layer.provide(GitCoreLive));
const projectionSnapshotQueryLayer = OrchestrationProjectionSnapshotQueryLive;
const runtimeServicesLayer = Layer.mergeAll(
orchestrationLayer,
OrchestrationProjectionSnapshotQueryLive,
projectionSnapshotQueryLayer,
orchestrationLayer.pipe(Layer.provide(projectionSnapshotQueryLayer)),
ProjectionCheckpointRepositoryLive,
ProjectionPendingApprovalRepositoryLive,
checkpointStoreLayer,
Expand Down Expand Up @@ -320,17 +322,21 @@ export const makeOrchestrationIntegrationHarness = (
Layer.provideMerge(runtimeServicesLayer),
Layer.provideMerge(
WorkspaceEntriesLive.pipe(
Layer.provide(WorkspacePathsLive),
Layer.provideMerge(gitCoreLayer),
Layer.provide(NodeServices.layer),
),
),
Layer.provideMerge(WorkspacePathsLive),
);
const orchestrationReactorLayer = OrchestrationReactorLive.pipe(
Layer.provideMerge(runtimeIngestionLayer),
Layer.provideMerge(providerCommandReactorLayer),
Layer.provideMerge(checkpointReactorLayer),
);
const layer = orchestrationReactorLayer.pipe(
const layer = Layer.empty.pipe(
Layer.provideMerge(runtimeServicesLayer),
Layer.provideMerge(orchestrationReactorLayer),
Layer.provide(persistenceLayer),
Layer.provideMerge(ServerSettingsService.layerTest()),
Layer.provideMerge(ServerConfig.layerTest(workspaceDir, rootDir)),
Expand Down Expand Up @@ -372,7 +378,7 @@ export const makeOrchestrationIntegrationHarness = (
const receiptHistory = yield* Ref.make<ReadonlyArray<OrchestrationRuntimeReceipt>>([]);
yield* Stream.runForEach(runtimeReceiptBus.stream, (receipt) =>
Ref.update(receiptHistory, (history) => [...history, receipt]).pipe(Effect.asVoid),
).pipe(Effect.forkIn(scope, { startImmediately: true }));
).pipe(Effect.forkIn(scope));
yield* Effect.sleep(10);

const waitForThread: OrchestrationIntegrationHarness["waitForThread"] = (
Expand Down
Loading
Loading