authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-04-13 14:58:03-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-04-13 14:58:03-07:00
log9dcea68bce25e1c30be7a6439bfe607b236740a0
tree1538eda948b6476fcd5952b6e8022e4588e4990c
parent5f7ec73a22b28e2775987b03a744f75e5e151152
signature Signed by PGP key B5690EEEBB952194

Refresh git status after branch rename and worktree setup (#2005)


4 files changed, 70 insertions(+), 1 deletions(-)

apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts+34
......@@ -27,6 +27,10 @@ import {
2727 type ProviderServiceShape,
2828} from "../../provider/Services/ProviderService.ts";
2929import { GitCore, type GitCoreShape } from "../../git/Services/GitCore.ts";
30import {
31 GitStatusBroadcaster,
32 type GitStatusBroadcasterShape,
33} from "../../git/Services/GitStatusBroadcaster.ts";
3034import { TextGeneration, type TextGenerationShape } from "../../git/Services/TextGeneration.ts";
3135import { RepositoryIdentityResolverLive } from "../../project/Layers/RepositoryIdentityResolver.ts";
3236import { OrchestrationEngineLive } from "./OrchestrationEngine.ts";
......@@ -177,6 +181,24 @@ describe("ProviderCommandReactor", () => {
177181 : "renamed-branch",
178182 }),
179183 );
184 const refreshStatus = vi.fn((_: string) =>
185 Effect.succeed({
186 isRepo: true,
187 hasOriginRemote: true,
188 isDefaultBranch: false,
189 branch: "renamed-branch",
190 hasWorkingTreeChanges: false,
191 workingTree: {
192 files: [],
193 insertions: 0,
194 deletions: 0,
195 },
196 hasUpstream: true,
197 aheadCount: 0,
198 behindCount: 0,
199 pr: null,
200 }),
201 );
180202 const generateBranchName = vi.fn<TextGenerationShape["generateBranchName"]>((_) =>
181203 Effect.fail(
182204 new TextGenerationError({
......@@ -225,6 +247,15 @@ describe("ProviderCommandReactor", () => {
225247 Layer.provideMerge(orchestrationLayer),
226248 Layer.provideMerge(Layer.succeed(ProviderService, service)),
227249 Layer.provideMerge(Layer.succeed(GitCore, { renameBranch } as unknown as GitCoreShape)),
250 Layer.provideMerge(
251 Layer.succeed(GitStatusBroadcaster, {
252 getStatus: () => Effect.die("getStatus should not be called in this test"),
253 refreshLocalStatus: () =>
254 Effect.die("refreshLocalStatus should not be called in this test"),
255 refreshStatus,
256 streamStatus: () => Stream.die("streamStatus should not be called in this test"),
257 } satisfies GitStatusBroadcasterShape),
258 ),
228259 Layer.provideMerge(
229260 Layer.mock(TextGeneration, {
230261 generateBranchName,
......@@ -279,6 +310,7 @@ describe("ProviderCommandReactor", () => {
279310 respondToUserInput,
280311 stopSession,
281312 renameBranch,
313 refreshStatus,
282314 generateBranchName,
283315 generateThreadTitle,
284316 stateDir,
......@@ -513,9 +545,11 @@ describe("ProviderCommandReactor", () => {
513545 );
514546
515547 await waitFor(() => harness.generateBranchName.mock.calls.length === 1);
548 await waitFor(() => harness.refreshStatus.mock.calls.length === 1);
516549 expect(harness.generateBranchName.mock.calls[0]?.[0]).toMatchObject({
517550 message: "Add a safer reconnect backoff.",
518551 });
552 expect(harness.refreshStatus.mock.calls[0]?.[0]).toBe("/tmp/provider-project-worktree");
519553 });
520554
521555 it("forwards codex model options through session start and turn send", async () => {
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts+3
......@@ -17,6 +17,7 @@ import { makeDrainableWorker } from "@t3tools/shared/DrainableWorker";
1717
1818import { resolveThreadWorkspaceCwd } from "../../checkpointing/Utils.ts";
1919import { GitCore } from "../../git/Services/GitCore.ts";
20import { GitStatusBroadcaster } from "../../git/Services/GitStatusBroadcaster.ts";
2021import { increment, orchestrationEventsProcessedTotal } from "../../observability/Metrics.ts";
2122import { ProviderAdapterRequestError, ProviderServiceError } from "../../provider/Errors.ts";
2223import { TextGeneration } from "../../git/Services/TextGeneration.ts";
......@@ -145,6 +146,7 @@ const make = Effect.gen(function* () {
145146 const orchestrationEngine = yield* OrchestrationEngineService;
146147 const providerService = yield* ProviderService;
147148 const git = yield* GitCore;
149 const gitStatusBroadcaster = yield* GitStatusBroadcaster;
148150 const textGeneration = yield* TextGeneration;
149151 const serverSettingsService = yield* ServerSettingsService;
150152 const handledTurnStartKeys = yield* Cache.make<string, true>({
......@@ -447,6 +449,7 @@ const make = Effect.gen(function* () {
447449 branch: renamed.branch,
448450 worktreePath: cwd,
449451 });
452 yield* gitStatusBroadcaster.refreshStatus(cwd).pipe(Effect.ignoreCause({ log: true }));
450453 }).pipe(
451454 Effect.catchCause((cause) =>
452455 Effect.logWarning("provider command reactor failed to generate or rename worktree branch", {
apps/server/src/server.test.ts+32-1
......@@ -58,6 +58,10 @@ import {
5858import { GitCore, type GitCoreShape } from "./git/Services/GitCore.ts";
5959import { GitManager, type GitManagerShape } from "./git/Services/GitManager.ts";
6060import { GitStatusBroadcasterLive } from "./git/Layers/GitStatusBroadcaster.ts";
61import {
62 GitStatusBroadcaster,
63 type GitStatusBroadcasterShape,
64} from "./git/Services/GitStatusBroadcaster.ts";
6165import { Keybindings, type KeybindingsShape } from "./keybindings.ts";
6266import { Open, type OpenShape } from "./open.ts";
6367import {
......@@ -293,6 +297,7 @@ const buildAppUnderTest = (options?: {
293297 open?: Partial<OpenShape>;
294298 gitCore?: Partial<GitCoreShape>;
295299 gitManager?: Partial<GitManagerShape>;
300 gitStatusBroadcaster?: Partial<GitStatusBroadcasterShape>;
296301 projectSetupScriptRunner?: Partial<ProjectSetupScriptRunnerShape>;
297302 terminalManager?: Partial<TerminalManagerShape>;
298303 orchestrationEngine?: Partial<OrchestrationEngineShape>;
......@@ -341,7 +346,11 @@ const buildAppUnderTest = (options?: {
341346 const gitManagerLayer = Layer.mock(GitManager)({
342347 ...options?.layers?.gitManager,
343348 });
344 const gitStatusBroadcasterLayer = GitStatusBroadcasterLive.pipe(Layer.provide(gitManagerLayer));
349 const gitStatusBroadcasterLayer = options?.layers?.gitStatusBroadcaster
350 ? Layer.mock(GitStatusBroadcaster)({
351 ...options.layers.gitStatusBroadcaster,
352 })
353 : GitStatusBroadcasterLive.pipe(Layer.provide(gitManagerLayer));
345354
346355 const servedRoutesLayer = HttpRouter.serve(makeRoutesLayer, {
347356 disableListenLog: true,
......@@ -2913,6 +2922,24 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
29132922 () =>
29142923 Effect.gen(function* () {
29152924 const dispatchedCommands: Array<OrchestrationCommand> = [];
2925 const refreshStatus = vi.fn((_: string) =>
2926 Effect.succeed({
2927 isRepo: true,
2928 hasOriginRemote: true,
2929 isDefaultBranch: false,
2930 branch: "t3code/bootstrap-branch",
2931 hasWorkingTreeChanges: false,
2932 workingTree: {
2933 files: [],
2934 insertions: 0,
2935 deletions: 0,
2936 },
2937 hasUpstream: true,
2938 aheadCount: 0,
2939 behindCount: 0,
2940 pr: null,
2941 }),
2942 );
29162943 const createWorktree = vi.fn((_: Parameters<GitCoreShape["createWorktree"]>[0]) =>
29172944 Effect.succeed({
29182945 worktree: {
......@@ -2937,6 +2964,9 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
29372964 gitCore: {
29382965 createWorktree,
29392966 },
2967 gitStatusBroadcaster: {
2968 refreshStatus,
2969 },
29402970 orchestrationEngine: {
29412971 dispatch: (command) =>
29422972 Effect.sync(() => {
......@@ -3014,6 +3044,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
30143044 projectCwd: "/tmp/project",
30153045 worktreePath: "/tmp/bootstrap-worktree",
30163046 });
3047 assert.deepEqual(refreshStatus.mock.calls[0]?.[0], "/tmp/bootstrap-worktree");
30173048
30183049 const setupActivities = dispatchedCommands.filter(
30193050 (command): command is Extract<OrchestrationCommand, { type: "thread.activity.append" }> =>
apps/server/src/ws.ts+1
......@@ -464,6 +464,7 @@ const makeWsRpcLayer = (currentSessionId: AuthSessionId) =>
464464 branch: worktree.worktree.branch,
465465 worktreePath: targetWorktreePath,
466466 });
467 yield* refreshGitStatus(targetWorktreePath);
467468 }
468469
469470 yield* runSetupProgram();