88 TurnId ,
99 type OrchestrationEvent ,
1010} from "@t3tools/contracts" ;
11- import { Effect , Layer , ManagedRuntime , Queue , Stream } from "effect" ;
11+ import { Effect , Layer , ManagedRuntime , Option , Queue , Stream } from "effect" ;
1212import { describe , expect , it } from "vitest" ;
1313
1414import { PersistenceSqlError } from "../../persistence/Errors.ts" ;
@@ -21,11 +21,13 @@ import {
2121} from "../../persistence/Services/OrchestrationEventStore.ts" ;
2222import { OrchestrationEngineLive } from "./OrchestrationEngine.ts" ;
2323import { OrchestrationProjectionPipelineLive } from "./ProjectionPipeline.ts" ;
24+ import { OrchestrationProjectionSnapshotQueryLive } from "./ProjectionSnapshotQuery.ts" ;
2425import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts" ;
2526import {
2627 OrchestrationProjectionPipeline ,
2728 type OrchestrationProjectionPipelineShape ,
2829} from "../Services/ProjectionPipeline.ts" ;
30+ import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts" ;
2931import { ServerConfig } from "../../config.ts" ;
3032import * as NodeServices from "@effect/platform-node/NodeServices" ;
3133
@@ -39,6 +41,7 @@ async function createOrchestrationSystem() {
3941 prefix : "t3-orchestration-engine-test-" ,
4042 } ) ;
4143 const orchestrationLayer = OrchestrationEngineLive . pipe (
44+ Layer . provide ( OrchestrationProjectionSnapshotQueryLive ) ,
4245 Layer . provide ( OrchestrationProjectionPipelineLive ) ,
4346 Layer . provide ( OrchestrationEventStoreLive ) ,
4447 Layer . provide ( OrchestrationCommandReceiptRepositoryLive ) ,
@@ -60,6 +63,105 @@ function now() {
6063}
6164
6265describe ( "OrchestrationEngine" , ( ) => {
66+ it ( "bootstraps the in-memory read model from persisted projections" , async ( ) => {
67+ const failOnHistoricalReplayStore : OrchestrationEventStoreShape = {
68+ append : ( ) =>
69+ Effect . fail (
70+ new PersistenceSqlError ( {
71+ operation : "test.append" ,
72+ detail : "append should not be called during bootstrap" ,
73+ } ) ,
74+ ) ,
75+ readFromSequence : ( ) => Stream . empty ,
76+ readAll : ( ) =>
77+ Stream . fail (
78+ new PersistenceSqlError ( {
79+ operation : "test.readAll" ,
80+ detail : "historical replay should not be used during bootstrap" ,
81+ } ) ,
82+ ) ,
83+ } ;
84+
85+ const projectionSnapshot = {
86+ snapshotSequence : 7 ,
87+ updatedAt : "2026-03-03T00:00:04.000Z" ,
88+ projects : [
89+ {
90+ id : asProjectId ( "project-bootstrap" ) ,
91+ title : "Bootstrap Project" ,
92+ workspaceRoot : "/tmp/project-bootstrap" ,
93+ defaultModelSelection : {
94+ provider : "codex" as const ,
95+ model : "gpt-5-codex" ,
96+ } ,
97+ scripts : [ ] ,
98+ createdAt : "2026-03-03T00:00:00.000Z" ,
99+ updatedAt : "2026-03-03T00:00:01.000Z" ,
100+ deletedAt : null ,
101+ } ,
102+ ] ,
103+ threads : [
104+ {
105+ id : ThreadId . makeUnsafe ( "thread-bootstrap" ) ,
106+ projectId : asProjectId ( "project-bootstrap" ) ,
107+ title : "Bootstrap Thread" ,
108+ modelSelection : {
109+ provider : "codex" as const ,
110+ model : "gpt-5-codex" ,
111+ } ,
112+ interactionMode : DEFAULT_PROVIDER_INTERACTION_MODE ,
113+ runtimeMode : "full-access" as const ,
114+ branch : null ,
115+ worktreePath : null ,
116+ latestTurn : null ,
117+ createdAt : "2026-03-03T00:00:02.000Z" ,
118+ updatedAt : "2026-03-03T00:00:03.000Z" ,
119+ archivedAt : null ,
120+ deletedAt : null ,
121+ messages : [ ] ,
122+ proposedPlans : [ ] ,
123+ activities : [ ] ,
124+ checkpoints : [ ] ,
125+ session : null ,
126+ } ,
127+ ] ,
128+ } ;
129+
130+ const layer = OrchestrationEngineLive . pipe (
131+ Layer . provide (
132+ Layer . succeed ( ProjectionSnapshotQuery , {
133+ getSnapshot : ( ) => Effect . succeed ( projectionSnapshot ) ,
134+ getCounts : ( ) => Effect . succeed ( { projectCount : 1 , threadCount : 1 } ) ,
135+ getActiveProjectByWorkspaceRoot : ( ) => Effect . succeed ( Option . none ( ) ) ,
136+ getFirstActiveThreadIdByProjectId : ( ) => Effect . succeed ( Option . none ( ) ) ,
137+ getThreadCheckpointContext : ( ) => Effect . succeed ( Option . none ( ) ) ,
138+ } ) ,
139+ ) ,
140+ Layer . provide (
141+ Layer . succeed ( OrchestrationProjectionPipeline , {
142+ bootstrap : Effect . void ,
143+ projectEvent : ( ) => Effect . void ,
144+ } satisfies OrchestrationProjectionPipelineShape ) ,
145+ ) ,
146+ Layer . provide ( Layer . succeed ( OrchestrationEventStore , failOnHistoricalReplayStore ) ) ,
147+ Layer . provide ( OrchestrationCommandReceiptRepositoryLive ) ,
148+ Layer . provide ( SqlitePersistenceMemory ) ,
149+ ) ;
150+
151+ const runtime = ManagedRuntime . make ( layer ) ;
152+
153+ const engine = await runtime . runPromise ( Effect . service ( OrchestrationEngineService ) ) ;
154+ const readModel = await runtime . runPromise ( engine . getReadModel ( ) ) ;
155+
156+ expect ( readModel . snapshotSequence ) . toBe ( 7 ) ;
157+ expect ( readModel . projects ) . toHaveLength ( 1 ) ;
158+ expect ( readModel . projects [ 0 ] ?. title ) . toBe ( "Bootstrap Project" ) ;
159+ expect ( readModel . threads ) . toHaveLength ( 1 ) ;
160+ expect ( readModel . threads [ 0 ] ?. title ) . toBe ( "Bootstrap Thread" ) ;
161+
162+ await runtime . dispose ( ) ;
163+ } ) ;
164+
63165 it ( "returns deterministic read models for repeated reads" , async ( ) => {
64166 const createdAt = now ( ) ;
65167 const system = await createOrchestrationSystem ( ) ;
@@ -417,6 +519,7 @@ describe("OrchestrationEngine", () => {
417519
418520 const runtime = ManagedRuntime . make (
419521 OrchestrationEngineLive . pipe (
522+ Layer . provide ( OrchestrationProjectionSnapshotQueryLive ) ,
420523 Layer . provide ( OrchestrationProjectionPipelineLive ) ,
421524 Layer . provide ( Layer . succeed ( OrchestrationEventStore , flakyStore ) ) ,
422525 Layer . provide ( OrchestrationCommandReceiptRepositoryLive ) ,
@@ -512,6 +615,7 @@ describe("OrchestrationEngine", () => {
512615
513616 const runtime = ManagedRuntime . make (
514617 OrchestrationEngineLive . pipe (
618+ Layer . provide ( OrchestrationProjectionSnapshotQueryLive ) ,
515619 Layer . provide ( Layer . succeed ( OrchestrationProjectionPipeline , flakyProjectionPipeline ) ) ,
516620 Layer . provide ( OrchestrationEventStoreLive ) ,
517621 Layer . provide ( OrchestrationCommandReceiptRepositoryLive ) ,
@@ -653,6 +757,7 @@ describe("OrchestrationEngine", () => {
653757
654758 const runtime = ManagedRuntime . make (
655759 OrchestrationEngineLive . pipe (
760+ Layer . provide ( OrchestrationProjectionSnapshotQueryLive ) ,
656761 Layer . provide ( Layer . succeed ( OrchestrationProjectionPipeline , flakyProjectionPipeline ) ) ,
657762 Layer . provide ( Layer . succeed ( OrchestrationEventStore , nonTransactionalStore ) ) ,
658763 Layer . provide ( OrchestrationCommandReceiptRepositoryLive ) ,
0 commit comments