Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
217f044
Persist worktree terminal launch context
juliusmarminge Mar 29, 2026
2535cf1
Persist setup terminals for PR worktree bootstrap
juliusmarminge Mar 29, 2026
6a43f7b
Merge origin/main into t3code/persist-script-terminals
juliusmarminge Mar 30, 2026
033be30
Fix ProjectSetupScriptRunner test after main merge
juliusmarminge Mar 30, 2026
e446571
Merge origin/main into t3code/persist-script-terminals
juliusmarminge Mar 30, 2026
e3372a4
Fix: move preparingWorktree=false after dispatchCommand so indicator …
cursoragent Mar 31, 2026
5bcc23c
Merge origin/main
juliusmarminge Mar 31, 2026
7f5ee34
Merge branch 'main' into t3code/persist-script-terminals
juliusmarminge Apr 1, 2026
d3b8b2e
Persist script terminal worktree paths
juliusmarminge Apr 1, 2026
9d56921
Fix wsServer bootstrap effect chaining
juliusmarminge Apr 1, 2026
9883959
Merge origin/main into t3code/persist-script-terminals
juliusmarminge Apr 1, 2026
06e7c4c
Merge origin/main
juliusmarminge Apr 3, 2026
448eec7
Merge origin/main
juliusmarminge Apr 3, 2026
5d80605
rev
juliusmarminge Apr 3, 2026
b42a75d
kewl
juliusmarminge Apr 3, 2026
dcaf3aa
kewl
juliusmarminge Apr 3, 2026
3cacb37
Persist scripted terminal state from server events
juliusmarminge Apr 3, 2026
e1b607a
Persist terminal event buffering
juliusmarminge Apr 3, 2026
d726c1f
Centralize project script helpers in shared package
juliusmarminge Apr 3, 2026
a5a9521
Separate setup script launch and activity recording failures
juliusmarminge Apr 3, 2026
49bf283
Persist worktree metadata for terminal sessions
juliusmarminge Apr 3, 2026
1b1f688
Preserve worktree metadata on terminal reopen
juliusmarminge Apr 3, 2026
2247f18
Include worktreePath in browser terminal RPCs
juliusmarminge Apr 3, 2026
797f437
Honor cleared terminal launch context worktree path
juliusmarminge Apr 3, 2026
58e0ed7
Reset terminal store between ChatView parity tests
juliusmarminge 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,656 changes: 845 additions & 811 deletions apps/server/integration/orchestrationEngine.integration.test.ts

Large diffs are not rendered by default.

57 changes: 30 additions & 27 deletions apps/server/src/checkpointing/Layers/CheckpointStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,43 +80,46 @@ function initRepoWithCommit(
});
}

function buildLargeText(lineCount = 20_000): string {
function buildLargeText(lineCount = 5_000): string {
return Array.from({ length: lineCount }, (_, index) => `line ${String(index).padStart(5, "0")}`)
.join("\n")
.concat("\n");
}

it.layer(TestLayer)("CheckpointStoreLive", (it) => {
describe("diffCheckpoints", () => {
it.effect("returns full oversized checkpoint diffs without truncation", () =>
Effect.gen(function* () {
const tmp = yield* makeTmpDir();
yield* initRepoWithCommit(tmp);
const checkpointStore = yield* CheckpointStore;
const threadId = ThreadId.makeUnsafe("thread-checkpoint-store");
const fromCheckpointRef = checkpointRefForThreadTurn(threadId, 0);
const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);
it.effect(
"returns full oversized checkpoint diffs without truncation",
() =>
Effect.gen(function* () {
const tmp = yield* makeTmpDir();
yield* initRepoWithCommit(tmp);
const checkpointStore = yield* CheckpointStore;
const threadId = ThreadId.makeUnsafe("thread-checkpoint-store");
const fromCheckpointRef = checkpointRefForThreadTurn(threadId, 0);
const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);

yield* checkpointStore.captureCheckpoint({
cwd: tmp,
checkpointRef: fromCheckpointRef,
});
yield* writeTextFile(path.join(tmp, "README.md"), buildLargeText());
yield* checkpointStore.captureCheckpoint({
cwd: tmp,
checkpointRef: toCheckpointRef,
});
yield* checkpointStore.captureCheckpoint({
cwd: tmp,
checkpointRef: fromCheckpointRef,
});
yield* writeTextFile(path.join(tmp, "README.md"), buildLargeText());
yield* checkpointStore.captureCheckpoint({
cwd: tmp,
checkpointRef: toCheckpointRef,
});

const diff = yield* checkpointStore.diffCheckpoints({
cwd: tmp,
fromCheckpointRef,
toCheckpointRef,
});
const diff = yield* checkpointStore.diffCheckpoints({
cwd: tmp,
fromCheckpointRef,
toCheckpointRef,
});

expect(diff).toContain("diff --git");
expect(diff).not.toContain("[truncated]");
expect(diff).toContain("+line 19999");
}),
expect(diff).toContain("diff --git");
expect(diff).not.toContain("[truncated]");
expect(diff).toContain("+line 04999");
}),
60_000,
);
});
});
184 changes: 99 additions & 85 deletions apps/server/src/git/Layers/GitCore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const GitCoreTestLayer = GitCoreLive.pipe(
Layer.provide(NodeServices.layer),
);
const TestLayer = Layer.mergeAll(NodeServices.layer, GitCoreTestLayer);
const SLOW_GIT_CORE_TEST_TIMEOUT_MS = 60_000;
const SLOW_GIT_CORE_WAIT_TIMEOUT_MS = 30_000;

function makeTmpDir(
prefix = "git-test-",
Expand Down Expand Up @@ -311,48 +313,51 @@ it.layer(TestLayer)("git integration", (it) => {
}),
);

it.effect("keeps default branch right after current branch", () =>
Effect.gen(function* () {
const tmp = yield* makeTmpDir();
const remote = yield* makeTmpDir();
yield* initRepoWithCommit(tmp);
const defaultBranch = (yield* (yield* GitCore).listBranches({ cwd: tmp })).branches.find(
(branch) => branch.current,
)!.name;

yield* git(remote, ["init", "--bare"]);
yield* git(tmp, ["remote", "add", "origin", remote]);
yield* git(tmp, ["push", "-u", "origin", defaultBranch]);
yield* git(tmp, ["remote", "set-head", "origin", defaultBranch]);
it.effect(
"keeps default branch right after current branch",
() =>
Effect.gen(function* () {
const tmp = yield* makeTmpDir();
const remote = yield* makeTmpDir();
yield* initRepoWithCommit(tmp);
const defaultBranch = (yield* (yield* GitCore).listBranches({ cwd: tmp })).branches.find(
(branch) => branch.current,
)!.name;

yield* (yield* GitCore).createBranch({ cwd: tmp, branch: "current-branch" });
yield* (yield* GitCore).checkoutBranch({ cwd: tmp, branch: "current-branch" });
yield* commitWithDate(
tmp,
"current.txt",
"current change\n",
"Thu, 1 Jan 2037 00:00:00 +0000",
"current change",
);
yield* git(remote, ["init", "--bare"]);
yield* git(tmp, ["remote", "add", "origin", remote]);
yield* git(tmp, ["push", "-u", "origin", defaultBranch]);
yield* git(tmp, ["remote", "set-head", "origin", defaultBranch]);

yield* (yield* GitCore).createBranch({ cwd: tmp, branch: "current-branch" });
yield* (yield* GitCore).checkoutBranch({ cwd: tmp, branch: "current-branch" });
yield* commitWithDate(
tmp,
"current.txt",
"current change\n",
"Thu, 1 Jan 2037 00:00:00 +0000",
"current change",
);

yield* (yield* GitCore).checkoutBranch({ cwd: tmp, branch: defaultBranch });
yield* (yield* GitCore).createBranch({ cwd: tmp, branch: "newer-branch" });
yield* (yield* GitCore).checkoutBranch({ cwd: tmp, branch: "newer-branch" });
yield* commitWithDate(
tmp,
"newer.txt",
"newer change\n",
"Fri, 1 Jan 2038 00:00:00 +0000",
"newer change",
);
yield* (yield* GitCore).checkoutBranch({ cwd: tmp, branch: defaultBranch });
yield* (yield* GitCore).createBranch({ cwd: tmp, branch: "newer-branch" });
yield* (yield* GitCore).checkoutBranch({ cwd: tmp, branch: "newer-branch" });
yield* commitWithDate(
tmp,
"newer.txt",
"newer change\n",
"Fri, 1 Jan 2038 00:00:00 +0000",
"newer change",
);

yield* (yield* GitCore).checkoutBranch({ cwd: tmp, branch: "current-branch" });
yield* (yield* GitCore).checkoutBranch({ cwd: tmp, branch: "current-branch" });

const result = yield* (yield* GitCore).listBranches({ cwd: tmp });
expect(result.branches[0]!.name).toBe("current-branch");
expect(result.branches[1]!.name).toBe(defaultBranch);
expect(result.branches[2]!.name).toBe("newer-branch");
}),
const result = yield* (yield* GitCore).listBranches({ cwd: tmp });
expect(result.branches[0]!.name).toBe("current-branch");
expect(result.branches[1]!.name).toBe(defaultBranch);
expect(result.branches[2]!.name).toBe("newer-branch");
}),
SLOW_GIT_CORE_TEST_TIMEOUT_MS,
);

it.effect("lists multiple branches after creating them", () =>
Expand Down Expand Up @@ -533,58 +538,67 @@ it.layer(TestLayer)("git integration", (it) => {
}),
);

it.effect("refreshes upstream behind count after checkout when remote branch advanced", () =>
Effect.gen(function* () {
const services = yield* Effect.services();
const runPromise = Effect.runPromiseWith(services);
it.effect(
"refreshes upstream behind count after checkout when remote branch advanced",
() =>
Effect.gen(function* () {
const services = yield* Effect.services();
const runPromise = Effect.runPromiseWith(services);

const remote = yield* makeTmpDir();
const source = yield* makeTmpDir();
const clone = yield* makeTmpDir();
yield* git(remote, ["init", "--bare"]);
const remote = yield* makeTmpDir();
const source = yield* makeTmpDir();
const clone = yield* makeTmpDir();
yield* git(remote, ["init", "--bare"]);

yield* initRepoWithCommit(source);
const defaultBranch = (yield* (yield* GitCore).listBranches({ cwd: source })).branches.find(
(branch) => branch.current,
)!.name;
yield* git(source, ["remote", "add", "origin", remote]);
yield* git(source, ["push", "-u", "origin", defaultBranch]);
yield* initRepoWithCommit(source);
const defaultBranch = (yield* (yield* GitCore).listBranches({
cwd: source,
})).branches.find((branch) => branch.current)!.name;
yield* git(source, ["remote", "add", "origin", remote]);
yield* git(source, ["push", "-u", "origin", defaultBranch]);

const featureBranch = "feature-behind";
yield* (yield* GitCore).createBranch({ cwd: source, branch: featureBranch });
yield* (yield* GitCore).checkoutBranch({ cwd: source, branch: featureBranch });
yield* writeTextFile(path.join(source, "feature.txt"), "feature base\n");
yield* git(source, ["add", "feature.txt"]);
yield* git(source, ["commit", "-m", "feature base"]);
yield* git(source, ["push", "-u", "origin", featureBranch]);
yield* (yield* GitCore).checkoutBranch({ cwd: source, branch: defaultBranch });
const featureBranch = "feature-behind";
yield* (yield* GitCore).createBranch({ cwd: source, branch: featureBranch });
yield* (yield* GitCore).checkoutBranch({ cwd: source, branch: featureBranch });
yield* writeTextFile(path.join(source, "feature.txt"), "feature base\n");
yield* git(source, ["add", "feature.txt"]);
yield* git(source, ["commit", "-m", "feature base"]);
yield* git(source, ["push", "-u", "origin", featureBranch]);
yield* (yield* GitCore).checkoutBranch({ cwd: source, branch: defaultBranch });

yield* git(clone, ["clone", remote, "."]);
yield* git(clone, ["config", "user.email", "test@test.com"]);
yield* git(clone, ["config", "user.name", "Test"]);
yield* git(clone, ["checkout", "-b", featureBranch, "--track", `origin/${featureBranch}`]);
yield* writeTextFile(path.join(clone, "feature.txt"), "feature from remote\n");
yield* git(clone, ["add", "feature.txt"]);
yield* git(clone, ["commit", "-m", "remote feature update"]);
yield* git(clone, ["push", "origin", featureBranch]);
yield* git(clone, ["clone", remote, "."]);
yield* git(clone, ["config", "user.email", "test@test.com"]);
yield* git(clone, ["config", "user.name", "Test"]);
yield* git(clone, [
"checkout",
"-b",
featureBranch,
"--track",
`origin/${featureBranch}`,
]);
yield* writeTextFile(path.join(clone, "feature.txt"), "feature from remote\n");
yield* git(clone, ["add", "feature.txt"]);
yield* git(clone, ["commit", "-m", "remote feature update"]);
yield* git(clone, ["push", "origin", featureBranch]);

yield* (yield* GitCore).checkoutBranch({ cwd: source, branch: featureBranch });
const core = yield* GitCore;
yield* Effect.promise(() =>
vi.waitFor(
async () => {
const details = await runPromise(core.statusDetails(source));
expect(details.branch).toBe(featureBranch);
expect(details.aheadCount).toBe(0);
expect(details.behindCount).toBe(1);
},
{
timeout: 10_000,
interval: 100,
},
),
);
}),
yield* (yield* GitCore).checkoutBranch({ cwd: source, branch: featureBranch });
const core = yield* GitCore;
yield* Effect.promise(() =>
vi.waitFor(
async () => {
const details = await runPromise(core.statusDetails(source));
expect(details.branch).toBe(featureBranch);
expect(details.aheadCount).toBe(0);
expect(details.behindCount).toBe(1);
},
{
timeout: SLOW_GIT_CORE_WAIT_TIMEOUT_MS,
interval: 100,
},
),
);
}),
SLOW_GIT_CORE_TEST_TIMEOUT_MS,
);

it.effect("keeps checkout successful when upstream refresh fails", () =>
Expand Down
Loading
Loading