| ... | @@ -108,27 +108,11 @@ describe("ProviderCommandReactor", () => { | ... | @@ -108,27 +108,11 @@ describe("ProviderCommandReactor", () => { |
| 108 | const interruptTurn = vi.fn((_: unknown) => Effect.void); | 108 | const interruptTurn = vi.fn((_: unknown) => Effect.void); |
| 109 | const respondToRequest = vi.fn((_: unknown) => Effect.void); | 109 | const respondToRequest = vi.fn((_: unknown) => Effect.void); |
| 110 | const stopSession = vi.fn((_: unknown) => Effect.void); | 110 | const stopSession = vi.fn((_: unknown) => Effect.void); |
| 111 | const statusBranchByCwd = new Map<string, string | null>(); | | |
| 112 | const renameBranch = vi.fn((_: unknown) => | 111 | const renameBranch = vi.fn((_: unknown) => |
| 113 | Effect.succeed({ | 112 | Effect.succeed({ |
| 114 | branch: "t3code/generated-name", | 113 | branch: "t3code/generated-name", |
| 115 | }), | 114 | }), |
| 116 | ); | 115 | ); |
| 117 | const statusDetails = vi.fn((cwd: string) => | | |
| 118 | Effect.succeed({ | | |
| 119 | branch: statusBranchByCwd.get(cwd) ?? null, | | |
| 120 | upstreamRef: null, | | |
| 121 | hasWorkingTreeChanges: false, | | |
| 122 | workingTree: { | | |
| 123 | files: [], | | |
| 124 | insertions: 0, | | |
| 125 | deletions: 0, | | |
| 126 | }, | | |
| 127 | hasUpstream: false, | | |
| 128 | aheadCount: 0, | | |
| 129 | behindCount: 0, | | |
| 130 | }), | | |
| 131 | ); | | |
| 132 | const generateBranchName = vi.fn<TextGenerationShape["generateBranchName"]>(() => | 116 | const generateBranchName = vi.fn<TextGenerationShape["generateBranchName"]>(() => |
| 133 | Effect.succeed({ | 117 | Effect.succeed({ |
| 134 | branch: "generated-name", | 118 | branch: "generated-name", |
| ... | @@ -157,12 +141,7 @@ describe("ProviderCommandReactor", () => { | ... | @@ -157,12 +141,7 @@ describe("ProviderCommandReactor", () => { |
| 157 | const layer = ProviderCommandReactorLive.pipe( | 141 | const layer = ProviderCommandReactorLive.pipe( |
| 158 | Layer.provideMerge(orchestrationLayer), | 142 | Layer.provideMerge(orchestrationLayer), |
| 159 | Layer.provideMerge(Layer.succeed(ProviderService, service)), | 143 | Layer.provideMerge(Layer.succeed(ProviderService, service)), |
| 160 | Layer.provideMerge( | 144 | Layer.provideMerge(Layer.succeed(GitCore, { renameBranch } as unknown as GitCoreShape)), |
| 161 | Layer.succeed( | | |
| 162 | GitCore, | | |
| 163 | { renameBranch, statusDetails } as unknown as GitCoreShape, | | |
| 164 | ), | | |
| 165 | ), | | |
| 166 | Layer.provideMerge( | 145 | Layer.provideMerge( |
| 167 | Layer.succeed(TextGeneration, { generateBranchName } as unknown as TextGenerationShape), | 146 | Layer.succeed(TextGeneration, { generateBranchName } as unknown as TextGenerationShape), |
| 168 | ), | 147 | ), |
| ... | @@ -210,11 +189,7 @@ describe("ProviderCommandReactor", () => { | ... | @@ -210,11 +189,7 @@ describe("ProviderCommandReactor", () => { |
| 210 | respondToRequest, | 189 | respondToRequest, |
| 211 | stopSession, | 190 | stopSession, |
| 212 | renameBranch, | 191 | renameBranch, |
| 213 | statusDetails, | | |
| 214 | generateBranchName, | 192 | generateBranchName, |
| 215 | setStatusBranch: (cwd: string, branch: string | null) => { | | |
| 216 | statusBranchByCwd.set(cwd, branch); | | |
| 217 | }, | | |
| 218 | stateDir, | 193 | stateDir, |
| 219 | }; | 194 | }; |
| 220 | } | 195 | } |
| ... | @@ -260,7 +235,6 @@ describe("ProviderCommandReactor", () => { | ... | @@ -260,7 +235,6 @@ describe("ProviderCommandReactor", () => { |
| 260 | it("generates and renames temporary worktree branch on first turn", async () => { | 235 | it("generates and renames temporary worktree branch on first turn", async () => { |
| 261 | const harness = await createHarness(); | 236 | const harness = await createHarness(); |
| 262 | const now = new Date().toISOString(); | 237 | const now = new Date().toISOString(); |
| 263 | const worktreeCwd = "/tmp/provider-project/.t3/worktrees/t3code-89abc123"; | | |
| 264 | | 238 | |
| 265 | await Effect.runPromise( | 239 | await Effect.runPromise( |
| 266 | harness.engine.dispatch({ | 240 | harness.engine.dispatch({ |
| ... | @@ -268,10 +242,9 @@ describe("ProviderCommandReactor", () => { | ... | @@ -268,10 +242,9 @@ describe("ProviderCommandReactor", () => { |
| 268 | commandId: CommandId.makeUnsafe("cmd-thread-meta-set-temp-branch"), | 242 | commandId: CommandId.makeUnsafe("cmd-thread-meta-set-temp-branch"), |
| 269 | threadId: ThreadId.makeUnsafe("thread-1"), | 243 | threadId: ThreadId.makeUnsafe("thread-1"), |
| 270 | branch: "t3code/89abc123", | 244 | branch: "t3code/89abc123", |
| 271 | worktreePath: worktreeCwd, | 245 | worktreePath: "/tmp/provider-project/.t3/worktrees/t3code-89abc123", |
| 272 | }), | 246 | }), |
| 273 | ); | 247 | ); |
| 274 | harness.setStatusBranch(worktreeCwd, "t3code/89abc123"); | | |
| 275 | | 248 | |
| 276 | await Effect.runPromise( | 249 | await Effect.runPromise( |
| 277 | harness.engine.dispatch({ | 250 | harness.engine.dispatch({ |
| ... | @@ -302,7 +275,7 @@ describe("ProviderCommandReactor", () => { | ... | @@ -302,7 +275,7 @@ describe("ProviderCommandReactor", () => { |
| 302 | await waitFor(() => harness.renameBranch.mock.calls.length === 1); | 275 | await waitFor(() => harness.renameBranch.mock.calls.length === 1); |
| 303 | | 276 | |
| 304 | expect(harness.generateBranchName.mock.calls[0]?.[0]).toEqual({ | 277 | expect(harness.generateBranchName.mock.calls[0]?.[0]).toEqual({ |
| 305 | cwd: worktreeCwd, | 278 | cwd: "/tmp/provider-project/.t3/worktrees/t3code-89abc123", |
| 306 | message: "Fix visual bug from screenshot", | 279 | message: "Fix visual bug from screenshot", |
| 307 | attachments: [ | 280 | attachments: [ |
| 308 | { | 281 | { |
| ... | @@ -315,14 +288,18 @@ describe("ProviderCommandReactor", () => { | ... | @@ -315,14 +288,18 @@ describe("ProviderCommandReactor", () => { |
| 315 | ], | 288 | ], |
| 316 | }); | 289 | }); |
| 317 | expect(harness.renameBranch.mock.calls[0]?.[0]).toEqual({ | 290 | expect(harness.renameBranch.mock.calls[0]?.[0]).toEqual({ |
| 318 | cwd: worktreeCwd, | 291 | cwd: "/tmp/provider-project/.t3/worktrees/t3code-89abc123", |
| 319 | oldBranch: "t3code/89abc123", | 292 | oldBranch: "t3code/89abc123", |
| 320 | newBranch: "t3code/generated-name", | 293 | newBranch: "t3code/generated-name", |
| 321 | }); | 294 | }); |
| 322 | | 295 | |
| 323 | const readModel = await Effect.runPromise(harness.engine.getReadModel()); | 296 | await waitFor(() => { |
| 324 | const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1")); | 297 | const readModel = Effect.runSync(harness.engine.getReadModel()); |
| 325 | expect(thread?.branch).toBe("t3code/89abc123"); | 298 | const thread = readModel.threads.find( |
| | 299 | (entry) => entry.id === ThreadId.makeUnsafe("thread-1"), |
| | 300 | ); |
| | 301 | return thread?.branch === "t3code/generated-name"; |
| | 302 | }); |
| 326 | }); | 303 | }); |
| 327 | | 304 | |
| 328 | it("passes persisted attachment references to branch generation and turn start", async () => { | 305 | it("passes persisted attachment references to branch generation and turn start", async () => { |
| ... | @@ -396,7 +373,6 @@ describe("ProviderCommandReactor", () => { | ... | @@ -396,7 +373,6 @@ describe("ProviderCommandReactor", () => { |
| 396 | it("skips worktree branch generation after the first user turn", async () => { | 373 | it("skips worktree branch generation after the first user turn", async () => { |
| 397 | const harness = await createHarness(); | 374 | const harness = await createHarness(); |
| 398 | const now = new Date().toISOString(); | 375 | const now = new Date().toISOString(); |
| 399 | const worktreeCwd = "/tmp/provider-project/.t3/worktrees/t3code-1234abcd"; | | |
| 400 | | 376 | |
| 401 | await Effect.runPromise( | 377 | await Effect.runPromise( |
| 402 | harness.engine.dispatch({ | 378 | harness.engine.dispatch({ |
| ... | @@ -404,10 +380,9 @@ describe("ProviderCommandReactor", () => { | ... | @@ -404,10 +380,9 @@ describe("ProviderCommandReactor", () => { |
| 404 | commandId: CommandId.makeUnsafe("cmd-thread-meta-set-temp-branch-2"), | 380 | commandId: CommandId.makeUnsafe("cmd-thread-meta-set-temp-branch-2"), |
| 405 | threadId: ThreadId.makeUnsafe("thread-1"), | 381 | threadId: ThreadId.makeUnsafe("thread-1"), |
| 406 | branch: "t3code/1234abcd", | 382 | branch: "t3code/1234abcd", |
| 407 | worktreePath: worktreeCwd, | 383 | worktreePath: "/tmp/provider-project/.t3/worktrees/t3code-1234abcd", |
| 408 | }), | 384 | }), |
| 409 | ); | 385 | ); |
| 410 | harness.setStatusBranch(worktreeCwd, "t3code/1234abcd"); | | |
| 411 | | 386 | |
| 412 | await Effect.runPromise( | 387 | await Effect.runPromise( |
| 413 | harness.engine.dispatch({ | 388 | harness.engine.dispatch({ |
| ... | @@ -450,44 +425,6 @@ describe("ProviderCommandReactor", () => { | ... | @@ -450,44 +425,6 @@ describe("ProviderCommandReactor", () => { |
| 450 | expect(harness.renameBranch.mock.calls.length).toBe(1); | 425 | expect(harness.renameBranch.mock.calls.length).toBe(1); |
| 451 | }); | 426 | }); |
| 452 | | 427 | |
| 453 | it("skips worktree rename when active branch already changed", async () => { | | |
| 454 | const harness = await createHarness(); | | |
| 455 | const now = new Date().toISOString(); | | |
| 456 | const worktreeCwd = "/tmp/provider-project/.t3/worktrees/t3code-badf00d0"; | | |
| 457 | | | |
| 458 | await Effect.runPromise( | | |
| 459 | harness.engine.dispatch({ | | |
| 460 | type: "thread.meta.update", | | |
| 461 | commandId: CommandId.makeUnsafe("cmd-thread-meta-set-temp-branch-switched"), | | |
| 462 | threadId: ThreadId.makeUnsafe("thread-1"), | | |
| 463 | branch: "t3code/badf00d0", | | |
| 464 | worktreePath: worktreeCwd, | | |
| 465 | }), | | |
| 466 | ); | | |
| 467 | harness.setStatusBranch(worktreeCwd, "feature/add-vscode-open-in-support"); | | |
| 468 | | | |
| 469 | await Effect.runPromise( | | |
| 470 | harness.engine.dispatch({ | | |
| 471 | type: "thread.turn.start", | | |
| 472 | commandId: CommandId.makeUnsafe("cmd-turn-start-worktree-switched"), | | |
| 473 | threadId: ThreadId.makeUnsafe("thread-1"), | | |
| 474 | message: { | | |
| 475 | messageId: asMessageId("user-message-worktree-switched"), | | |
| 476 | role: "user", | | |
| 477 | text: "Fix visual bug from screenshot", | | |
| 478 | attachments: [], | | |
| 479 | }, | | |
| 480 | approvalPolicy: "on-request", | | |
| 481 | sandboxMode: "workspace-write", | | |
| 482 | createdAt: now, | | |
| 483 | }), | | |
| 484 | ); | | |
| 485 | | | |
| 486 | await waitFor(() => harness.generateBranchName.mock.calls.length === 1); | | |
| 487 | await Effect.runPromise(Effect.sleep("20 millis")); | | |
| 488 | expect(harness.renameBranch.mock.calls.length).toBe(0); | | |
| 489 | }); | | |
| 490 | | | |
| 491 | it("skips worktree rename when branch-name generation fails", async () => { | 428 | it("skips worktree rename when branch-name generation fails", async () => { |
| 492 | const harness = await createHarness(); | 429 | const harness = await createHarness(); |
| 493 | const now = new Date().toISOString(); | 430 | const now = new Date().toISOString(); |