import { ProjectId, ThreadId, TurnId, ProviderInstanceId, OrchestrationProposedPlanId,} from "@t3tools/contracts";import { assert, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as SqlClient from "effect/unstable/sql/SqlClient";import * as Statement from "effect/unstable/sql/Statement"; import { SqlitePersistenceMemory } from "./Sqlite.ts";import { ProjectionProjectRepositoryLive } from "./ProjectionProjects.ts";import { ProjectionThreadRepositoryLive } from "./ProjectionThreads.ts";import * as ProjectionThreadPullRequests from "../ProjectionThreadPullRequests.ts";import { ProjectionProjectRepository } from "../Services/ProjectionProjects.ts";import { ProjectionThreadRepository } from "../Services/ProjectionThreads.ts";import { ProjectionThreadPullRequestRepository, type ProjectionThreadPullRequest,} from "../ProjectionThreadPullRequests.ts";import { ProjectionThreadProposedPlanRepositoryLive } from "./ProjectionThreadProposedPlans.ts";import { ProjectionThreadProposedPlanRepository } from "../Services/ProjectionThreadProposedPlans.ts"; const projectionRepositoriesLayer = it.layer( Layer.mergeAll( ProjectionProjectRepositoryLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)), ProjectionThreadRepositoryLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)), ProjectionThreadPullRequests.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)), ProjectionThreadProposedPlanRepositoryLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)), SqlitePersistenceMemory, ),); projectionRepositoriesLayer("Projection repositories", (it) => { it.effect("selects the latest-turn plan before checking implementation status", () => Effect.gen(function* () { const plans = yield* ProjectionThreadProposedPlanRepository; const threadId = ThreadId.make("thread-plan-status"); const latestTurnId = TurnId.make("turn-plan-status-current"); const firstPlan = { planId: "plan-status-first", threadId, turnId: latestTurnId, planMarkdown: "# First plan", implementedAt: null, implementationThreadId: null, createdAt: "2026-03-24T00:00:01.000Z", updatedAt: "2026-03-24T00:00:01.000Z", }; yield* plans.upsert(firstPlan); yield* plans.upsert({ ...firstPlan, planId: "plan-status-implemented", implementedAt: "2026-03-24T00:00:02.000Z", createdAt: "2026-03-24T00:00:02.000Z", updatedAt: "2026-03-24T00:00:02.000Z", }); yield* plans.upsert({ ...firstPlan, planId: "plan-status-other-turn", turnId: TurnId.make("turn-plan-status-old"), updatedAt: "2026-03-24T00:00:10.000Z", }); assert.isFalse(yield* plans.hasActionableByThreadId({ threadId, latestTurnId })); assert.isTrue(yield* plans.hasActionableByThreadId({ threadId, latestTurnId: null })); yield* plans.upsert({ ...firstPlan, updatedAt: "2026-03-24T00:00:03.000Z" }); assert.isTrue(yield* plans.hasActionableByThreadId({ threadId, latestTurnId })); }), ); it.effect("falls back within the thread when the latest turn has no plan", () => Effect.gen(function* () { const plans = yield* ProjectionThreadProposedPlanRepository; const threadId = ThreadId.make("thread-plan-fallback"); const latestTurnId = TurnId.make("turn-plan-fallback-missing"); assert.isFalse(yield* plans.hasActionableByThreadId({ threadId, latestTurnId })); assert.isFalse(yield* plans.hasActionableByThreadId({ threadId, latestTurnId: null })); const firstPlan = { planId: "plan-fallback-without-turn", threadId, turnId: null, planMarkdown: "# Old plan", implementedAt: "2026-03-24T00:00:01.000Z", implementationThreadId: null, createdAt: "2026-03-24T00:00:01.000Z", updatedAt: "2026-03-24T00:00:01.000Z", }; yield* plans.upsert(firstPlan); yield* plans.upsert({ ...firstPlan, planId: "plan-fallback-with-turn", turnId: TurnId.make("turn-plan-fallback-old"), implementedAt: null, updatedAt: "2026-03-24T00:00:02.000Z", }); yield* plans.upsert({ ...firstPlan, planId: "plan-fallback-other-thread", threadId: ThreadId.make("thread-plan-fallback-other"), turnId: latestTurnId, updatedAt: "2026-03-24T00:00:03.000Z", }); assert.isTrue(yield* plans.hasActionableByThreadId({ threadId, latestTurnId })); assert.isTrue(yield* plans.hasActionableByThreadId({ threadId, latestTurnId: null })); }), ); it.effect("preserves locale ordering and stable ties when selecting plan status", () => Effect.gen(function* () { const plans = yield* ProjectionThreadProposedPlanRepository; const timestamp = "2026-03-24T00:00:00.000Z"; const cases = [ { name: "mixed-case-ids", expected: "plan-A".localeCompare("plan-a") > 0, rows: [ { planId: "plan-a", implementedAt: timestamp, createdAt: timestamp, updatedAt: timestamp, }, { planId: "plan-A", implementedAt: null, createdAt: timestamp, updatedAt: timestamp }, ], }, { name: "equivalent-ids", expected: true, rows: [ { planId: "plan-\u00e9", implementedAt: timestamp, createdAt: timestamp, updatedAt: timestamp, }, { planId: "plan-e\u0301", implementedAt: null, createdAt: "2026-03-24T00:00:01.000Z", updatedAt: timestamp, }, ], }, { name: "timestamp-formats", expected: "2026-03-24T00:00:00+00:00".localeCompare("2026-03-24T00:00:00-01:00") > 0, rows: [ { planId: "plan-minus", implementedAt: timestamp, createdAt: timestamp, updatedAt: "2026-03-24T00:00:00-01:00", }, { planId: "plan-plus", implementedAt: null, createdAt: timestamp, updatedAt: "2026-03-24T00:00:00+00:00", }, ], }, ]; for (const testCase of cases) { const threadId = ThreadId.make(`thread-plan-order-${testCase.name}`); const latestTurnId = TurnId.make(`turn-plan-order-${testCase.name}`); for (const plan of testCase.rows) { yield* plans.upsert({ ...plan, planId: `${testCase.name}-${plan.planId}`, threadId, turnId: latestTurnId, planMarkdown: "# Plan", implementationThreadId: null, }); } assert.strictEqual( yield* plans.hasActionableByThreadId({ threadId, latestTurnId }), testCase.expected, testCase.name, ); assert.strictEqual( yield* plans.hasActionableByThreadId({ threadId, latestTurnId: null }), testCase.expected, testCase.name, ); } }), ); it.effect("returns only current-turn status metadata when old plans have large bodies", () => Effect.gen(function* () { const plans = yield* ProjectionThreadProposedPlanRepository; const sql = yield* SqlClient.SqlClient; const threadId = ThreadId.make("thread-plan-metadata"); const latestTurnId = TurnId.make("turn-plan-metadata-current"); const updatedAt = "2026-03-24T00:00:00.000Z"; yield* sql` WITH RECURSIVE history(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM history WHERE n < 256 ) INSERT INTO projection_thread_proposed_plans ( plan_id, thread_id, turn_id, plan_markdown, implemented_at, implementation_thread_id, created_at, updated_at ) SELECT 'plan-metadata-old-' || n, ${threadId}, 'turn-plan-metadata-old', ${"# Old plan\n".repeat(1024)}, ${updatedAt}, NULL, ${updatedAt}, ${updatedAt} FROM history `; yield* plans.upsert({ planId: "plan-metadata-current", threadId, turnId: latestTurnId, planMarkdown: "# Current plan", implementedAt: null, implementationThreadId: null, createdAt: updatedAt, updatedAt, }); const statements: Array<Statement.Statement<unknown>> = []; const actionable = yield* plans.hasActionableByThreadId({ threadId, latestTurnId }).pipe( Effect.provideService(Statement.CurrentTransformer, (statement) => { statements.push(statement); return Effect.succeed(statement); }), ); assert.isTrue(actionable); assert.strictEqual(statements.length, 1); const statement = statements[0]; if (statement === undefined) return yield* Effect.die("Expected a plan status query."); assert.deepEqual(yield* statement, [ { planId: "plan-metadata-current", implementedAt: null, updatedAt }, ]); }), ); it.effect("reads only the requested plan in its thread", () => Effect.gen(function* () { const plans = yield* ProjectionThreadProposedPlanRepository; const sql = yield* SqlClient.SqlClient; const threadId = ThreadId.make("plan-query-thread"); const planId = OrchestrationProposedPlanId.make("plan-query-target"); yield* plans.upsert({ planId, threadId, turnId: null, planMarkdown: "Keep this plan", implementedAt: "2026-03-01T00:01:00.000Z", implementationThreadId: ThreadId.make("implementation-thread"), createdAt: "2026-03-01T00:00:00.000Z", updatedAt: "2026-03-01T00:01:00.000Z", }); // An unrelated old row must not be loaded or decoded by the exact lookup. yield* sql` INSERT INTO projection_thread_proposed_plans ( plan_id, thread_id, turn_id, plan_markdown, implemented_at, implementation_thread_id, created_at, updated_at ) VALUES ( 'unrelated-plan', ${threadId}, NULL, '', NULL, NULL, '2026-03-01T00:00:00.000Z', '2026-03-01T00:00:00.000Z' ) `; const plan = Option.getOrThrow(yield* plans.getByPlanId({ threadId, planId })); assert.equal(plan.planMarkdown, "Keep this plan"); assert.equal(plan.implementedAt, "2026-03-01T00:01:00.000Z"); assert.isTrue( Option.isNone( yield* plans.getByPlanId({ threadId: ThreadId.make("another-thread"), planId, }), ), ); }), ); it.effect("stores SQL NULL for missing project model options", () => Effect.gen(function* () { const projects = yield* ProjectionProjectRepository; const sql = yield* SqlClient.SqlClient; yield* projects.upsert({ projectId: ProjectId.make("project-null-options"), title: "Null options project", workspaceRoot: "/tmp/project-null-options", defaultModelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4", }, defaultThreadEnvMode: null, autoPull: false, scripts: [], createdAt: "2026-03-24T00:00:00.000Z", updatedAt: "2026-03-24T00:00:00.000Z", deletedAt: null, }); const rows = yield* sql<{ readonly defaultModelSelection: string | null; }>` SELECT default_model_selection_json AS "defaultModelSelection" FROM projection_projects WHERE project_id = 'project-null-options' `; const row = rows[0]; if (!row) { return yield* Effect.die("Expected projection_projects row to exist."); } assert.strictEqual( row.defaultModelSelection, // @effect-diagnostics-next-line preferSchemaOverJson:off JSON.stringify({ instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4", }), ); const persisted = yield* projects.getById({ projectId: ProjectId.make("project-null-options"), }); assert.deepStrictEqual(Option.getOrNull(persisted)?.defaultModelSelection, { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4", }); }), ); it.effect("stores JSON for thread model options", () => Effect.gen(function* () { const threads = yield* ProjectionThreadRepository; const sql = yield* SqlClient.SqlClient; yield* threads.upsert({ threadId: ThreadId.make("thread-null-options"), projectId: ProjectId.make("project-null-options"), title: "Null options thread", modelSelection: { instanceId: ProviderInstanceId.make("claudeAgent"), model: "claude-opus-4-6", }, runtimeMode: "full-access", interactionMode: "default", branch: null, worktreePath: null, latestTurnId: null, createdAt: "2026-03-24T00:00:00.000Z", updatedAt: "2026-03-24T00:00:00.000Z", archivedAt: null, settledOverride: null, settledAt: null, unsettledAt: null, snoozedUntil: null, snoozedAt: null, pinnedAt: null, latestUserMessageAt: null, pendingApprovalCount: 0, pendingUserInputCount: 0, hasActionableProposedPlan: 0, deletedAt: null, }); const rows = yield* sql<{ readonly modelSelection: string | null; }>` SELECT model_selection_json AS "modelSelection" FROM projection_threads WHERE thread_id = 'thread-null-options' `; const row = rows[0]; if (!row) { return yield* Effect.die("Expected projection_threads row to exist."); } assert.strictEqual( row.modelSelection, // @effect-diagnostics-next-line preferSchemaOverJson:off JSON.stringify({ instanceId: ProviderInstanceId.make("claudeAgent"), model: "claude-opus-4-6", }), ); const persisted = yield* threads.getById({ threadId: ThreadId.make("thread-null-options"), }); assert.deepStrictEqual(Option.getOrNull(persisted)?.modelSelection, { instanceId: ProviderInstanceId.make("claudeAgent"), model: "claude-opus-4-6", }); }), ); it.effect("round-trips non-null settlement values through the thread row", () => Effect.gen(function* () { const threads = yield* ProjectionThreadRepository; yield* threads.upsert({ threadId: ThreadId.make("thread-settled"), projectId: ProjectId.make("project-1"), title: "Settled thread", modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4", }, runtimeMode: "full-access", interactionMode: "default", branch: null, worktreePath: null, latestTurnId: null, createdAt: "2026-03-24T00:00:00.000Z", updatedAt: "2026-03-25T00:00:00.000Z", archivedAt: null, settledOverride: "settled", settledAt: "2026-03-25T00:00:00.000Z", unsettledAt: null, snoozedUntil: "2026-03-26T09:00:00.000Z", snoozedAt: "2026-03-25T00:00:00.000Z", pinnedAt: "2026-03-25T00:00:00.000Z", latestUserMessageAt: null, pendingApprovalCount: 0, pendingUserInputCount: 0, hasActionableProposedPlan: 0, deletedAt: null, }); const persisted = yield* threads.getById({ threadId: ThreadId.make("thread-settled"), }); const row = Option.getOrNull(persisted); if (!row) { return yield* Effect.die("Expected settled projection_threads row to exist."); } assert.strictEqual(row.settledOverride, "settled"); assert.strictEqual(row.settledAt, "2026-03-25T00:00:00.000Z"); assert.strictEqual(row.snoozedUntil, "2026-03-26T09:00:00.000Z"); assert.strictEqual(row.snoozedAt, "2026-03-25T00:00:00.000Z"); assert.strictEqual(row.pinnedAt, "2026-03-25T00:00:00.000Z"); // Un-settle to the keep-active pin and wake the snooze; confirm the // flips persist. yield* threads.upsert({ ...row, settledOverride: "active", settledAt: null, unsettledAt: "2026-03-26T00:00:00.000Z", snoozedUntil: null, snoozedAt: null, pinnedAt: null, }); const repersisted = yield* threads.getById({ threadId: ThreadId.make("thread-settled"), }); const updated = Option.getOrNull(repersisted); assert.strictEqual(updated?.settledOverride, "active"); assert.strictEqual(updated?.settledAt, null); assert.strictEqual(updated?.unsettledAt, "2026-03-26T00:00:00.000Z"); assert.strictEqual(updated?.snoozedUntil, null); assert.strictEqual(updated?.snoozedAt, null); assert.strictEqual(updated?.pinnedAt, null); }), ); it.effect("round-trips manual and branch pull requests through the thread row", () => Effect.gen(function* () { const threads = yield* ProjectionThreadRepository; const linkedPullRequest = { projectId: ProjectId.make("project-linked-pr"), repository: "pingdotgg/t3code", number: 42, url: "https://github.com/pingdotgg/t3code/pull/42", }; const branchPullRequest = { ...linkedPullRequest, number: 43, url: "https://github.com/pingdotgg/t3code/pull/43", }; yield* threads.upsert({ threadId: ThreadId.make("thread-linked-pr"), projectId: ProjectId.make("project-linked-pr"), title: "Linked pull request", modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4", }, runtimeMode: "full-access", interactionMode: "default", branch: null, worktreePath: null, linkedPullRequest, branchPullRequest, latestTurnId: null,Lines 1–500 of 698. Load more
import { ProjectId, ThreadId, TurnId, ProviderInstanceId, OrchestrationProposedPlanId, } from "@t3tools/contracts"; import { assert, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as Statement from "effect/unstable/sql/Statement"; import { SqlitePersistenceMemory } from "./Sqlite.ts"; import { ProjectionProjectRepositoryLive } from "./ProjectionProjects.ts"; import { ProjectionThreadRepositoryLive } from "./ProjectionThreads.ts"; import * as ProjectionThreadPullRequests from "../ProjectionThreadPullRequests.ts"; import { ProjectionProjectRepository } from "../Services/ProjectionProjects.ts"; import { ProjectionThreadRepository } from "../Services/ProjectionThreads.ts"; import { ProjectionThreadPullRequestRepository, type ProjectionThreadPullRequest, } from "../ProjectionThreadPullRequests.ts"; import { ProjectionThreadProposedPlanRepositoryLive } from "./ProjectionThreadProposedPlans.ts"; import { ProjectionThreadProposedPlanRepository } from "../Services/ProjectionThreadProposedPlans.ts"; const projectionRepositoriesLayer = it.layer( Layer.mergeAll( ProjectionProjectRepositoryLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)), ProjectionThreadRepositoryLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)), ProjectionThreadPullRequests.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)), ProjectionThreadProposedPlanRepositoryLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)), SqlitePersistenceMemory, ), ); projectionRepositoriesLayer("Projection repositories", (it) => { it.effect("selects the latest-turn plan before checking implementation status", () => Effect.gen(function* () { const plans = yield* ProjectionThreadProposedPlanRepository; const threadId = ThreadId.make("thread-plan-status"); const latestTurnId = TurnId.make("turn-plan-status-current"); const firstPlan = { planId: "plan-status-first", threadId, turnId: latestTurnId, planMarkdown: "# First plan", implementedAt: null, implementationThreadId: null, createdAt: "2026-03-24T00:00:01.000Z", updatedAt: "2026-03-24T00:00:01.000Z", }; yield* plans.upsert(firstPlan); yield* plans.upsert({ ...firstPlan, planId: "plan-status-implemented", implementedAt: "2026-03-24T00:00:02.000Z", createdAt: "2026-03-24T00:00:02.000Z", updatedAt: "2026-03-24T00:00:02.000Z", }); yield* plans.upsert({ ...firstPlan, planId: "plan-status-other-turn", turnId: TurnId.make("turn-plan-status-old"), updatedAt: "2026-03-24T00:00:10.000Z", }); assert.isFalse(yield* plans.hasActionableByThreadId({ threadId, latestTurnId })); assert.isTrue(yield* plans.hasActionableByThreadId({ threadId, latestTurnId: null })); yield* plans.upsert({ ...firstPlan, updatedAt: "2026-03-24T00:00:03.000Z" }); assert.isTrue(yield* plans.hasActionableByThreadId({ threadId, latestTurnId })); }), ); it.effect("falls back within the thread when the latest turn has no plan", () => Effect.gen(function* () { const plans = yield* ProjectionThreadProposedPlanRepository; const threadId = ThreadId.make("thread-plan-fallback"); const latestTurnId = TurnId.make("turn-plan-fallback-missing"); assert.isFalse(yield* plans.hasActionableByThreadId({ threadId, latestTurnId })); assert.isFalse(yield* plans.hasActionableByThreadId({ threadId, latestTurnId: null })); const firstPlan = { planId: "plan-fallback-without-turn", threadId, turnId: null, planMarkdown: "# Old plan", implementedAt: "2026-03-24T00:00:01.000Z", implementationThreadId: null, createdAt: "2026-03-24T00:00:01.000Z", updatedAt: "2026-03-24T00:00:01.000Z", }; yield* plans.upsert(firstPlan); yield* plans.upsert({ ...firstPlan, planId: "plan-fallback-with-turn", turnId: TurnId.make("turn-plan-fallback-old"), implementedAt: null, updatedAt: "2026-03-24T00:00:02.000Z", }); yield* plans.upsert({ ...firstPlan, planId: "plan-fallback-other-thread", threadId: ThreadId.make("thread-plan-fallback-other"), turnId: latestTurnId, updatedAt: "2026-03-24T00:00:03.000Z", }); assert.isTrue(yield* plans.hasActionableByThreadId({ threadId, latestTurnId })); assert.isTrue(yield* plans.hasActionableByThreadId({ threadId, latestTurnId: null })); }), ); it.effect("preserves locale ordering and stable ties when selecting plan status", () => Effect.gen(function* () { const plans = yield* ProjectionThreadProposedPlanRepository; const timestamp = "2026-03-24T00:00:00.000Z"; const cases = [ { name: "mixed-case-ids", expected: "plan-A".localeCompare("plan-a") > 0, rows: [ { planId: "plan-a", implementedAt: timestamp, createdAt: timestamp, updatedAt: timestamp, }, { planId: "plan-A", implementedAt: null, createdAt: timestamp, updatedAt: timestamp }, ], }, { name: "equivalent-ids", expected: true, rows: [ { planId: "plan-\u00e9", implementedAt: timestamp, createdAt: timestamp, updatedAt: timestamp, }, { planId: "plan-e\u0301", implementedAt: null, createdAt: "2026-03-24T00:00:01.000Z", updatedAt: timestamp, }, ], }, { name: "timestamp-formats", expected: "2026-03-24T00:00:00+00:00".localeCompare("2026-03-24T00:00:00-01:00") > 0, rows: [ { planId: "plan-minus", implementedAt: timestamp, createdAt: timestamp, updatedAt: "2026-03-24T00:00:00-01:00", }, { planId: "plan-plus", implementedAt: null, createdAt: timestamp, updatedAt: "2026-03-24T00:00:00+00:00", }, ], }, ]; for (const testCase of cases) { const threadId = ThreadId.make(`thread-plan-order-${testCase.name}`); const latestTurnId = TurnId.make(`turn-plan-order-${testCase.name}`); for (const plan of testCase.rows) { yield* plans.upsert({ ...plan, planId: `${testCase.name}-${plan.planId}`, threadId, turnId: latestTurnId, planMarkdown: "# Plan", implementationThreadId: null, }); } assert.strictEqual( yield* plans.hasActionableByThreadId({ threadId, latestTurnId }), testCase.expected, testCase.name, ); assert.strictEqual( yield* plans.hasActionableByThreadId({ threadId, latestTurnId: null }), testCase.expected, testCase.name, ); } }), ); it.effect("returns only current-turn status metadata when old plans have large bodies", () => Effect.gen(function* () { const plans = yield* ProjectionThreadProposedPlanRepository; const sql = yield* SqlClient.SqlClient; const threadId = ThreadId.make("thread-plan-metadata"); const latestTurnId = TurnId.make("turn-plan-metadata-current"); const updatedAt = "2026-03-24T00:00:00.000Z"; yield* sql` WITH RECURSIVE history(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM history WHERE n < 256 ) INSERT INTO projection_thread_proposed_plans ( plan_id, thread_id, turn_id, plan_markdown, implemented_at, implementation_thread_id, created_at, updated_at ) SELECT 'plan-metadata-old-' || n, ${threadId}, 'turn-plan-metadata-old', ${"# Old plan\n".repeat(1024)}, ${updatedAt}, NULL, ${updatedAt}, ${updatedAt} FROM history `; yield* plans.upsert({ planId: "plan-metadata-current", threadId, turnId: latestTurnId, planMarkdown: "# Current plan", implementedAt: null, implementationThreadId: null, createdAt: updatedAt, updatedAt, }); const statements: Array<Statement.Statement<unknown>> = []; const actionable = yield* plans.hasActionableByThreadId({ threadId, latestTurnId }).pipe( Effect.provideService(Statement.CurrentTransformer, (statement) => { statements.push(statement); return Effect.succeed(statement); }), ); assert.isTrue(actionable); assert.strictEqual(statements.length, 1); const statement = statements[0]; if (statement === undefined) return yield* Effect.die("Expected a plan status query."); assert.deepEqual(yield* statement, [ { planId: "plan-metadata-current", implementedAt: null, updatedAt }, ]); }), ); it.effect("reads only the requested plan in its thread", () => Effect.gen(function* () { const plans = yield* ProjectionThreadProposedPlanRepository; const sql = yield* SqlClient.SqlClient; const threadId = ThreadId.make("plan-query-thread"); const planId = OrchestrationProposedPlanId.make("plan-query-target"); yield* plans.upsert({ planId, threadId, turnId: null, planMarkdown: "Keep this plan", implementedAt: "2026-03-01T00:01:00.000Z", implementationThreadId: ThreadId.make("implementation-thread"), createdAt: "2026-03-01T00:00:00.000Z", updatedAt: "2026-03-01T00:01:00.000Z", }); // An unrelated old row must not be loaded or decoded by the exact lookup. yield* sql` INSERT INTO projection_thread_proposed_plans ( plan_id, thread_id, turn_id, plan_markdown, implemented_at, implementation_thread_id, created_at, updated_at ) VALUES ( 'unrelated-plan', ${threadId}, NULL, '', NULL, NULL, '2026-03-01T00:00:00.000Z', '2026-03-01T00:00:00.000Z' ) `; const plan = Option.getOrThrow(yield* plans.getByPlanId({ threadId, planId })); assert.equal(plan.planMarkdown, "Keep this plan"); assert.equal(plan.implementedAt, "2026-03-01T00:01:00.000Z"); assert.isTrue( Option.isNone( yield* plans.getByPlanId({ threadId: ThreadId.make("another-thread"), planId, }), ), ); }), ); it.effect("stores SQL NULL for missing project model options", () => Effect.gen(function* () { const projects = yield* ProjectionProjectRepository; const sql = yield* SqlClient.SqlClient; yield* projects.upsert({ projectId: ProjectId.make("project-null-options"), title: "Null options project", workspaceRoot: "/tmp/project-null-options", defaultModelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4", }, defaultThreadEnvMode: null, autoPull: false, scripts: [], createdAt: "2026-03-24T00:00:00.000Z", updatedAt: "2026-03-24T00:00:00.000Z", deletedAt: null, }); const rows = yield* sql<{ readonly defaultModelSelection: string | null; }>` SELECT default_model_selection_json AS "defaultModelSelection" FROM projection_projects WHERE project_id = 'project-null-options' `; const row = rows[0]; if (!row) { return yield* Effect.die("Expected projection_projects row to exist."); } assert.strictEqual( row.defaultModelSelection, // @effect-diagnostics-next-line preferSchemaOverJson:off JSON.stringify({ instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4", }), ); const persisted = yield* projects.getById({ projectId: ProjectId.make("project-null-options"), }); assert.deepStrictEqual(Option.getOrNull(persisted)?.defaultModelSelection, { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4", }); }), ); it.effect("stores JSON for thread model options", () => Effect.gen(function* () { const threads = yield* ProjectionThreadRepository; const sql = yield* SqlClient.SqlClient; yield* threads.upsert({ threadId: ThreadId.make("thread-null-options"), projectId: ProjectId.make("project-null-options"), title: "Null options thread", modelSelection: { instanceId: ProviderInstanceId.make("claudeAgent"), model: "claude-opus-4-6", }, runtimeMode: "full-access", interactionMode: "default", branch: null, worktreePath: null, latestTurnId: null, createdAt: "2026-03-24T00:00:00.000Z", updatedAt: "2026-03-24T00:00:00.000Z", archivedAt: null, settledOverride: null, settledAt: null, unsettledAt: null, snoozedUntil: null, snoozedAt: null, pinnedAt: null, latestUserMessageAt: null, pendingApprovalCount: 0, pendingUserInputCount: 0, hasActionableProposedPlan: 0, deletedAt: null, }); const rows = yield* sql<{ readonly modelSelection: string | null; }>` SELECT model_selection_json AS "modelSelection" FROM projection_threads WHERE thread_id = 'thread-null-options' `; const row = rows[0]; if (!row) { return yield* Effect.die("Expected projection_threads row to exist."); } assert.strictEqual( row.modelSelection, // @effect-diagnostics-next-line preferSchemaOverJson:off JSON.stringify({ instanceId: ProviderInstanceId.make("claudeAgent"), model: "claude-opus-4-6", }), ); const persisted = yield* threads.getById({ threadId: ThreadId.make("thread-null-options"), }); assert.deepStrictEqual(Option.getOrNull(persisted)?.modelSelection, { instanceId: ProviderInstanceId.make("claudeAgent"), model: "claude-opus-4-6", }); }), ); it.effect("round-trips non-null settlement values through the thread row", () => Effect.gen(function* () { const threads = yield* ProjectionThreadRepository; yield* threads.upsert({ threadId: ThreadId.make("thread-settled"), projectId: ProjectId.make("project-1"), title: "Settled thread", modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4", }, runtimeMode: "full-access", interactionMode: "default", branch: null, worktreePath: null, latestTurnId: null, createdAt: "2026-03-24T00:00:00.000Z", updatedAt: "2026-03-25T00:00:00.000Z", archivedAt: null, settledOverride: "settled", settledAt: "2026-03-25T00:00:00.000Z", unsettledAt: null, snoozedUntil: "2026-03-26T09:00:00.000Z", snoozedAt: "2026-03-25T00:00:00.000Z", pinnedAt: "2026-03-25T00:00:00.000Z", latestUserMessageAt: null, pendingApprovalCount: 0, pendingUserInputCount: 0, hasActionableProposedPlan: 0, deletedAt: null, }); const persisted = yield* threads.getById({ threadId: ThreadId.make("thread-settled"), }); const row = Option.getOrNull(persisted); if (!row) { return yield* Effect.die("Expected settled projection_threads row to exist."); } assert.strictEqual(row.settledOverride, "settled"); assert.strictEqual(row.settledAt, "2026-03-25T00:00:00.000Z"); assert.strictEqual(row.snoozedUntil, "2026-03-26T09:00:00.000Z"); assert.strictEqual(row.snoozedAt, "2026-03-25T00:00:00.000Z"); assert.strictEqual(row.pinnedAt, "2026-03-25T00:00:00.000Z"); // Un-settle to the keep-active pin and wake the snooze; confirm the // flips persist. yield* threads.upsert({ ...row, settledOverride: "active", settledAt: null, unsettledAt: "2026-03-26T00:00:00.000Z", snoozedUntil: null, snoozedAt: null, pinnedAt: null, }); const repersisted = yield* threads.getById({ threadId: ThreadId.make("thread-settled"), }); const updated = Option.getOrNull(repersisted); assert.strictEqual(updated?.settledOverride, "active"); assert.strictEqual(updated?.settledAt, null); assert.strictEqual(updated?.unsettledAt, "2026-03-26T00:00:00.000Z"); assert.strictEqual(updated?.snoozedUntil, null); assert.strictEqual(updated?.snoozedAt, null); assert.strictEqual(updated?.pinnedAt, null); }), ); it.effect("round-trips manual and branch pull requests through the thread row", () => Effect.gen(function* () { const threads = yield* ProjectionThreadRepository; const linkedPullRequest = { projectId: ProjectId.make("project-linked-pr"), repository: "pingdotgg/t3code", number: 42, url: "https://github.com/pingdotgg/t3code/pull/42", }; const branchPullRequest = { ...linkedPullRequest, number: 43, url: "https://github.com/pingdotgg/t3code/pull/43", }; yield* threads.upsert({ threadId: ThreadId.make("thread-linked-pr"), projectId: ProjectId.make("project-linked-pr"), title: "Linked pull request", modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4", }, runtimeMode: "full-access", interactionMode: "default", branch: null, worktreePath: null, linkedPullRequest, branchPullRequest, latestTurnId: null,