import { assert, it, vi } from "@effect/vitest"; import { CommandId, DEFAULT_SERVER_SETTINGS, GitCommandError, MessageId, ProjectId, ProviderDriverKind, ProviderInstanceId, type ServerProvider, ThreadId, } from "@t3tools/contracts"; import * as Deferred from "effect/Deferred"; import * as DateTime from "effect/DateTime"; import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Ref from "effect/Ref"; import * as Stream from "effect/Stream"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as TestClock from "effect/testing/TestClock"; import * as GitWorkflow from "../git/GitWorkflowService.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import { ProjectionProjectRepository } from "../persistence/Services/ProjectionProjects.ts"; import * as ProjectService from "../project/ProjectService.ts"; import * as ProjectSetupScriptRunner from "../project/ProjectSetupScriptRunner.ts"; import { makeProviderRegistryLayer } from "../provider/testUtils/providerRegistryMock.ts"; import * as ServerSettings from "../serverSettings.ts"; import * as TextGeneration from "../textGeneration/TextGeneration.ts"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; import * as CommandReceiptStore from "./CommandReceiptStore.ts"; import * as EffectOutbox from "./EffectOutbox.ts"; import * as IdAllocator from "./IdAllocator.ts"; import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; import * as ThreadLaunch from "./ThreadLaunchService.ts"; import * as ThreadManagement from "./ThreadManagementService.ts"; import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts"; import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; const projectId = ProjectId.make("project:launch-test"); const modelSelection = { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.1-codex", } as const; const project = { id: projectId, title: "Project", workspaceRoot: "/repo", repositoryIdentity: null, faviconPath: null, defaultModelSelection: modelSelection, defaultThreadEnvMode: null, scripts: [], createdAt: "2026-06-20T00:00:00.000Z", updatedAt: "2026-06-20T00:00:00.000Z", deletedAt: null, } as const; const adapter = { instanceId: modelSelection.instanceId, driver: ProviderDriverKind.make("codex"), getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), openSession: () => Effect.die("provider execution is disabled in launch tests"), } as ProviderAdapterV2Shape; interface HarnessOptions { readonly createWorktree?: GitWorkflow.GitWorkflowService["Service"]["createWorktree"]; readonly fetchRemote?: GitWorkflow.GitWorkflowService["Service"]["fetchRemote"]; readonly renameBranch?: GitWorkflow.GitWorkflowService["Service"]["renameBranch"]; readonly runSetup?: ProjectSetupScriptRunner.ProjectSetupScriptRunner["Service"]["runForThread"]; readonly generateTitle?: TextGeneration.TextGeneration["Service"]["generateThreadTitle"]; readonly generateBranchName?: TextGeneration.TextGeneration["Service"]["generateBranchName"]; readonly serverSettings?: Parameters[0]; readonly providers?: ReadonlyArray; readonly mapCommandReceipts?: ( service: CommandReceiptStore.CommandReceiptStoreV2["Service"], ) => CommandReceiptStore.CommandReceiptStoreV2["Service"]; } function makeHarness(options: HarnessOptions = {}) { const database = SqlitePersistenceMemory; const registry = ProviderAdapterRegistry.makeLayer([adapter]); const orchestrator = makeOrchestratorV2ReplayLayerWithRegistry( { name: "thread-launch" }, registry, { databaseLayer: database, runEffectWorker: false }, ); const threadManagement = ThreadManagement.layer.pipe(Layer.provide(orchestrator)); const baseReceipts = CommandReceiptStore.layer.pipe(Layer.provide(database)); const receipts = options.mapCommandReceipts === undefined ? baseReceipts : Layer.effect( CommandReceiptStore.CommandReceiptStoreV2, Effect.map(CommandReceiptStore.CommandReceiptStoreV2, options.mapCommandReceipts), ).pipe(Layer.provide(baseReceipts)); const outbox = EffectOutbox.layer.pipe(Layer.provide(database)); const createWorktree = vi.fn( options.createWorktree ?? ((input) => Effect.succeed({ worktree: { path: "/repo-worktrees/feature", refName: input.newRefName, headSha: "abc" }, } as never)), ); const renameBranch = vi.fn( options.renameBranch ?? ((input) => Effect.succeed({ branch: input.newBranch })), ); const runSetup = vi.fn( options.runSetup ?? (() => Effect.succeed({ status: "no-script" as const })), ); const generateBranchName = vi.fn( options.generateBranchName ?? (() => Effect.succeed({ branch: "generated-branch" })), ); const generateThreadTitle = vi.fn( options.generateTitle ?? (() => Effect.succeed({ title: "Generated title" })), ); const externalServices = Layer.mergeAll( Layer.succeed(ProjectService.ProjectService, { create: () => Effect.die("unused"), bootstrap: () => Effect.die("unused"), update: () => Effect.die("unused"), delete: () => Effect.die("unused"), getById: (id) => Effect.succeed(id === projectId ? Option.some(project) : Option.none()), getByWorkspaceRoot: () => Effect.succeed(Option.some(project)), snapshot: Effect.die("unused"), }), Layer.mock(GitWorkflow.GitWorkflowService)({ createWorktree, renameBranch, fetchRemote: options.fetchRemote ?? (() => Effect.void), remoteExists: () => Effect.succeed(true), remoteBranchExists: () => Effect.succeed(true), removeWorktree: () => Effect.void, resolveRemoteTrackingCommit: () => Effect.succeed({ commitSha: "remote-main-sha", remoteRefName: "origin/main" }), }), Layer.succeed(ProjectSetupScriptRunner.ProjectSetupScriptRunner, { runForThread: runSetup, }), Layer.mock(TextGeneration.TextGeneration)({ generateThreadTitle, generateBranchName, }), ServerSettings.layerTest(options.serverSettings), makeProviderRegistryLayer(options.providers), ); const launch = ThreadLaunch.layer.pipe( Layer.provide(Layer.mergeAll(externalServices, threadManagement, receipts, IdAllocator.layer)), ); const projectedProjects = Layer.mock(ProjectionProjectRepository)({ getById: ({ projectId: requestedProjectId }) => Effect.succeed( requestedProjectId === projectId ? Option.some({ projectId, title: project.title, workspaceRoot: project.workspaceRoot, defaultModelSelection: project.defaultModelSelection, defaultThreadEnvMode: null, autoPull: false, scripts: project.scripts, createdAt: project.createdAt, updatedAt: project.updatedAt, deletedAt: project.deletedAt, }) : Option.none(), ), }); const titleRegeneration = ThreadTitleRegeneration.layer.pipe( Layer.provide(Layer.mergeAll(threadManagement, projectedProjects, externalServices)), ); return { layer: Layer.mergeAll(launch, threadManagement, titleRegeneration, outbox, database), createWorktree, renameBranch, generateBranchName, generateThreadTitle, runSetup, }; } function launchInput(input: { readonly command: string; readonly thread: string; readonly message?: string; readonly workspace?: ThreadLaunch.ThreadLaunchWorkspaceStrategy; }) { return { commandId: CommandId.make(input.command), threadId: ThreadId.make(input.thread), projectId, title: "New thread", modelSelection, runtimeMode: "full-access" as const, interactionMode: "default" as const, workspaceStrategy: input.workspace ?? { type: "root" as const }, ...(input.message === undefined ? {} : { initialMessage: { messageId: MessageId.make(`${input.message}:id`), text: input.message, attachments: [], }, }), createdBy: "user" as const, creationSource: "web" as const, }; } function waitUntil(predicate: () => Effect.Effect): Effect.Effect { return Effect.gen(function* () { for (let attempt = 0; attempt < 200; attempt += 1) { if (yield* predicate()) return; yield* Effect.promise( () => new Promise((resolve) => { setImmediate(resolve); }), ); } assert.fail("Condition was not reached before timeout."); }); } it.effect("returns a visible preparing message while provisioning is still blocked", () => Effect.gen(function* () { const worktreeEntered = yield* Deferred.make(); const allowWorktree = yield* Deferred.make(); const setupEntered = yield* Deferred.make(); const allowSetup = yield* Deferred.make(); const harness = makeHarness({ createWorktree: () => Deferred.succeed(worktreeEntered, undefined).pipe( Effect.andThen(Deferred.await(allowWorktree)), Effect.as({ worktree: { path: "/repo-worktrees/feature", refName: "feature", headSha: "abc" }, } as never), ), runSetup: () => Deferred.succeed(setupEntered, undefined).pipe( Effect.andThen(Deferred.await(allowSetup)), Effect.as({ status: "no-script" as const }), ), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const outbox = yield* EffectOutbox.EffectOutboxV2; const threads = yield* ThreadManagement.ThreadManagementService; const input = launchInput({ command: "command:launch:blocked", thread: "thread:launch:blocked", message: "Build the feature", workspace: { type: "worktree", baseRef: "main" }, }); const launched = yield* launches.launch(input); assert.equal(launched.projection.messages[0]?.text, "Build the feature"); assert.equal(launched.projection.runs[0]?.status, "preparing"); assert.equal( launched.projection.turnItems.find((item) => item.type === "command_execution")?.status, "running", ); yield* Deferred.await(worktreeEntered); let current = yield* threads.getThreadProjection(launched.threadId); assert.equal( current.turnItems.find((item) => item.type === "command_execution")?.title, "Preparing worktree", ); yield* Deferred.succeed(allowWorktree, undefined); const entered = yield* Deferred.await(setupEntered).pipe( Effect.timeoutOption(Duration.seconds(2)), ); if (Option.isNone(entered)) { current = yield* threads.getThreadProjection(launched.threadId); assert.fail( `Setup was not reached; run=${current.runs[0]?.status ?? "missing"}, worklog=${current.turnItems.find((item) => item.type === "command_execution")?.title ?? "missing"}.`, ); } current = yield* threads.getThreadProjection(launched.threadId); assert.equal( current.turnItems.find((item) => item.type === "command_execution")?.title, "Starting setup script", ); const prematureEffects = yield* outbox.listByCommandId( CommandId.make("command:launch:blocked:initial-message"), ); assert.isEmpty(prematureEffects); yield* Deferred.succeed(allowSetup, undefined); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("provisions independent launches concurrently instead of behind a global semaphore", () => Effect.gen(function* () { const setupCount = yield* Ref.make(0); const bothEntered = yield* Deferred.make(); const allowSetup = yield* Deferred.make(); const harness = makeHarness({ runSetup: () => Ref.updateAndGet(setupCount, (count) => count + 1).pipe( Effect.tap((count) => count === 2 ? Deferred.succeed(bothEntered, undefined) : Effect.void, ), Effect.andThen(Deferred.await(allowSetup)), Effect.as({ status: "no-script" as const }), ), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const results = yield* Effect.all( [ launches.launch( launchInput({ command: "command:launch:concurrent-a", thread: "thread:launch:concurrent-a", message: "First", }), ), launches.launch( launchInput({ command: "command:launch:concurrent-b", thread: "thread:launch:concurrent-b", message: "Second", }), ), ], { concurrency: "unbounded" }, ); assert.deepEqual( results.map((result) => result.projection.runs[0]?.status), ["preparing", "preparing"], ); yield* Deferred.await(bothEntered); assert.equal(yield* Ref.get(setupCount), 2); yield* Deferred.succeed(allowSetup, undefined); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("reports an initial-message replay that wins after the receipt preflight", () => Effect.gen(function* () { const receiptLookupCompleted = yield* Deferred.make(); const allowReceiptLookup = yield* Deferred.make(); const messageCommandId = CommandId.make("command:launch:message-replay:initial-message"); let gated = false; const harness = makeHarness({ mapCommandReceipts: (service) => ({ ...service, getByCommandId: (commandId) => service.getByCommandId(commandId).pipe( Effect.flatMap((receipt) => { if (gated || commandId !== messageCommandId || Option.isSome(receipt)) { return Effect.succeed(receipt); } gated = true; return Deferred.succeed(receiptLookupCompleted, undefined).pipe( Effect.andThen(Deferred.await(allowReceiptLookup)), Effect.as(receipt), ); }), ), }), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const input = launchInput({ command: "command:launch:message-replay", thread: "thread:launch:message-replay", message: "Use the durable message once", }); const first = yield* launches.launch(input).pipe(Effect.forkChild); yield* Deferred.await(receiptLookupCompleted); const accepted = yield* launches.launch(input); const acceptedRunId = accepted.initialMessageRunId; assert.isNotNull(acceptedRunId); yield* threads.dispatch({ type: "message.dispatch", commandId: CommandId.make("command:launch:message-replay:later-message"), threadId: accepted.threadId, messageId: MessageId.make("message:launch:message-replay:later-message"), text: "A later queued message", attachments: [], modelSelection, dispatchMode: { type: "queue_after_active" }, createdBy: "user", creationSource: "web", }); yield* Deferred.succeed(allowReceiptLookup, undefined); const replayed = yield* Fiber.join(first); assert.isFalse(accepted.initialMessageReplayed); assert.isTrue(replayed.initialMessageReplayed); assert.equal(replayed.initialMessageRunId, acceptedRunId); assert.lengthOf(replayed.projection.messages, 2); assert.lengthOf(replayed.projection.runs, 2); assert.notEqual(replayed.projection.runs.at(-1)?.id, replayed.initialMessageRunId); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("enqueues provider work only after setup has been initiated", () => Effect.gen(function* () { const setupEntered = yield* Deferred.make(); const allowSetup = yield* Deferred.make(); const harness = makeHarness({ runSetup: () => Deferred.succeed(setupEntered, undefined).pipe( Effect.andThen(Deferred.await(allowSetup)), Effect.as({ status: "started" as const, scriptId: "setup", scriptName: "Setup", terminalId: "setup", cwd: "/repo", }), ), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const outbox = yield* EffectOutbox.EffectOutboxV2; const threads = yield* ThreadManagement.ThreadManagementService; const input = launchInput({ command: "command:launch:release", thread: "thread:launch:release", message: "Start after setup", workspace: { type: "worktree", baseRef: "main" }, }); const launched = yield* launches.launch(input); yield* Deferred.await(setupEntered); assert.isEmpty( yield* outbox.listByCommandId(CommandId.make("command:launch:release:release")), ); yield* Deferred.succeed(allowSetup, undefined); yield* waitUntil(() => outbox .listByCommandId(CommandId.make("command:launch:release:release")) .pipe(Effect.map((effects) => effects.length === 1)), ); const projection = yield* threads.getThreadProjection(launched.threadId); assert.equal(projection.runs[0]?.status, "starting"); assert.equal(projection.checkpointScopes[0]?.cwd, "/repo-worktrees/feature"); assert.equal( projection.turnItems.find((item) => item.type === "command_execution")?.status, "completed", ); }).pipe(Effect.provide(harness.layer)); }), ); it.effect( "queues follow-up messages behind preparation and checkpoints them in the final workspace", () => Effect.gen(function* () { const setupEntered = yield* Deferred.make(); const failSetup = yield* Deferred.make(); const harness = makeHarness({ runSetup: () => Deferred.succeed(setupEntered, undefined).pipe( Effect.andThen(Deferred.await(failSetup)), Effect.andThen(Effect.fail(new Error("setup failed") as never)), ), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const launched = yield* launches.launch( launchInput({ command: "command:launch:queued-during-preparation", thread: "thread:launch:queued-during-preparation", message: "Prepare the workspace", workspace: { type: "worktree", baseRef: "main" }, }), ); yield* Deferred.await(setupEntered); const followUp = yield* threads.sendToThread({ projectId, commandId: CommandId.make("command:launch:queued-follow-up"), threadId: launched.threadId, messageId: MessageId.make("message:launch:queued-follow-up"), text: "Run after preparation", attachments: [], mode: "auto", createdBy: "user", creationSource: "web", }); assert.equal(followUp.delivery, "queued"); assert.equal(followUp.run.status, "queued"); assert.equal( followUp.projection.nodes.find( (node) => node.runId === followUp.run.id && node.kind === "root_turn", )?.checkpointScopeId, null, ); yield* Deferred.succeed(failSetup, undefined); yield* waitUntil(() => threads .getThreadProjection(launched.threadId) .pipe( Effect.map( (projection) => projection.runs.find((run) => run.id === followUp.run.id)?.status === "starting", ), ), ); const projection = yield* threads.getThreadProjection(launched.threadId); const rootNode = projection.nodes.find( (node) => node.runId === followUp.run.id && node.kind === "root_turn", ); assert.isNotNull(rootNode?.checkpointScopeId); assert.equal( projection.checkpointScopes.find((scope) => scope.id === rootNode?.checkpointScopeId) ?.cwd, "/repo-worktrees/feature", ); }).pipe(Effect.provide(harness.layer)); }), ); for (const nativeCommand of [" /COMPACT ", "/logout"]) { it.effect(`uses the first conversation message for a title after ${nativeCommand}`, () => Effect.gen(function* () { const harness = makeHarness(); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const outbox = yield* EffectOutbox.EffectOutboxV2; const launched = yield* launches.launch({ ...launchInput({ command: "compact-title", thread: "compact-title-thread", message: nativeCommand, }), generateTitle: true, }); assert.isUndefined( (yield* threads.getThreadProjection(launched.threadId)).thread.titleRegeneration, ); assert.isFalse( (yield* outbox.listByCommandId(CommandId.make("compact-title:initial-message"))).some( (effect) => effect.request.type === "thread-title.generate", ), ); const commandId = CommandId.make("compact-title-conversation"); const messageId = MessageId.make("compact-title-conversation-message"); yield* threads.dispatch({ type: "message.dispatch", commandId, threadId: launched.threadId, messageId, createdBy: "user", creationSource: "web", text: "Fix the failing parser", attachments: [], dispatchMode: { type: "defer_start" }, }); assert.equal( (yield* threads.getThreadProjection(launched.threadId)).thread.titleRegeneration ?.requestId, commandId, ); assert.deepEqual( (yield* outbox.listByCommandId(commandId)) .filter((effect) => effect.request.type === "thread-title.generate") .map((effect) => effect.request), [{ type: "thread-title.generate", kind: { type: "initial", messageId } }], ); }).pipe(Effect.provide(harness.layer)); }), ); } it.effect("keeps native maintenance commands out of steering and restart messages", () => Effect.gen(function* () { const harness = makeHarness(); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const outbox = yield* EffectOutbox.EffectOutboxV2; for (const scenario of [ { name: "compact-steer", first: "Fix the parser", next: " /COMPACT ", mode: "steer_active", }, { name: "compact-restart", first: "Fix the parser", next: "/compact", mode: "restart_active", }, { name: "logout-steer", first: "Fix the parser", next: "/logout", mode: "steer_active", }, { name: "logout-restart", first: "Fix the parser", next: "/logout", mode: "restart_active", }, { name: "steer-logout", first: "/logout", next: "Continue with the parser", mode: "steer_active", }, { name: "steer-compaction", first: "/compact", next: "Continue with the parser", mode: "steer_active", }, ] as const) { const launched = yield* launches.launch( launchInput({ command: `${scenario.name}:launch`, thread: scenario.name, message: scenario.first, }), ); const before = yield* threads.getThreadProjection(launched.threadId); const targetRun = before.runs[0]; if (targetRun === undefined) return yield* Effect.die("Launch must create a run"); const commandId = CommandId.make(`${scenario.name}:message`); const failure = yield* threads .dispatch({ type: "message.dispatch", commandId, threadId: launched.threadId, messageId: MessageId.make(`${scenario.name}:message`), createdBy: "user", creationSource: "web", text: scenario.next, attachments: [], dispatchMode: { type: scenario.mode, targetRunId: targetRun.id }, }) .pipe(Effect.flip); assert.include( String(failure.cause).toLowerCase(), scenario.name.includes("logout") ? "sign" : "context compaction", ); const after = yield* threads.getThreadProjection(launched.threadId); assert.deepEqual(after.messages, before.messages); assert.deepEqual(after.thread.titleRegeneration, before.thread.titleRegeneration); assert.deepEqual(yield* outbox.listByCommandId(commandId), []); } }).pipe(Effect.provide(harness.layer)); }), ); it.effect("arms durable title generation after accepting the first message", () => Effect.gen(function* () { const harness = makeHarness({ generateTitle: (input) => Effect.succeed({ title: input.previousTitle === undefined ? "Generated title" : "Regenerated title", }), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const outbox = yield* EffectOutbox.EffectOutboxV2; const titleRegeneration = yield* ThreadTitleRegeneration.ThreadTitleRegenerationService; const input = { ...launchInput({ command: "command:launch:title-generation", thread: "thread:launch:title-generation", message: "Generate my title", }), title: "Generate my title", generateTitle: true, }; const launched = yield* launches.launch(input); const generationCommandId = CommandId.make("command:launch:title-generation:initial-message"); const projection = yield* threads.getThreadProjection(launched.threadId); assert.equal(projection.thread.title, "Generate my title"); assert.equal(projection.thread.titleRegeneration?.requestId, generationCommandId); assert.deepEqual( (yield* outbox.listByCommandId(generationCommandId)).map((effect) => effect.request), [ { type: "thread-title.generate", kind: { type: "initial", messageId: MessageId.make("Generate my title:id") }, }, ], ); yield* titleRegeneration.execute({ threadId: launched.threadId, requestId: generationCommandId, kind: { type: "initial", messageId: MessageId.make("Generate my title:id") }, }); const generated = yield* threads.getThreadProjection(launched.threadId); assert.equal(generated.thread.title, "Generated title"); assert.deepEqual( harness.generateThreadTitle.mock.calls[0]?.[0].modelSelection, DEFAULT_SERVER_SETTINGS.textGenerationModelSelection, ); const manualRequestId = CommandId.make("command:title-generation:manual"); yield* threads.dispatch({ type: "thread.metadata.update", commandId: manualRequestId, threadId: launched.threadId, regenerateTitle: true, }); assert.deepEqual( (yield* outbox.listByCommandId(manualRequestId)).map((effect) => effect.request), [{ type: "thread-title.generate", kind: { type: "regenerate" } }], ); yield* titleRegeneration.execute({ threadId: launched.threadId, requestId: manualRequestId, kind: { type: "regenerate" }, }); const regenerated = yield* threads.getThreadProjection(launched.threadId); assert.equal(regenerated.thread.title, "Regenerated title"); assert.equal(harness.generateThreadTitle.mock.calls[1]?.[0].previousTitle, "Generated title"); yield* threads.dispatch({ type: "thread.metadata.update", commandId: CommandId.make("command:title-generation:user-rename"), threadId: launched.threadId, title: "Keep my title", }); const renamed = yield* threads.getThreadProjection(launched.threadId); yield* TestClock.adjust(Duration.seconds(1)); yield* threads.dispatch({ type: "thread.title.regeneration.complete", commandId: CommandId.make("command:title-generation:stale-completion"), threadId: launched.threadId, requestId: generationCommandId, title: "Stale generated title", }); const afterStaleCompletion = yield* threads.getThreadProjection(launched.threadId); assert.equal(afterStaleCompletion.thread.title, "Keep my title"); assert.equal( DateTime.toEpochMillis(afterStaleCompletion.thread.updatedAt), DateTime.toEpochMillis(renamed.thread.updatedAt), ); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("does not update a reused thread title when the initial message is rejected", () => Effect.gen(function* () { const harness = makeHarness(); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const outbox = yield* EffectOutbox.EffectOutboxV2; const threadId = ThreadId.make("thread:launch:reused-title-failure"); yield* threads.dispatch({ type: "thread.create", commandId: CommandId.make("command:launch:reused-title-failure:create"), threadId, projectId, title: "Original title", modelSelection, runtimeMode: "full-access", interactionMode: "default", branch: null, worktreePath: null, createdBy: "user", creationSource: "web", }); const commandId = CommandId.make("command:launch:reused-title-failure"); const failed = yield* launches .launch({ ...launchInput({ command: commandId, thread: threadId, message: "Generate a provisional title", }), reuseExistingThread: true, title: "Generate a provisional title", generateTitle: true, modelSelection: { instanceId: ProviderInstanceId.make("missing-provider"), model: "missing-model", }, }) .pipe(Effect.exit); assert.isTrue(Exit.isFailure(failed)); const projection = yield* threads.getThreadProjection(threadId); assert.equal(projection.thread.title, "Original title"); assert.isUndefined(projection.thread.titleRegeneration); assert.isEmpty(projection.messages); assert.isEmpty(yield* outbox.listByCommandId(CommandId.make(`${commandId}:initial-message`))); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("generates an initial title for an attachment-only message", () => Effect.gen(function* () { const harness = makeHarness(); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const titleRegeneration = yield* ThreadTitleRegeneration.ThreadTitleRegenerationService; const messageId = MessageId.make("message:image-only"); const input = { ...launchInput({ command: "command:launch:image-only", thread: "thread:launch:image-only", }), title: "Image: screenshot.png", generateTitle: true, initialMessage: { messageId, text: "", attachments: [ { type: "image" as const, id: "attachment-image-only", name: "screenshot.png", mimeType: "image/png", sizeBytes: 128, }, ], }, }; const launched = yield* launches.launch(input); yield* titleRegeneration.execute({ threadId: launched.threadId, requestId: CommandId.make("command:launch:image-only:initial-message"), kind: { type: "initial", messageId }, }); assert.equal(harness.generateThreadTitle.mock.calls[0]?.[0].message, ""); assert.equal( harness.generateThreadTitle.mock.calls[0]?.[0].attachments?.[0]?.name, "screenshot.png", ); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("uses the available source control writer for generated worktree branches", () => Effect.gen(function* () { const writerInstanceId = ProviderInstanceId.make("source-control-writer"); const writerModelSelection = { instanceId: writerInstanceId, model: "branch-writer-model", } as const; const harness = makeHarness({ serverSettings: { providerInstances: { [writerInstanceId]: { driver: ProviderDriverKind.make("codex"), config: {}, }, }, sourceControlWriterModelSelection: writerModelSelection, }, providers: [ { instanceId: writerInstanceId, driver: ProviderDriverKind.make("codex"), enabled: true, installed: true, version: null, status: "ready", auth: { status: "authenticated" }, checkedAt: "2026-07-28T00:00:00.000Z", availability: "available", models: [], slashCommands: [], skills: [], }, ], }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; yield* launches.launch( launchInput({ command: "command:launch:source-control-writer", thread: "thread:launch:source-control-writer", message: "Generate a branch with the configured writer", workspace: { type: "worktree", baseRef: "main" }, }), ); yield* waitUntil(() => Effect.sync(() => harness.generateBranchName.mock.calls.length === 1)); assert.deepEqual( harness.generateBranchName.mock.calls[0]?.[0].modelSelection, writerModelSelection, ); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("falls back when the source control writer is unavailable", () => Effect.gen(function* () { const writerInstanceId = ProviderInstanceId.make("missing-source-control-writer"); const harness = makeHarness({ serverSettings: { providerInstances: { [writerInstanceId]: { driver: ProviderDriverKind.make("missing-driver"), config: {}, }, }, sourceControlWriterModelSelection: { instanceId: writerInstanceId, model: "missing-branch-writer-model", }, }, providers: [], }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; yield* launches.launch( launchInput({ command: "command:launch:source-control-writer-fallback", thread: "thread:launch:source-control-writer-fallback", message: "Generate a branch with the available writer", workspace: { type: "worktree", baseRef: "main" }, }), ); yield* waitUntil(() => Effect.sync(() => harness.generateBranchName.mock.calls.length === 1)); assert.deepEqual( harness.generateBranchName.mock.calls[0]?.[0].modelSelection, DEFAULT_SERVER_SETTINGS.textGenerationModelSelection, ); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("names the worktree itself when the client provides no branch", () => Effect.gen(function* () { const harness = makeHarness(); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const launched = yield* launches.launch( launchInput({ command: "command:launch:server-named-branch", thread: "thread:launch:server-named-branch", message: "Build the feature", workspace: { type: "worktree", baseRef: "main" }, }), ); yield* waitUntil(() => Effect.sync(() => harness.createWorktree.mock.calls.length === 1)); assert.match( harness.createWorktree.mock.calls[0]?.[0].newRefName ?? "", /^t3code\/[0-9a-f]{8}$/u, ); yield* waitUntil(() => threads .getThreadProjection(launched.threadId) .pipe(Effect.map((projection) => projection.thread.branch === "generated-branch")), ); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("renames a temporary t3code/ branch off the provisioning critical path", () => Effect.gen(function* () { const branchNameStarted = yield* Deferred.make(); const allowBranchName = yield* Deferred.make(); const harness = makeHarness({ createWorktree: (input) => Effect.succeed({ worktree: { path: "/repo-worktrees/temp", refName: input.newRefName, headSha: "abc" }, } as never), generateBranchName: () => Deferred.succeed(branchNameStarted, undefined).pipe( Effect.andThen(Deferred.await(allowBranchName)), Effect.as({ branch: "generated-branch" }), ), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const launched = yield* launches.launch( launchInput({ command: "command:launch:temp-branch", thread: "thread:launch:temp-branch", message: "Build the feature", workspace: { type: "worktree", baseRef: "main", branch: "t3code/abcd1234" }, }), ); yield* Deferred.await(branchNameStarted); assert.equal(harness.createWorktree.mock.calls[0]?.[0].newRefName, "t3code/abcd1234"); yield* waitUntil(() => threads .getThreadProjection(launched.threadId) .pipe(Effect.map((projection) => projection.runs[0]?.status === "starting")), ); assert.equal( (yield* threads.getThreadProjection(launched.threadId)).thread.branch, "t3code/abcd1234", ); yield* Deferred.succeed(allowBranchName, undefined); yield* waitUntil(() => threads .getThreadProjection(launched.threadId) .pipe(Effect.map((projection) => projection.thread.branch === "generated-branch")), ); assert.deepEqual(harness.renameBranch.mock.calls[0]?.[0], { cwd: "/repo-worktrees/temp", oldBranch: "t3code/abcd1234", newBranch: "generated-branch", }); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("keeps an explicit branch name instead of generating one", () => Effect.gen(function* () { const harness = makeHarness(); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; yield* launches.launch( launchInput({ command: "command:launch:explicit-branch", thread: "thread:launch:explicit-branch", message: "Build the feature", workspace: { type: "worktree", baseRef: "main", branch: "my-feature" }, }), ); yield* waitUntil(() => Effect.sync(() => harness.createWorktree.mock.calls.length === 1)); assert.equal(harness.generateBranchName.mock.calls.length, 0); assert.equal(harness.createWorktree.mock.calls[0]?.[0].newRefName, "my-feature"); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("keeps the temporary branch when branch generation fails", () => Effect.gen(function* () { const harness = makeHarness({ createWorktree: (input) => Effect.succeed({ worktree: { path: "/repo-worktrees/temp", refName: input.newRefName, headSha: "abc" }, } as never), generateBranchName: () => Effect.die("branch generation is down"), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const launched = yield* launches.launch( launchInput({ command: "command:launch:branch-fallback", thread: "thread:launch:branch-fallback", message: "Build the feature", workspace: { type: "worktree", baseRef: "main", branch: "t3code/abcd1234" }, }), ); yield* waitUntil(() => Effect.sync(() => harness.generateBranchName.mock.calls.length === 1)); assert.equal(harness.createWorktree.mock.calls[0]?.[0].newRefName, "t3code/abcd1234"); yield* waitUntil(() => threads .getThreadProjection(launched.threadId) .pipe(Effect.map((projection) => projection.runs[0]?.status === "starting")), ); assert.equal(harness.renameBranch.mock.calls.length, 0); assert.equal( (yield* threads.getThreadProjection(launched.threadId)).thread.branch, "t3code/abcd1234", ); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("renames a temporary branch on an existing worktree to a generated name", () => Effect.gen(function* () { const harness = makeHarness(); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const launched = yield* launches.launch( launchInput({ command: "command:launch:existing-worktree-rename", thread: "thread:launch:existing-worktree-rename", message: "Build the feature", workspace: { type: "existing_worktree", worktreePath: "/repo-worktrees/t3code-abcd1234", branch: "t3code/abcd1234", }, }), ); yield* waitUntil(() => threads .getThreadProjection(launched.threadId) .pipe(Effect.map((projection) => projection.thread.branch === "generated-branch")), ); assert.deepEqual(harness.renameBranch.mock.calls[0]?.[0], { cwd: "/repo-worktrees/t3code-abcd1234", oldBranch: "t3code/abcd1234", newBranch: "generated-branch", }); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("shows the fetch diagnosis when preparing a worktree from origin fails", () => { const detail = "Git could not authenticate with the remote. Check Git credentials or SSH access on the server, then retry."; const harness = makeHarness({ fetchRemote: () => Effect.fail( new GitCommandError({ operation: "GitVcsDriver.fetchRemote", command: "git", cwd: project.workspaceRoot, detail, exitCode: 128, }), ), }); return Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const launched = yield* launches.launch( launchInput({ command: "command:launch:fetch-failure", thread: "thread:launch:fetch-failure", message: "Start from origin", workspace: { type: "worktree", baseRef: "main", startFromOrigin: true }, }), ); yield* threads.streamStoredEventsFrom({ threadId: launched.threadId }).pipe( Stream.filter( (stored) => stored.event.type === "run.updated" && stored.event.payload.status === "failed", ), Stream.runHead, ); const projection = yield* threads.getThreadProjection(launched.threadId); assert.equal(projection.messages[0]?.text, "Start from origin"); assert.equal(projection.runs[0]?.status, "failed"); assert.equal(projection.thread.worktreePath, null); assert.equal( projection.turnItems.find((item) => item.type === "command_execution")?.status, "failed", ); assert.include( projection.turnItems.find((item) => item.type === "error")?.failure.message ?? "", detail, ); assert.equal(harness.createWorktree.mock.calls.length, 0); assert.equal(harness.runSetup.mock.calls.length, 0); }).pipe(Effect.provide(harness.layer)); }); for (const failurePoint of ["worktree", "setup"] as const) { it.effect( `${failurePoint} failure keeps the thread and message visible and emits failure items`, () => Effect.gen(function* () { const failure = new Error(`${failurePoint} failed`); const harness = makeHarness( failurePoint === "worktree" ? { createWorktree: () => Effect.fail(failure as never) } : { runSetup: () => Effect.fail(failure as never) }, ); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const input = launchInput({ command: `command:launch:${failurePoint}-failure`, thread: `thread:launch:${failurePoint}-failure`, message: `Fail during ${failurePoint}`, workspace: { type: "worktree", baseRef: "main" }, }); const launched = yield* launches.launch(input); yield* waitUntil(() => threads .getThreadProjection(launched.threadId) .pipe(Effect.map((projection) => projection.runs[0]?.status === "failed")), ); const projection = yield* threads.getThreadProjection(launched.threadId); assert.equal(projection.messages[0]?.text, `Fail during ${failurePoint}`); assert.equal(projection.runs[0]?.status, "failed"); assert.equal( projection.turnItems.find((item) => item.type === "command_execution")?.status, "failed", ); assert.match( projection.turnItems.find((item) => item.type === "error")?.failure.message ?? "", new RegExp(`${failurePoint} failed`, "u"), ); }).pipe(Effect.provide(harness.layer)); }), ); } it.effect("deduplicates retried launch side effects in-process", () => Effect.gen(function* () { const setupEntered = yield* Deferred.make(); const allowSetup = yield* Deferred.make(); const harness = makeHarness({ runSetup: () => Deferred.succeed(setupEntered, undefined).pipe( Effect.andThen(Deferred.await(allowSetup)), Effect.as({ status: "no-script" as const }), ), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const input = launchInput({ command: "command:launch:retry", thread: "thread:launch:retry", message: "Only once", }); const [first, retry] = yield* Effect.all([launches.launch(input), launches.launch(input)], { concurrency: "unbounded", }); yield* Deferred.await(setupEntered); assert.equal(first.threadId, retry.threadId); assert.isFalse(first.resumed); assert.isTrue(retry.resumed); assert.equal(harness.runSetup.mock.calls.length, 1); yield* Deferred.succeed(allowSetup, undefined); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("does not let a failing same-command caller strand a concurrent durable launch", () => Effect.gen(function* () { const setupEntered = yield* Deferred.make(); const allowSetup = yield* Deferred.make(); const harness = makeHarness({ runSetup: () => Deferred.succeed(setupEntered, undefined).pipe( Effect.andThen(Deferred.await(allowSetup)), Effect.as({ status: "no-script" as const }), ), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const command = "command:launch:failed-owner-race"; const [failed, launched] = yield* Effect.all( [ launches .launch({ ...launchInput({ command, thread: "thread:launch:failed-owner-race", message: "This invalid reuse fails", }), reuseExistingThread: true, }) .pipe(Effect.exit), launches.launch( launchInput({ command, thread: "thread:launch:successful-peer", message: "This peer persists", }), ), ], { concurrency: "unbounded" }, ); assert.isTrue(Exit.isFailure(failed)); assert.equal(launched.projection.runs[0]?.status, "preparing"); const entered = yield* Deferred.await(setupEntered).pipe( Effect.timeoutOption(Duration.seconds(2)), ); assert.isTrue(Option.isSome(entered)); assert.equal(harness.runSetup.mock.calls.length, 1); yield* Deferred.succeed(allowSetup, undefined); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("schedules an accepted preparing message exactly once across concurrent retries", () => Effect.gen(function* () { const setupEntered = yield* Deferred.make(); const allowSetup = yield* Deferred.make(); const harness = makeHarness({ runSetup: () => Deferred.succeed(setupEntered, undefined).pipe( Effect.andThen(Deferred.await(allowSetup)), Effect.as({ status: "no-script" as const }), ), }); yield* Effect.gen(function* () { const launches = yield* ThreadLaunch.ThreadLaunchService; const threads = yield* ThreadManagement.ThreadManagementService; const input = launchInput({ command: "command:launch:accepted-before-fork", thread: "thread:launch:accepted-before-fork", message: "Resume preparation", }); const messageId = MessageId.make("message:launch:accepted-before-fork"); yield* threads.dispatch({ type: "thread.create", commandId: input.commandId, threadId: input.threadId, projectId: input.projectId, title: input.title, modelSelection: input.modelSelection, runtimeMode: input.runtimeMode, interactionMode: input.interactionMode, branch: null, worktreePath: null, createdBy: input.createdBy, creationSource: input.creationSource, }); yield* threads.dispatch({ type: "message.dispatch", commandId: CommandId.make(`${input.commandId}:initial-message`), threadId: input.threadId, messageId, text: "Resume preparation", attachments: [], modelSelection: input.modelSelection, dispatchMode: { type: "defer_start" }, createdBy: input.createdBy, creationSource: input.creationSource, }); const preparing = yield* threads.getThreadProjection(input.threadId); assert.equal(preparing.runs[0]?.status, "preparing"); const [first, second] = yield* Effect.all([launches.launch(input), launches.launch(input)], { concurrency: "unbounded", }); yield* Deferred.await(setupEntered); assert.isTrue(first.resumed); assert.isTrue(second.resumed); assert.equal(harness.runSetup.mock.calls.length, 1); yield* Deferred.succeed(allowSetup, undefined); }).pipe(Effect.provide(harness.layer)); }), ); it.effect("does not depend on the legacy launch workflow table", () => { const harness = makeHarness(); return Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; const launches = yield* ThreadLaunch.ThreadLaunchService; yield* sql`DROP TABLE orchestration_v2_thread_launch_workflows`; const launched = yield* launches.launch( launchInput({ command: "command:launch:no-workflow-table", thread: "thread:launch:no-workflow-table", message: "No private workflow state", }), ); assert.equal(launched.projection.messages[0]?.text, "No private workflow state"); }).pipe(Effect.provide(harness.layer)); });