| author | |
| committer | |
| log | 9dcea68bce25e1c30be7a6439bfe607b236740a0 |
| tree | 1538eda948b6476fcd5952b6e8022e4588e4990c |
| parent | 5f7ec73a22b28e2775987b03a744f75e5e151152 |
| signature | Signed by PGP key B5690EEEBB952194 |
4 files changed, 70 insertions(+), 1 deletions(-)
apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts+34| ... | ... | @@ -27,6 +27,10 @@ import { |
| 27 | 27 | type ProviderServiceShape, |
| 28 | 28 | } from "../../provider/Services/ProviderService.ts"; |
| 29 | 29 | import { GitCore, type GitCoreShape } from "../../git/Services/GitCore.ts"; |
| 30 | import { | |
| 31 | GitStatusBroadcaster, | |
| 32 | type GitStatusBroadcasterShape, | |
| 33 | } from "../../git/Services/GitStatusBroadcaster.ts"; | |
| 30 | 34 | import { TextGeneration, type TextGenerationShape } from "../../git/Services/TextGeneration.ts"; |
| 31 | 35 | import { RepositoryIdentityResolverLive } from "../../project/Layers/RepositoryIdentityResolver.ts"; |
| 32 | 36 | import { OrchestrationEngineLive } from "./OrchestrationEngine.ts"; |
| ... | ... | @@ -177,6 +181,24 @@ describe("ProviderCommandReactor", () => { |
| 177 | 181 | : "renamed-branch", |
| 178 | 182 | }), |
| 179 | 183 | ); |
| 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 | ); | |
| 180 | 202 | const generateBranchName = vi.fn<TextGenerationShape["generateBranchName"]>((_) => |
| 181 | 203 | Effect.fail( |
| 182 | 204 | new TextGenerationError({ |
| ... | ... | @@ -225,6 +247,15 @@ describe("ProviderCommandReactor", () => { |
| 225 | 247 | Layer.provideMerge(orchestrationLayer), |
| 226 | 248 | Layer.provideMerge(Layer.succeed(ProviderService, service)), |
| 227 | 249 | 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 | ), | |
| 228 | 259 | Layer.provideMerge( |
| 229 | 260 | Layer.mock(TextGeneration, { |
| 230 | 261 | generateBranchName, |
| ... | ... | @@ -279,6 +310,7 @@ describe("ProviderCommandReactor", () => { |
| 279 | 310 | respondToUserInput, |
| 280 | 311 | stopSession, |
| 281 | 312 | renameBranch, |
| 313 | refreshStatus, | |
| 282 | 314 | generateBranchName, |
| 283 | 315 | generateThreadTitle, |
| 284 | 316 | stateDir, |
| ... | ... | @@ -513,9 +545,11 @@ describe("ProviderCommandReactor", () => { |
| 513 | 545 | ); |
| 514 | 546 | |
| 515 | 547 | await waitFor(() => harness.generateBranchName.mock.calls.length === 1); |
| 548 | await waitFor(() => harness.refreshStatus.mock.calls.length === 1); | |
| 516 | 549 | expect(harness.generateBranchName.mock.calls[0]?.[0]).toMatchObject({ |
| 517 | 550 | message: "Add a safer reconnect backoff.", |
| 518 | 551 | }); |
| 552 | expect(harness.refreshStatus.mock.calls[0]?.[0]).toBe("/tmp/provider-project-worktree"); | |
| 519 | 553 | }); |
| 520 | 554 | |
| 521 | 555 | 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"; |
| 17 | 17 | |
| 18 | 18 | import { resolveThreadWorkspaceCwd } from "../../checkpointing/Utils.ts"; |
| 19 | 19 | import { GitCore } from "../../git/Services/GitCore.ts"; |
| 20 | import { GitStatusBroadcaster } from "../../git/Services/GitStatusBroadcaster.ts"; | |
| 20 | 21 | import { increment, orchestrationEventsProcessedTotal } from "../../observability/Metrics.ts"; |
| 21 | 22 | import { ProviderAdapterRequestError, ProviderServiceError } from "../../provider/Errors.ts"; |
| 22 | 23 | import { TextGeneration } from "../../git/Services/TextGeneration.ts"; |
| ... | ... | @@ -145,6 +146,7 @@ const make = Effect.gen(function* () { |
| 145 | 146 | const orchestrationEngine = yield* OrchestrationEngineService; |
| 146 | 147 | const providerService = yield* ProviderService; |
| 147 | 148 | const git = yield* GitCore; |
| 149 | const gitStatusBroadcaster = yield* GitStatusBroadcaster; | |
| 148 | 150 | const textGeneration = yield* TextGeneration; |
| 149 | 151 | const serverSettingsService = yield* ServerSettingsService; |
| 150 | 152 | const handledTurnStartKeys = yield* Cache.make<string, true>({ |
| ... | ... | @@ -447,6 +449,7 @@ const make = Effect.gen(function* () { |
| 447 | 449 | branch: renamed.branch, |
| 448 | 450 | worktreePath: cwd, |
| 449 | 451 | }); |
| 452 | yield* gitStatusBroadcaster.refreshStatus(cwd).pipe(Effect.ignoreCause({ log: true })); | |
| 450 | 453 | }).pipe( |
| 451 | 454 | Effect.catchCause((cause) => |
| 452 | 455 | 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 { |
| 58 | 58 | import { GitCore, type GitCoreShape } from "./git/Services/GitCore.ts"; |
| 59 | 59 | import { GitManager, type GitManagerShape } from "./git/Services/GitManager.ts"; |
| 60 | 60 | import { GitStatusBroadcasterLive } from "./git/Layers/GitStatusBroadcaster.ts"; |
| 61 | import { | |
| 62 | GitStatusBroadcaster, | |
| 63 | type GitStatusBroadcasterShape, | |
| 64 | } from "./git/Services/GitStatusBroadcaster.ts"; | |
| 61 | 65 | import { Keybindings, type KeybindingsShape } from "./keybindings.ts"; |
| 62 | 66 | import { Open, type OpenShape } from "./open.ts"; |
| 63 | 67 | import { |
| ... | ... | @@ -293,6 +297,7 @@ const buildAppUnderTest = (options?: { |
| 293 | 297 | open?: Partial<OpenShape>; |
| 294 | 298 | gitCore?: Partial<GitCoreShape>; |
| 295 | 299 | gitManager?: Partial<GitManagerShape>; |
| 300 | gitStatusBroadcaster?: Partial<GitStatusBroadcasterShape>; | |
| 296 | 301 | projectSetupScriptRunner?: Partial<ProjectSetupScriptRunnerShape>; |
| 297 | 302 | terminalManager?: Partial<TerminalManagerShape>; |
| 298 | 303 | orchestrationEngine?: Partial<OrchestrationEngineShape>; |
| ... | ... | @@ -341,7 +346,11 @@ const buildAppUnderTest = (options?: { |
| 341 | 346 | const gitManagerLayer = Layer.mock(GitManager)({ |
| 342 | 347 | ...options?.layers?.gitManager, |
| 343 | 348 | }); |
| 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)); | |
| 345 | 354 | |
| 346 | 355 | const servedRoutesLayer = HttpRouter.serve(makeRoutesLayer, { |
| 347 | 356 | disableListenLog: true, |
| ... | ... | @@ -2913,6 +2922,24 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 2913 | 2922 | () => |
| 2914 | 2923 | Effect.gen(function* () { |
| 2915 | 2924 | 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 | ); | |
| 2916 | 2943 | const createWorktree = vi.fn((_: Parameters<GitCoreShape["createWorktree"]>[0]) => |
| 2917 | 2944 | Effect.succeed({ |
| 2918 | 2945 | worktree: { |
| ... | ... | @@ -2937,6 +2964,9 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 2937 | 2964 | gitCore: { |
| 2938 | 2965 | createWorktree, |
| 2939 | 2966 | }, |
| 2967 | gitStatusBroadcaster: { | |
| 2968 | refreshStatus, | |
| 2969 | }, | |
| 2940 | 2970 | orchestrationEngine: { |
| 2941 | 2971 | dispatch: (command) => |
| 2942 | 2972 | Effect.sync(() => { |
| ... | ... | @@ -3014,6 +3044,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 3014 | 3044 | projectCwd: "/tmp/project", |
| 3015 | 3045 | worktreePath: "/tmp/bootstrap-worktree", |
| 3016 | 3046 | }); |
| 3047 | assert.deepEqual(refreshStatus.mock.calls[0]?.[0], "/tmp/bootstrap-worktree"); | |
| 3017 | 3048 | |
| 3018 | 3049 | const setupActivities = dispatchedCommands.filter( |
| 3019 | 3050 | (command): command is Extract<OrchestrationCommand, { type: "thread.activity.append" }> => |
apps/server/src/ws.ts+1| ... | ... | @@ -464,6 +464,7 @@ const makeWsRpcLayer = (currentSessionId: AuthSessionId) => |
| 464 | 464 | branch: worktree.worktree.branch, |
| 465 | 465 | worktreePath: targetWorktreePath, |
| 466 | 466 | }); |
| 467 | yield* refreshGitStatus(targetWorktreePath); | |
| 467 | 468 | } |
| 468 | 469 | |
| 469 | 470 | yield* runSetupProgram(); |