authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-26 00:18:11-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-26 13:20:36-08:00
log9fae3ee0e95b036388b597558ee58617b0035494
tree1c308d19081f13418191a0188d75f5186559e63a
parenta328f66bfa9f09f2748a5960ed65546dbd8da9c9

look over effect services


44 files changed, 765 insertions(+), 280 deletions(-)

.plans/16c-pr89-remediation-checklist.md+6-6
......@@ -210,8 +210,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6`
210210 - Threads: PRRT_kwDORLtfbc5wiLhQ
211211 - Audit note: Snapshot sequence can under-report due to extra projectors, but replay impact is lower now.
212212
213- [ ] `C033` Three error classes defined but never instantiated anywhere
214 - Status: `TODO`
213- [x] `C033` Three error classes defined but never instantiated anywhere
214 - Status: `DONE`
215215 - Verdict: `partially-valid`
216216 - Severity: `Low`
217217 - Area: `Checkpointing correctness`
......@@ -219,8 +219,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6`
219219 - Threads: PRRT_kwDORLtfbc5wlYgo
220220 - Audit note: Original claim overstated; some errors used, others appear unused.
221221
222- [ ] `C034` Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type
223 - Status: `TODO`
222- [x] `C034` Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type
223 - Status: `DONE`
224224 - Verdict: `valid`
225225 - Severity: `Low`
226226 - Area: `Checkpointing correctness`
......@@ -228,8 +228,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6`
228228 - Threads: PRRT_kwDORLtfbc5wj5fn
229229 - Audit note: CheckpointInvariantError remains redundantly included in service union.
230230
231- [ ] `C035` Redundant error type in CheckpointServiceError union definition
232 - Status: `TODO`
231- [x] `C035` Redundant error type in CheckpointServiceError union definition
232 - Status: `DONE`
233233 - Verdict: `valid`
234234 - Severity: `Low`
235235 - Area: `Checkpointing correctness`
apps/server/src/checkpointing/Errors.ts+2-38
......@@ -19,37 +19,6 @@ export class CheckpointUnavailableError extends Schema.TaggedErrorClass<Checkpoi
1919 }
2020}
2121
22/**
23 * CheckpointRepositoryError - Checkpointing unavailable for cwd/repository.
24 */
25export class CheckpointRepositoryError extends Schema.TaggedErrorClass<CheckpointRepositoryError>()(
26 "CheckpointRepositoryError",
27 {
28 cwd: Schema.String,
29 detail: Schema.String,
30 cause: Schema.optional(Schema.Defect),
31 },
32) {
33 override get message(): string {
34 return `Checkpoint repository error for ${this.cwd}: ${this.detail}`;
35 }
36}
37
38/**
39 * CheckpointSessionNotFoundError - Session is missing in checkpoint workflow.
40 */
41export class CheckpointSessionNotFoundError extends Schema.TaggedErrorClass<CheckpointSessionNotFoundError>()(
42 "CheckpointSessionNotFoundError",
43 {
44 sessionId: Schema.String,
45 cause: Schema.optional(Schema.Defect),
46 },
47) {
48 override get message(): string {
49 return `Checkpoint session not found: ${this.sessionId}`;
50 }
51}
52
5322/**
5423 * CheckpointInvariantError - Inconsistent provider/filesystem/catalog state.
5524 */
......@@ -69,11 +38,6 @@ export class CheckpointInvariantError extends Schema.TaggedErrorClass<Checkpoint
6938export type CheckpointStoreError =
7039 | GitCommandError
7140 | CheckpointInvariantError
72 | CheckpointUnavailableError
73 | CheckpointRepositoryError;
41 | CheckpointUnavailableError;
7442
75export type CheckpointServiceError =
76 | CheckpointStoreError
77 | ProjectionRepositoryError
78 | CheckpointSessionNotFoundError
79 | CheckpointInvariantError;
43export type CheckpointServiceError = CheckpointStoreError | ProjectionRepositoryError;
apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts-1
......@@ -1,6 +1,5 @@
11import {
22 OrchestrationGetTurnDiffResult,
3 type ProjectId,
43 type OrchestrationGetFullThreadDiffInput,
54 type OrchestrationGetFullThreadDiffResult,
65 type OrchestrationGetTurnDiffResult as OrchestrationGetTurnDiffResultType,
apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts+25
......@@ -1,3 +1,11 @@
1/**
2 * CheckpointDiffQuery - Query interface for computed checkpoint diffs.
3 *
4 * Provides read-only diff operations across checkpoint snapshots used by
5 * orchestration APIs.
6 *
7 * @module CheckpointDiffQuery
8 */
19import type {
210 OrchestrationGetFullThreadDiffInput,
311 OrchestrationGetFullThreadDiffResult,
......@@ -9,15 +17,32 @@ import type { Effect } from "effect";
917
1018import type { CheckpointServiceError } from "../Errors.ts";
1119
20/**
21 * CheckpointDiffQueryShape - Service API for checkpoint diff queries.
22 */
1223export interface CheckpointDiffQueryShape {
24 /**
25 * Read the patch diff for a single turn checkpoint transition.
26 *
27 * Verifies checkpoint availability in both projection state and filesystem.
28 */
1329 readonly getTurnDiff: (
1430 input: OrchestrationGetTurnDiffInput,
1531 ) => Effect.Effect<OrchestrationGetTurnDiffResult, CheckpointServiceError>;
32
33 /**
34 * Read the full patch diff across a thread range of checkpoints.
35 *
36 * Delegates to turn diff with `fromTurnCount = 0`.
37 */
1638 readonly getFullThreadDiff: (
1739 input: OrchestrationGetFullThreadDiffInput,
1840 ) => Effect.Effect<OrchestrationGetFullThreadDiffResult, CheckpointServiceError>;
1941}
2042
43/**
44 * CheckpointDiffQuery - Service tag for checkpoint diff queries.
45 */
2146export class CheckpointDiffQuery extends ServiceMap.Service<
2247 CheckpointDiffQuery,
2348 CheckpointDiffQueryShape
apps/server/src/checkpointing/Services/CheckpointStore.ts+11
......@@ -39,6 +39,9 @@ export interface DeleteCheckpointRefsInput {
3939 readonly checkpointRefs: ReadonlyArray<CheckpointRef>;
4040}
4141
42/**
43 * CheckpointStoreShape - Service API for checkpoint capture/restore and diff access.
44 */
4245export interface CheckpointStoreShape {
4346 /**
4447 * Check whether cwd is inside a Git worktree.
......@@ -47,6 +50,8 @@ export interface CheckpointStoreShape {
4750
4851 /**
4952 * Capture a checkpoint commit and store it at the provided checkpoint ref.
53 *
54 * Uses an isolated temporary Git index and writes a hidden ref.
5055 */
5156 readonly captureCheckpoint: (
5257 input: CaptureCheckpointInput,
......@@ -61,6 +66,8 @@ export interface CheckpointStoreShape {
6166
6267 /**
6368 * Restore workspace/staging state to a checkpoint.
69 *
70 * Optionally falls back to current `HEAD` when the checkpoint ref is missing.
6471 */
6572 readonly restoreCheckpoint: (
6673 input: RestoreCheckpointInput,
......@@ -68,6 +75,8 @@ export interface CheckpointStoreShape {
6875
6976 /**
7077 * Compute patch diff between two checkpoint refs.
78 *
79 * Can optionally treat missing "from" ref as `HEAD`.
7180 */
7281 readonly diffCheckpoints: (
7382 input: DiffCheckpointsInput,
......@@ -75,6 +84,8 @@ export interface CheckpointStoreShape {
7584
7685 /**
7786 * Delete the provided checkpoint refs.
87 *
88 * Best-effort delete: missing refs are tolerated.
7889 */
7990 readonly deleteCheckpointRefs: (
8091 input: DeleteCheckpointRefsInput,
apps/server/src/config.ts+25
......@@ -1,3 +1,11 @@
1/**
2 * ServerConfig and NetService - Runtime configuration services.
3 *
4 * Defines process-level server configuration and networking helpers used by
5 * startup and runtime layers.
6 *
7 * @module ServerConfig
8 */
19import * as Net from "node:net";
210import { Data, Effect, FileSystem, Layer, Path, ServiceMap } from "effect";
311
......@@ -5,6 +13,9 @@ export const DEFAULT_PORT = 3773;
513
614export type RuntimeMode = "web" | "desktop";
715
16/**
17 * ServerConfigShape - Process/runtime configuration required by the server.
18 */
819export interface ServerConfigShape {
920 readonly mode: RuntimeMode;
1021 readonly port: number;
......@@ -20,6 +31,9 @@ export interface ServerConfigShape {
2031 readonly logWebSocketEvents: boolean;
2132}
2233
34/**
35 * ServerConfig - Service tag for server runtime configuration.
36 */
2337export class ServerConfig extends ServiceMap.Service<ServerConfig, ServerConfigShape>()(
2438 "server/ServerConfig",
2539) {}
......@@ -31,10 +45,21 @@ export class NetError extends Data.TaggedError("NetError")<{
3145 readonly cause?: unknown;
3246}> {}
3347
48/**
49 * NetServiceShape - Networking helper operations used during startup.
50 */
3451export interface NetServiceShape {
52 /**
53 * Resolve an available listening port, preferring the provided port first.
54 *
55 * Falls back to an ephemeral OS-assigned port when the preferred port is in use.
56 */
3557 readonly findAvailablePort: (preferred: number) => Effect.Effect<number, NetError>;
3658}
3759
60/**
61 * NetService - Service tag for startup networking helpers.
62 */
3863export class NetService extends ServiceMap.Service<NetService, NetServiceShape>()(
3964 "server/NetService",
4065) {
apps/server/src/git/Errors.ts+3-18
......@@ -57,26 +57,11 @@ export class GitManagerError extends Schema.TaggedErrorClass<GitManagerError>()(
5757 }
5858}
5959
60/**
61 * GitCoreError - Errors emitted by low-level Git repository operations.
62 */
63export type GitCoreError = GitCommandError;
64
65/**
66 * GitHubCliServiceError - Errors emitted by GitHub CLI service helpers.
67 */
68export type GitHubCliServiceError = GitHubCliError;
69
70/**
71 * TextGenerationServiceError - Errors emitted by commit/PR text generation.
72 */
73export type TextGenerationServiceError = TextGenerationError;
74
7560/**
7661 * GitManagerServiceError - Errors emitted by stacked Git workflow orchestration.
7762 */
7863export type GitManagerServiceError =
7964 | GitManagerError
80 | GitCoreError
81 | GitHubCliServiceError
82 | TextGenerationServiceError;
65 | GitCommandError
66 | GitHubCliError
67 | TextGenerationError;
apps/server/src/git/Services/GitService.ts+3
......@@ -27,6 +27,9 @@ export interface ExecuteGitResult {
2727 readonly stderr: string;
2828}
2929
30/**
31 * GitServiceShape - Service API for Git command execution.
32 */
3033export interface GitServiceShape {
3134 /**
3235 * Execute a Git command.
apps/server/src/keybindings.ts+26
......@@ -1,3 +1,11 @@
1/**
2 * Keybindings - Keybinding configuration service definitions.
3 *
4 * Owns parsing, validation, merge, and persistence of user keybinding
5 * configuration consumed by the server runtime.
6 *
7 * @module Keybindings
8 */
19import {
210 KeybindingRule,
311 KeybindingsConfig,
......@@ -388,16 +396,34 @@ function mergeWithDefaultKeybindings(custom: ResolvedKeybindingsConfig): Resolve
388396 return merged.slice(-MAX_KEYBINDINGS_COUNT);
389397}
390398
399/**
400 * KeybindingsShape - Service API for keybinding configuration operations.
401 */
391402export interface KeybindingsShape {
403 /**
404 * Load and resolve keybindings from disk merged with defaults.
405 *
406 * Returns an in-memory cached result after the first successful load.
407 */
392408 readonly loadResolvedKeybindingsConfig: Effect.Effect<
393409 readonly ResolvedKeybindingRule[],
394410 KeybindingsConfigError
395411 >;
412
413 /**
414 * Upsert a keybinding rule and persist the resulting configuration.
415 *
416 * Writes config atomically and enforces the max rule count by truncating
417 * oldest entries when needed.
418 */
396419 readonly upsertKeybindingRule: (
397420 rule: KeybindingRule,
398421 ) => Effect.Effect<ResolvedKeybindingsConfig, KeybindingsConfigError>;
399422}
400423
424/**
425 * Keybindings - Service tag for keybinding configuration operations.
426 */
401427export class Keybindings extends ServiceMap.Service<Keybindings, KeybindingsShape>()(
402428 "server/Keybindings",
403429) {}
apps/server/src/main.ts+25
......@@ -1,3 +1,11 @@
1/**
2 * CliConfig - CLI/runtime bootstrap service definitions.
3 *
4 * Defines startup-only service contracts used while resolving process config
5 * and constructing server runtime layers.
6 *
7 * @module CliConfig
8 */
19import { Config, Data, Effect, FileSystem, Layer, Option, Path, Schema, ServiceMap } from "effect";
210import { Command, Flag } from "effect/unstable/cli";
311
......@@ -32,12 +40,29 @@ interface CliInput {
3240 readonly logWebSocketEvents: Option.Option<boolean>;
3341}
3442
43/**
44 * CliConfigShape - Startup helpers required while building server layers.
45 */
3546export interface CliConfigShape {
47 /**
48 * Current process working directory.
49 */
3650 readonly cwd: string;
51
52 /**
53 * Apply OS-specific PATH normalization.
54 */
3755 readonly fixPath: Effect.Effect<void>;
56
57 /**
58 * Resolve static web asset directory for server mode.
59 */
3860 readonly resolveStaticDir: Effect.Effect<string | undefined>;
3961}
4062
63/**
64 * CliConfig - Service tag for startup CLI/runtime helpers.
65 */
4166export class CliConfig extends ServiceMap.Service<CliConfig, CliConfigShape>()("server/CliConfig") {
4267 static readonly layer = Layer.effect(
4368 CliConfig,
apps/server/src/open.ts+23
......@@ -1,3 +1,11 @@
1/**
2 * Open - Browser/editor launch service interface.
3 *
4 * Owns process launch helpers for opening URLs in a browser and workspace
5 * paths in a configured editor.
6 *
7 * @module Open
8 */
19import { spawn } from "node:child_process";
210
311import { EDITORS, type EditorId } from "@t3tools/contracts";
......@@ -22,11 +30,26 @@ interface EditorLaunch {
2230 readonly args: ReadonlyArray<string>;
2331}
2432
33/**
34 * OpenShape - Service API for browser and editor launch actions.
35 */
2536export interface OpenShape {
37 /**
38 * Open a URL target in the default browser.
39 */
2640 readonly openBrowser: (target: string) => Effect.Effect<void, OpenError>;
41
42 /**
43 * Open a workspace path in a selected editor integration.
44 *
45 * Launches the editor as a detached process so server startup is not blocked.
46 */
2747 readonly openInEditor: (input: OpenInEditorInput) => Effect.Effect<void, OpenError>;
2848}
2949
50/**
51 * Open - Service tag for browser/editor launch operations.
52 */
3053export class Open extends ServiceMap.Service<Open, OpenShape>()("server/Open") {}
3154
3255// ==============================
apps/server/src/orchestration/Errors.ts-4
......@@ -1,8 +1,6 @@
11import { SchemaIssue, Schema } from "effect";
22
33import type {
4 OrchestrationCommandReceiptRepositoryError,
5 OrchestrationEventStoreError,
64 ProjectionRepositoryError,
75} from "../persistence/Errors.ts";
86
......@@ -83,8 +81,6 @@ export class OrchestrationListenerCallbackError extends Schema.TaggedErrorClass<
8381}
8482
8583export type OrchestrationDispatchError =
86 | OrchestrationEventStoreError
87 | OrchestrationCommandReceiptRepositoryError
8884 | ProjectionRepositoryError
8985 | OrchestrationCommandInvariantError
9086 | OrchestrationCommandPreviouslyRejectedError
apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts+7-17
......@@ -14,9 +14,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient";
1414import * as SqlSchema from "effect/unstable/sql/SqlSchema";
1515
1616import {
17 PersistenceDecodeError,
18 PersistenceSerializationError,
19 PersistenceSqlError,
17 isPersistenceError,
2018 toPersistenceDecodeError,
2119 toPersistenceSqlError,
2220 type ProjectionRepositoryError,
......@@ -106,19 +104,6 @@ function toPersistenceSqlOrDecodeError(sqlOperation: string, decodeOperation: st
106104 : toPersistenceSqlError(sqlOperation)(cause);
107105}
108106
109function toProjectionRepositoryError(operation: string) {
110 return (cause: unknown): ProjectionRepositoryError => {
111 if (
112 cause instanceof PersistenceSqlError ||
113 cause instanceof PersistenceDecodeError ||
114 cause instanceof PersistenceSerializationError
115 ) {
116 return cause;
117 }
118 return toPersistenceSqlError(operation)(cause);
119 };
120}
121
122107const makeProjectionSnapshotQuery = Effect.gen(function* () {
123108 const sql = yield* SqlClient.SqlClient;
124109
......@@ -441,7 +426,12 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
441426 }),
442427 )
443428 .pipe(
444 Effect.mapError(toProjectionRepositoryError("ProjectionSnapshotQuery.getSnapshot:query")),
429 Effect.mapError((error) => {
430 if (isPersistenceError(error)) {
431 return error;
432 }
433 return toPersistenceSqlError("ProjectionSnapshotQuery.getSnapshot:query")(error);
434 }),
445435 );
446436
447437 return {
apps/server/src/orchestration/Services/CheckpointReactor.ts+17
......@@ -1,16 +1,33 @@
1/**
2 * CheckpointReactor - Checkpoint reaction service interface.
3 *
4 * Owns background workers that react to orchestration checkpoint lifecycle
5 * events and apply checkpoint side effects.
6 *
7 * @module CheckpointReactor
8 */
19import { ServiceMap } from "effect";
210import type { Effect, Scope } from "effect";
311
12/**
13 * CheckpointReactorShape - Service API for checkpoint reactor lifecycle.
14 */
415export interface CheckpointReactorShape {
516 /**
617 * Start the checkpoint reactor.
718 *
819 * The returned effect must be run in a scope so all worker fibers can be
920 * finalized on shutdown.
21 *
22 * Consumes both orchestration-domain and provider-runtime events via an
23 * internal queue.
1024 */
1125 readonly start: Effect.Effect<void, never, Scope.Scope>;
1226}
1327
28/**
29 * CheckpointReactor - Service tag for checkpoint reactor workers.
30 */
1431export class CheckpointReactor extends ServiceMap.Service<
1532 CheckpointReactor,
1633 CheckpointReactorShape
apps/server/src/orchestration/Services/OrchestrationEngine.ts+8
......@@ -21,6 +21,9 @@ import type { Effect, Stream } from "effect";
2121import type { OrchestrationDispatchError } from "../Errors.ts";
2222import type { OrchestrationEventStoreError } from "../../persistence/Errors.ts";
2323
24/**
25 * OrchestrationEngineShape - Service API for orchestration command and event flow.
26 */
2427export interface OrchestrationEngineShape {
2528 /**
2629 * Read the current in-memory orchestration read model.
......@@ -44,6 +47,9 @@ export interface OrchestrationEngineShape {
4447 *
4548 * @param command - Valid orchestration command.
4649 * @returns Effect containing the sequence of the persisted event.
50 *
51 * Dispatch is serialized through an internal queue and deduplicated via
52 * command receipts.
4753 */
4854 readonly dispatch: (
4955 command: OrchestrationCommand,
......@@ -51,6 +57,8 @@ export interface OrchestrationEngineShape {
5157
5258 /**
5359 * Stream persisted domain events in dispatch order.
60 *
61 * This is a hot runtime stream (new events only), not a historical replay.
5462 */
5563 readonly streamDomainEvents: Stream.Stream<OrchestrationEvent>;
5664}
apps/server/src/orchestration/Services/OrchestrationReactor.ts+14
......@@ -1,6 +1,17 @@
1/**
2 * OrchestrationReactor - Composite orchestration reactor service interface.
3 *
4 * Coordinates startup of orchestration runtime reactors that translate domain
5 * events into downstream side effects.
6 *
7 * @module OrchestrationReactor
8 */
19import { ServiceMap } from "effect";
210import type { Effect, Scope } from "effect";
311
12/**
13 * OrchestrationReactorShape - Service API for orchestration reactor lifecycle.
14 */
415export interface OrchestrationReactorShape {
516 /**
617 * Start orchestration-side reactors for provider/runtime/checkpoint flows.
......@@ -11,6 +22,9 @@ export interface OrchestrationReactorShape {
1122 readonly start: Effect.Effect<void, never, Scope.Scope>;
1223}
1324
25/**
26 * OrchestrationReactor - Service tag for orchestration reactor coordination.
27 */
1428export class OrchestrationReactor extends ServiceMap.Service<
1529 OrchestrationReactor,
1630 OrchestrationReactorShape
apps/server/src/orchestration/Services/ProjectionPipeline.ts+26-9
......@@ -1,24 +1,41 @@
1/**
2 * OrchestrationProjectionPipeline - Event projection pipeline service interface.
3 *
4 * Coordinates projection bootstrap/replay and per-event projection updates for
5 * orchestration read models.
6 *
7 * @module OrchestrationProjectionPipeline
8 */
19import type { OrchestrationEvent } from "@t3tools/contracts";
210import { ServiceMap } from "effect";
311import type { Effect } from "effect";
412
5import type {
6 OrchestrationEventStoreError,
7 ProjectionRepositoryError,
8} from "../../persistence/Errors.ts";
9
10export type OrchestrationProjectionPipelineError =
11 | OrchestrationEventStoreError
12 | ProjectionRepositoryError;
13import type { ProjectionRepositoryError } from "../../persistence/Errors.ts";
1314
15/**
16 * OrchestrationProjectionPipelineShape - Service API for projection execution.
17 */
1418export interface OrchestrationProjectionPipelineShape {
15 readonly bootstrap: Effect.Effect<void, OrchestrationProjectionPipelineError>;
19 /**
20 * Bootstrap projections by replaying persisted events.
21 *
22 * Resumes each projector from its stored projection-state cursor.
23 */
24 readonly bootstrap: Effect.Effect<void, ProjectionRepositoryError>;
1625
26 /**
27 * Project a single orchestration event into projection repositories.
28 *
29 * Projectors are executed sequentially to preserve deterministic ordering.
30 */
1731 readonly projectEvent: (
1832 event: OrchestrationEvent,
1933 ) => Effect.Effect<void, ProjectionRepositoryError>;
2034}
2135
36/**
37 * OrchestrationProjectionPipeline - Service tag for orchestration projections.
38 */
2239export class OrchestrationProjectionPipeline extends ServiceMap.Service<
2340 OrchestrationProjectionPipeline,
2441 OrchestrationProjectionPipelineShape
apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts+20
......@@ -1,13 +1,33 @@
1/**
2 * ProjectionSnapshotQuery - Read-model snapshot query service interface.
3 *
4 * Exposes the current orchestration projection snapshot for read-only API
5 * access.
6 *
7 * @module ProjectionSnapshotQuery
8 */
19import type { OrchestrationReadModel } from "@t3tools/contracts";
210import { ServiceMap } from "effect";
311import type { Effect } from "effect";
412
513import type { ProjectionRepositoryError } from "../../persistence/Errors.ts";
614
15/**
16 * ProjectionSnapshotQueryShape - Service API for read-model snapshots.
17 */
718export interface ProjectionSnapshotQueryShape {
19 /**
20 * Read the latest orchestration projection snapshot.
21 *
22 * Rehydrates from projection tables and derives snapshot sequence from
23 * projector cursor state.
24 */
825 readonly getSnapshot: () => Effect.Effect<OrchestrationReadModel, ProjectionRepositoryError>;
926}
1027
28/**
29 * ProjectionSnapshotQuery - Service tag for projection snapshot queries.
30 */
1131export class ProjectionSnapshotQuery extends ServiceMap.Service<
1232 ProjectionSnapshotQuery,
1333 ProjectionSnapshotQueryShape
apps/server/src/orchestration/Services/ProviderCommandReactor.ts+17
......@@ -1,16 +1,33 @@
1/**
2 * ProviderCommandReactor - Provider command reaction service interface.
3 *
4 * Owns background workers that react to orchestration intent events and
5 * dispatch provider-side command execution.
6 *
7 * @module ProviderCommandReactor
8 */
19import { ServiceMap } from "effect";
210import type { Effect, Scope } from "effect";
311
12/**
13 * ProviderCommandReactorShape - Service API for provider command reactors.
14 */
415export interface ProviderCommandReactorShape {
516 /**
617 * Start reacting to provider-intent orchestration domain events.
718 *
819 * The returned effect must be run in a scope so all worker fibers can be
920 * finalized on shutdown.
21 *
22 * Filters orchestration domain events to provider-intent types before
23 * processing.
1024 */
1125 readonly start: Effect.Effect<void, never, Scope.Scope>;
1226}
1327
28/**
29 * ProviderCommandReactor - Service tag for provider command reaction workers.
30 */
1431export class ProviderCommandReactor extends ServiceMap.Service<
1532 ProviderCommandReactor,
1633 ProviderCommandReactorShape
apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts+17
......@@ -1,16 +1,33 @@
1/**
2 * ProviderRuntimeIngestionService - Provider runtime ingestion service interface.
3 *
4 * Owns background workers that consume provider runtime streams and emit
5 * orchestration commands/events.
6 *
7 * @module ProviderRuntimeIngestionService
8 */
19import { ServiceMap } from "effect";
210import type { Effect, Scope } from "effect";
311
12/**
13 * ProviderRuntimeIngestionShape - Service API for runtime ingestion lifecycle.
14 */
415export interface ProviderRuntimeIngestionShape {
516 /**
617 * Start ingesting provider runtime events into orchestration commands.
718 *
819 * The returned effect must be run in a scope so all worker fibers can be
920 * finalized on shutdown.
21 *
22 * Uses an internal queue and continues after non-interrupt failures by
23 * logging warnings.
1024 */
1125 readonly start: Effect.Effect<void, never, Scope.Scope>;
1226}
1327
28/**
29 * ProviderRuntimeIngestionService - Service tag for runtime ingestion workers.
30 */
1431export class ProviderRuntimeIngestionService extends ServiceMap.Service<
1532 ProviderRuntimeIngestionService,
1633 ProviderRuntimeIngestionShape
apps/server/src/persistence/Errors.ts+5-116
......@@ -30,19 +30,6 @@ export class PersistenceDecodeError extends Schema.TaggedErrorClass<PersistenceD
3030 }
3131}
3232
33export class PersistenceSerializationError extends Schema.TaggedErrorClass<PersistenceSerializationError>()(
34 "PersistenceSerializationError",
35 {
36 operation: Schema.String,
37 detail: Schema.String,
38 cause: Schema.optional(Schema.Defect),
39 },
40) {
41 override get message(): string {
42 return `Serialization error in ${this.operation}: ${this.detail}`;
43 }
44}
45
4633export function toPersistenceSqlError(operation: string) {
4734 return (cause: unknown): PersistenceSqlError =>
4835 new PersistenceSqlError({
......@@ -70,85 +57,8 @@ export function toPersistenceDecodeCauseError(operation: string) {
7057 });
7158}
7259
73export function toPersistenceSerializationError(operation: string) {
74 return (cause: unknown): PersistenceSerializationError =>
75 new PersistenceSerializationError({
76 operation,
77 detail: `Failed to execute ${operation}`,
78 cause,
79 });
80}
81
82// ===============================
83// Project Repository Errors
84// ===============================
85
86export class ProjectValidationError extends Schema.TaggedErrorClass<ProjectValidationError>()(
87 "ProjectValidationError",
88 {
89 operation: Schema.String,
90 issue: Schema.String,
91 cause: Schema.optional(Schema.Defect),
92 },
93) {
94 override get message(): string {
95 return `Project validation failed in ${this.operation}: ${this.issue}`;
96 }
97}
98
99export class ProjectPathMissingError extends Schema.TaggedErrorClass<ProjectPathMissingError>()(
100 "ProjectPathMissingError",
101 {
102 cwd: Schema.String,
103 cause: Schema.optional(Schema.Defect),
104 },
105) {
106 override get message(): string {
107 return `Project path does not exist: ${this.cwd}`;
108 }
109}
110
111export class ProjectNotFoundError extends Schema.TaggedErrorClass<ProjectNotFoundError>()(
112 "ProjectNotFoundError",
113 {
114 projectId: Schema.String,
115 cause: Schema.optional(Schema.Defect),
116 },
117) {
118 override get message(): string {
119 return `Project not found: ${this.projectId}`;
120 }
121}
122
123// ===============================
124// Checkpoint Repository Errors
125// ===============================
126
127export class CheckpointRepositoryValidationError extends Schema.TaggedErrorClass<CheckpointRepositoryValidationError>()(
128 "CheckpointRepositoryValidationError",
129 {
130 operation: Schema.String,
131 issue: Schema.String,
132 cause: Schema.optional(Schema.Defect),
133 },
134) {
135 override get message(): string {
136 return `Checkpoint repository validation failed in ${this.operation}: ${this.issue}`;
137 }
138}
139
140export class CheckpointRepositoryPersistenceError extends Schema.TaggedErrorClass<CheckpointRepositoryPersistenceError>()(
141 "CheckpointRepositoryPersistenceError",
142 {
143 operation: Schema.String,
144 detail: Schema.String,
145 cause: Schema.optional(Schema.Defect),
146 },
147) {
148 override get message(): string {
149 return `Checkpoint repository persistence error in ${this.operation}: ${this.detail}`;
150 }
151}
60export const isPersistenceError = (u: unknown) =>
61 Schema.is(PersistenceSqlError)(u) || Schema.is(PersistenceDecodeError)(u);
15262
15363// ===============================
15464// Provider Session Repository Errors
......@@ -180,22 +90,7 @@ export class ProviderSessionRepositoryPersistenceError extends Schema.TaggedErro
18090 }
18191}
18292
183export type ProjectRepositoryError =
184 | PersistenceSqlError
185 | PersistenceDecodeError
186 | PersistenceSerializationError
187 | ProjectValidationError
188 | ProjectPathMissingError
189 | ProjectNotFoundError;
190
191export type OrchestrationEventStoreError =
192 | PersistenceSqlError
193 | PersistenceDecodeError
194 | PersistenceSerializationError;
195
196export type CheckpointRepositoryError =
197 | CheckpointRepositoryValidationError
198 | CheckpointRepositoryPersistenceError;
93export type OrchestrationEventStoreError = PersistenceSqlError | PersistenceDecodeError;
19994
20095export type ProviderSessionRepositoryError =
20196 | ProviderSessionRepositoryValidationError
......@@ -205,12 +100,6 @@ export type OrchestrationCommandReceiptRepositoryError =
205100 | PersistenceSqlError
206101 | PersistenceDecodeError;
207102
208export type ProviderSessionRuntimeRepositoryError =
209 | PersistenceSqlError
210 | PersistenceDecodeError
211 | PersistenceSerializationError;
103export type ProviderSessionRuntimeRepositoryError = PersistenceSqlError | PersistenceDecodeError;
212104
213export type ProjectionRepositoryError =
214 | PersistenceSqlError
215 | PersistenceDecodeError
216 | PersistenceSerializationError;
105export type ProjectionRepositoryError = PersistenceSqlError | PersistenceDecodeError;
apps/server/src/persistence/NodeSqliteClient.ts+5
......@@ -1,6 +1,8 @@
11/**
22 * Port of `@effect/sql-sqlite-node` that uses the native `node:sqlite`
33 * bindings instead of `better-sqlite3`.
4 *
5 * @module SqliteClient
46 */
57import { DatabaseSync, type StatementSync } from "node:sqlite";
68
......@@ -27,6 +29,9 @@ export const TypeId: TypeId = "~local/sqlite-node/SqliteClient";
2729
2830export type TypeId = "~local/sqlite-node/SqliteClient";
2931
32/**
33 * SqliteClient - Effect service tag for the sqlite SQL client.
34 */
3035export const SqliteClient = ServiceMap.Service<Client.SqlClient>("@local/sqlite-node/SqliteClient");
3136
3237export interface SqliteClientConfig {
apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts+22
......@@ -1,3 +1,11 @@
1/**
2 * OrchestrationCommandReceiptRepository - Repository interface for command receipts.
3 *
4 * Owns persistence operations for deduplication and status tracking of
5 * orchestration command handling.
6 *
7 * @module OrchestrationCommandReceiptRepository
8 */
19import {
210 CommandId,
311 IsoDateTime,
......@@ -28,11 +36,22 @@ export const GetByCommandIdInput = Schema.Struct({
2836});
2937export type GetByCommandIdInput = typeof GetByCommandIdInput.Type;
3038
39/**
40 * OrchestrationCommandReceiptRepositoryShape - Service API for command receipts.
41 */
3142export interface OrchestrationCommandReceiptRepositoryShape {
43 /**
44 * Insert or replace a command receipt row.
45 *
46 * Upserts by `commandId` for idempotent command-result tracking.
47 */
3248 readonly upsert: (
3349 receipt: OrchestrationCommandReceipt,
3450 ) => Effect.Effect<void, OrchestrationCommandReceiptRepositoryError>;
3551
52 /**
53 * Read a command receipt by command id.
54 */
3655 readonly getByCommandId: (
3756 input: GetByCommandIdInput,
3857 ) => Effect.Effect<
......@@ -41,6 +60,9 @@ export interface OrchestrationCommandReceiptRepositoryShape {
4160 >;
4261}
4362
63/**
64 * OrchestrationCommandReceiptRepository - Service tag for command receipt persistence.
65 */
4466export class OrchestrationCommandReceiptRepository extends ServiceMap.Service<
4567 OrchestrationCommandReceiptRepository,
4668 OrchestrationCommandReceiptRepositoryShape
apps/server/src/persistence/Services/OrchestrationEventStore.ts+7
......@@ -15,12 +15,17 @@ import type { Effect, Stream } from "effect";
1515
1616import type { OrchestrationEventStoreError } from "../Errors.ts";
1717
18/**
19 * OrchestrationEventStoreShape - Service API for orchestration event persistence.
20 */
1821export interface OrchestrationEventStoreShape {
1922 /**
2023 * Persist a new orchestration event.
2124 *
2225 * @param event - Event payload without sequence (assigned by storage).
2326 * @returns Effect containing the stored event with assigned sequence.
27 *
28 * Actor kind is inferred from command/metadata before persistence.
2429 */
2530 readonly append: (
2631 event: Omit<OrchestrationEvent, "sequence">,
......@@ -32,6 +37,8 @@ export interface OrchestrationEventStoreShape {
3237 * @param sequenceExclusive - Sequence cursor (exclusive).
3338 * @param limit - Maximum number of events to emit.
3439 * @returns Stream containing ordered events.
40 *
41 * Reads in fixed-size pages and normalizes non-integer/negative limits.
3542 */
3643 readonly readFromSequence: (
3744 sequenceExclusive: number,
apps/server/src/persistence/Services/ProjectionCheckpoints.ts+30
......@@ -1,3 +1,11 @@
1/**
2 * ProjectionCheckpointRepository - Projection repository interface for checkpoints.
3 *
4 * Owns persistence operations for projected checkpoint summaries in thread
5 * timelines.
6 *
7 * @module ProjectionCheckpointRepository
8 */
19import {
210 CheckpointRef,
311 IsoDateTime,
......@@ -41,22 +49,44 @@ export const DeleteByThreadIdInput = Schema.Struct({
4149});
4250export type DeleteByThreadIdInput = typeof DeleteByThreadIdInput.Type;
4351
52/**
53 * ProjectionCheckpointRepositoryShape - Service API for projected checkpoints.
54 */
4455export interface ProjectionCheckpointRepositoryShape {
56 /**
57 * Insert or replace a projected checkpoint row.
58 *
59 * Upserts by composite key `(threadId, checkpointTurnCount)`.
60 */
4561 readonly upsert: (row: ProjectionCheckpoint) => Effect.Effect<void, ProjectionRepositoryError>;
4662
63 /**
64 * List projected checkpoints for a thread.
65 *
66 * Returned in ascending checkpoint turn-count order.
67 */
4768 readonly listByThreadId: (
4869 input: ListByThreadIdInput,
4970 ) => Effect.Effect<ReadonlyArray<ProjectionCheckpoint>, ProjectionRepositoryError>;
5071
72 /**
73 * Read a projected checkpoint by thread and turn-count key.
74 */
5175 readonly getByThreadAndTurnCount: (
5276 input: GetByThreadAndTurnCountInput,
5377 ) => Effect.Effect<Option.Option<ProjectionCheckpoint>, ProjectionRepositoryError>;
5478
79 /**
80 * Delete projected checkpoint rows by thread.
81 */
5582 readonly deleteByThreadId: (
5683 input: DeleteByThreadIdInput,
5784 ) => Effect.Effect<void, ProjectionRepositoryError>;
5885}
5986
87/**
88 * ProjectionCheckpointRepository - Service tag for checkpoint projection persistence.
89 */
6090export class ProjectionCheckpointRepository extends ServiceMap.Service<
6191 ProjectionCheckpointRepository,
6292 ProjectionCheckpointRepositoryShape
apps/server/src/persistence/Services/ProjectionPendingApprovals.ts+30
......@@ -1,3 +1,11 @@
1/**
2 * ProjectionPendingApprovalRepository - Repository interface for pending approvals.
3 *
4 * Owns persistence operations for projected approval requests awaiting user
5 * decisions.
6 *
7 * @module ProjectionPendingApprovalRepository
8 */
19import {
210 ApprovalRequestId,
311 IsoDateTime,
......@@ -37,24 +45,46 @@ export const DeleteProjectionPendingApprovalInput = Schema.Struct({
3745});
3846export type DeleteProjectionPendingApprovalInput = typeof DeleteProjectionPendingApprovalInput.Type;
3947
48/**
49 * ProjectionPendingApprovalRepositoryShape - Service API for pending approvals.
50 */
4051export interface ProjectionPendingApprovalRepositoryShape {
52 /**
53 * Insert or replace a projected pending approval row.
54 *
55 * Upserts by `requestId`.
56 */
4157 readonly upsert: (
4258 row: ProjectionPendingApproval,
4359 ) => Effect.Effect<void, ProjectionRepositoryError>;
4460
61 /**
62 * List pending approvals for a thread.
63 *
64 * Returned in ascending creation order.
65 */
4566 readonly listByThreadId: (
4667 input: ListProjectionPendingApprovalsInput,
4768 ) => Effect.Effect<ReadonlyArray<ProjectionPendingApproval>, ProjectionRepositoryError>;
4869
70 /**
71 * Read a pending approval row by request id.
72 */
4973 readonly getByRequestId: (
5074 input: GetProjectionPendingApprovalInput,
5175 ) => Effect.Effect<Option.Option<ProjectionPendingApproval>, ProjectionRepositoryError>;
5276
77 /**
78 * Delete a pending approval row by request id.
79 */
5380 readonly deleteByRequestId: (
5481 input: DeleteProjectionPendingApprovalInput,
5582 ) => Effect.Effect<void, ProjectionRepositoryError>;
5683}
5784
85/**
86 * ProjectionPendingApprovalRepository - Service tag for pending approval persistence.
87 */
5888export class ProjectionPendingApprovalRepository extends ServiceMap.Service<
5989 ProjectionPendingApprovalRepository,
6090 ProjectionPendingApprovalRepositoryShape
apps/server/src/persistence/Services/ProjectionPendingTurnStarts.ts+25
......@@ -1,3 +1,11 @@
1/**
2 * ProjectionPendingTurnStartRepository - Repository interface for pending turn starts.
3 *
4 * Owns persistence operations for projected "pending turn start" markers used
5 * while creating new turns from queued messages.
6 *
7 * @module ProjectionPendingTurnStartRepository
8 */
19import { IsoDateTime, MessageId, ThreadId } from "@t3tools/contracts";
210import { Option, Schema, ServiceMap } from "effect";
311import type { Effect } from "effect";
......@@ -21,20 +29,37 @@ export const DeleteProjectionPendingTurnStartInput = Schema.Struct({
2129});
2230export type DeleteProjectionPendingTurnStartInput = typeof DeleteProjectionPendingTurnStartInput.Type;
2331
32/**
33 * ProjectionPendingTurnStartRepositoryShape - Service API for pending turn-start markers.
34 */
2435export interface ProjectionPendingTurnStartRepositoryShape {
36 /**
37 * Insert or replace a pending turn-start row.
38 *
39 * Maintains at most one pending marker per thread.
40 */
2541 readonly upsert: (
2642 row: ProjectionPendingTurnStart,
2743 ) => Effect.Effect<void, ProjectionRepositoryError>;
2844
45 /**
46 * Read a pending turn-start marker by thread id.
47 */
2948 readonly getByThreadId: (
3049 input: GetProjectionPendingTurnStartInput,
3150 ) => Effect.Effect<Option.Option<ProjectionPendingTurnStart>, ProjectionRepositoryError>;
3251
52 /**
53 * Delete a pending turn-start marker by thread id.
54 */
3355 readonly deleteByThreadId: (
3456 input: DeleteProjectionPendingTurnStartInput,
3557 ) => Effect.Effect<void, ProjectionRepositoryError>;
3658}
3759
60/**
61 * ProjectionPendingTurnStartRepository - Service tag for pending turn-start persistence.
62 */
3863export class ProjectionPendingTurnStartRepository extends ServiceMap.Service<
3964 ProjectionPendingTurnStartRepository,
4065 ProjectionPendingTurnStartRepositoryShape
apps/server/src/persistence/Services/ProjectionProjects.ts+30
......@@ -1,3 +1,11 @@
1/**
2 * ProjectionProjectRepository - Projection repository interface for projects.
3 *
4 * Owns persistence operations for project rows in the orchestration projection
5 * read model.
6 *
7 * @module ProjectionProjectRepository
8 */
19import { IsoDateTime, ProjectId, ProjectScript } from "@t3tools/contracts";
210import { Option, Schema, ServiceMap } from "effect";
311import type { Effect } from "effect";
......@@ -26,23 +34,45 @@ export const DeleteProjectionProjectInput = Schema.Struct({
2634});
2735export type DeleteProjectionProjectInput = typeof DeleteProjectionProjectInput.Type;
2836
37/**
38 * ProjectionProjectRepositoryShape - Service API for projected project records.
39 */
2940export interface ProjectionProjectRepositoryShape {
41 /**
42 * Insert or replace a projected project row.
43 *
44 * Upserts by `projectId` and persists scripts through JSON encoding.
45 */
3046 readonly upsert: (row: ProjectionProject) => Effect.Effect<void, ProjectionRepositoryError>;
3147
48 /**
49 * Read a projected project row by id.
50 */
3251 readonly getById: (
3352 input: GetProjectionProjectInput,
3453 ) => Effect.Effect<Option.Option<ProjectionProject>, ProjectionRepositoryError>;
3554
55 /**
56 * List all projected project rows.
57 *
58 * Returned in deterministic creation order.
59 */
3660 readonly listAll: () => Effect.Effect<
3761 ReadonlyArray<ProjectionProject>,
3862 ProjectionRepositoryError
3963 >;
4064
65 /**
66 * Soft-delete a projected project row by id.
67 */
4168 readonly deleteById: (
4269 input: DeleteProjectionProjectInput,
4370 ) => Effect.Effect<void, ProjectionRepositoryError>;
4471}
4572
73/**
74 * ProjectionProjectRepository - Service tag for project projection persistence.
75 */
4676export class ProjectionProjectRepository extends ServiceMap.Service<
4777 ProjectionProjectRepository,
4878 ProjectionProjectRepositoryShape
apps/server/src/persistence/Services/ProjectionState.ts+30
......@@ -1,3 +1,11 @@
1/**
2 * ProjectionStateRepository - Projection repository interface for projector cursors.
3 *
4 * Owns persistence operations for projection cursor state used to resume
5 * incremental event projection.
6 *
7 * @module ProjectionStateRepository
8 */
19import { IsoDateTime, NonNegativeInt } from "@t3tools/contracts";
210import { Option, Schema, ServiceMap } from "effect";
311import type { Effect } from "effect";
......@@ -16,18 +24,40 @@ export const GetProjectionStateInput = Schema.Struct({
1624});
1725export type GetProjectionStateInput = typeof GetProjectionStateInput.Type;
1826
27/**
28 * ProjectionStateRepositoryShape - Service API for projector state records.
29 */
1930export interface ProjectionStateRepositoryShape {
31 /**
32 * Insert or replace a projection cursor row.
33 *
34 * Upserts by projector name.
35 */
2036 readonly upsert: (row: ProjectionState) => Effect.Effect<void, ProjectionRepositoryError>;
2137
38 /**
39 * Read projection cursor state for a projector key.
40 */
2241 readonly getByProjector: (
2342 input: GetProjectionStateInput,
2443 ) => Effect.Effect<Option.Option<ProjectionState>, ProjectionRepositoryError>;
2544
45 /**
46 * List all projector cursor rows.
47 */
2648 readonly listAll: () => Effect.Effect<ReadonlyArray<ProjectionState>, ProjectionRepositoryError>;
2749
50 /**
51 * Read the minimum applied sequence across all projectors.
52 *
53 * Returns `null` when no projector state rows exist.
54 */
2855 readonly minLastAppliedSequence: () => Effect.Effect<number | null, ProjectionRepositoryError>;
2956}
3057
58/**
59 * ProjectionStateRepository - Service tag for projection cursor persistence.
60 */
3161export class ProjectionStateRepository extends ServiceMap.Service<
3262 ProjectionStateRepository,
3363 ProjectionStateRepositoryShape
apps/server/src/persistence/Services/ProjectionThreadActivities.ts+27
......@@ -1,3 +1,11 @@
1/**
2 * ProjectionThreadActivityRepository - Projection repository interface for thread activity.
3 *
4 * Owns persistence operations for activity timeline entries projected from
5 * orchestration events.
6 *
7 * @module ProjectionThreadActivityRepository
8 */
19import {
210 EventId,
311 IsoDateTime,
......@@ -33,20 +41,39 @@ export const DeleteProjectionThreadActivitiesInput = Schema.Struct({
3341export type DeleteProjectionThreadActivitiesInput =
3442 typeof DeleteProjectionThreadActivitiesInput.Type;
3543
44/**
45 * ProjectionThreadActivityRepositoryShape - Service API for projected thread activity.
46 */
3647export interface ProjectionThreadActivityRepositoryShape {
48 /**
49 * Insert or replace a projected thread activity row.
50 *
51 * Upserts by `activityId` and JSON-encodes payload.
52 */
3753 readonly upsert: (
3854 row: ProjectionThreadActivity,
3955 ) => Effect.Effect<void, ProjectionRepositoryError>;
4056
57 /**
58 * List projected thread activity rows for a thread.
59 *
60 * Returned in ascending creation order.
61 */
4162 readonly listByThreadId: (
4263 input: ListProjectionThreadActivitiesInput,
4364 ) => Effect.Effect<ReadonlyArray<ProjectionThreadActivity>, ProjectionRepositoryError>;
4465
66 /**
67 * Delete projected thread activity rows by thread.
68 */
4569 readonly deleteByThreadId: (
4670 input: DeleteProjectionThreadActivitiesInput,
4771 ) => Effect.Effect<void, ProjectionRepositoryError>;
4872}
4973
74/**
75 * ProjectionThreadActivityRepository - Service tag for thread activity persistence.
76 */
5077export class ProjectionThreadActivityRepository extends ServiceMap.Service<
5178 ProjectionThreadActivityRepository,
5279 ProjectionThreadActivityRepositoryShape
apps/server/src/persistence/Services/ProjectionThreadMessages.ts+27
......@@ -1,3 +1,11 @@
1/**
2 * ProjectionThreadMessageRepository - Projection repository interface for messages.
3 *
4 * Owns persistence operations for projected thread messages rendered in the
5 * orchestration read model.
6 *
7 * @module ProjectionThreadMessageRepository
8 */
19import {
210 OrchestrationMessageRole,
311 MessageId,
......@@ -32,20 +40,39 @@ export const DeleteProjectionThreadMessagesInput = Schema.Struct({
3240});
3341export type DeleteProjectionThreadMessagesInput = typeof DeleteProjectionThreadMessagesInput.Type;
3442
43/**
44 * ProjectionThreadMessageRepositoryShape - Service API for projected thread messages.
45 */
3546export interface ProjectionThreadMessageRepositoryShape {
47 /**
48 * Insert or replace a projected thread message row.
49 *
50 * Upserts by `messageId`.
51 */
3652 readonly upsert: (
3753 message: ProjectionThreadMessage,
3854 ) => Effect.Effect<void, ProjectionRepositoryError>;
3955
56 /**
57 * List projected thread messages for a thread.
58 *
59 * Returned in ascending creation order.
60 */
4061 readonly listByThreadId: (
4162 input: ListProjectionThreadMessagesInput,
4263 ) => Effect.Effect<ReadonlyArray<ProjectionThreadMessage>, ProjectionRepositoryError>;
4364
65 /**
66 * Delete projected thread messages by thread.
67 */
4468 readonly deleteByThreadId: (
4569 input: DeleteProjectionThreadMessagesInput,
4670 ) => Effect.Effect<void, ProjectionRepositoryError>;
4771}
4872
73/**
74 * ProjectionThreadMessageRepository - Service tag for message projection persistence.
75 */
4976export class ProjectionThreadMessageRepository extends ServiceMap.Service<
5077 ProjectionThreadMessageRepository,
5178 ProjectionThreadMessageRepositoryShape
apps/server/src/persistence/Services/ProjectionThreadSessions.ts+25
......@@ -1,3 +1,11 @@
1/**
2 * ProjectionThreadSessionRepository - Repository interface for thread sessions.
3 *
4 * Owns persistence operations for projected provider-session linkage and
5 * runtime status for each thread.
6 *
7 * @module ProjectionThreadSessionRepository
8 */
19import {
210 IsoDateTime,
311 OrchestrationSessionStatus,
......@@ -33,18 +41,35 @@ export const DeleteProjectionThreadSessionInput = Schema.Struct({
3341});
3442export type DeleteProjectionThreadSessionInput = typeof DeleteProjectionThreadSessionInput.Type;
3543
44/**
45 * ProjectionThreadSessionRepositoryShape - Service API for projected thread sessions.
46 */
3647export interface ProjectionThreadSessionRepositoryShape {
48 /**
49 * Insert or replace a projected thread-session row.
50 *
51 * Upserts by `threadId`.
52 */
3753 readonly upsert: (row: ProjectionThreadSession) => Effect.Effect<void, ProjectionRepositoryError>;
3854
55 /**
56 * Read projected thread-session state by thread id.
57 */
3958 readonly getByThreadId: (
4059 input: GetProjectionThreadSessionInput,
4160 ) => Effect.Effect<Option.Option<ProjectionThreadSession>, ProjectionRepositoryError>;
4261
62 /**
63 * Delete projected thread-session state by thread id.
64 */
4365 readonly deleteByThreadId: (
4466 input: DeleteProjectionThreadSessionInput,
4567 ) => Effect.Effect<void, ProjectionRepositoryError>;
4668}
4769
70/**
71 * ProjectionThreadSessionRepository - Service tag for thread-session persistence.
72 */
4873export class ProjectionThreadSessionRepository extends ServiceMap.Service<
4974 ProjectionThreadSessionRepository,
5075 ProjectionThreadSessionRepositoryShape
apps/server/src/persistence/Services/ProjectionThreadTurns.ts+29
......@@ -1,3 +1,10 @@
1/**
2 * ProjectionThreadTurnRepository - Projection repository interface for turns.
3 *
4 * Owns persistence operations for projected turn records in thread timelines.
5 *
6 * @module ProjectionThreadTurnRepository
7 */
18import {
29 IsoDateTime,
310 MessageId,
......@@ -38,22 +45,44 @@ export const DeleteProjectionThreadTurnsInput = Schema.Struct({
3845});
3946export type DeleteProjectionThreadTurnsInput = typeof DeleteProjectionThreadTurnsInput.Type;
4047
48/**
49 * ProjectionThreadTurnRepositoryShape - Service API for projected thread turns.
50 */
4151export interface ProjectionThreadTurnRepositoryShape {
52 /**
53 * Insert or replace a projected turn row.
54 *
55 * Upserts by `turnId`.
56 */
4257 readonly upsert: (turn: ProjectionThreadTurn) => Effect.Effect<void, ProjectionRepositoryError>;
4358
59 /**
60 * List projected turns for a thread.
61 *
62 * Returned in ascending `turnCount` order.
63 */
4464 readonly listByThreadId: (
4565 input: ListProjectionThreadTurnsInput,
4666 ) => Effect.Effect<ReadonlyArray<ProjectionThreadTurn>, ProjectionRepositoryError>;
4767
68 /**
69 * Read a projected turn row by turn id.
70 */
4871 readonly getByTurnId: (
4972 input: GetProjectionThreadTurnInput,
5073 ) => Effect.Effect<Option.Option<ProjectionThreadTurn>, ProjectionRepositoryError>;
5174
75 /**
76 * Delete projected turn rows by thread.
77 */
5278 readonly deleteByThreadId: (
5379 input: DeleteProjectionThreadTurnsInput,
5480 ) => Effect.Effect<void, ProjectionRepositoryError>;
5581}
5682
83/**
84 * ProjectionThreadTurnRepository - Service tag for turn projection persistence.
85 */
5786export class ProjectionThreadTurnRepository extends ServiceMap.Service<
5887 ProjectionThreadTurnRepository,
5988 ProjectionThreadTurnRepositoryShape
apps/server/src/persistence/Services/ProjectionThreads.ts+30
......@@ -1,3 +1,11 @@
1/**
2 * ProjectionThreadRepository - Projection repository interface for threads.
3 *
4 * Owns persistence operations for projected thread records in the
5 * orchestration read model.
6 *
7 * @module ProjectionThreadRepository
8 */
19import { IsoDateTime, ProjectId, ThreadId, TurnId } from "@t3tools/contracts";
210import { Option, Schema, ServiceMap } from "effect";
311import type { Effect } from "effect";
......@@ -33,22 +41,44 @@ export const ListProjectionThreadsByProjectInput = Schema.Struct({
3341});
3442export type ListProjectionThreadsByProjectInput = typeof ListProjectionThreadsByProjectInput.Type;
3543
44/**
45 * ProjectionThreadRepositoryShape - Service API for projected thread records.
46 */
3647export interface ProjectionThreadRepositoryShape {
48 /**
49 * Insert or replace a projected thread row.
50 *
51 * Upserts by `threadId`.
52 */
3753 readonly upsert: (thread: ProjectionThread) => Effect.Effect<void, ProjectionRepositoryError>;
3854
55 /**
56 * Read a projected thread row by id.
57 */
3958 readonly getById: (
4059 input: GetProjectionThreadInput,
4160 ) => Effect.Effect<Option.Option<ProjectionThread>, ProjectionRepositoryError>;
4261
62 /**
63 * List projected threads for a project.
64 *
65 * Returned in deterministic creation order.
66 */
4367 readonly listByProjectId: (
4468 input: ListProjectionThreadsByProjectInput,
4569 ) => Effect.Effect<ReadonlyArray<ProjectionThread>, ProjectionRepositoryError>;
4670
71 /**
72 * Soft-delete a projected thread row by id.
73 */
4774 readonly deleteById: (
4875 input: DeleteProjectionThreadInput,
4976 ) => Effect.Effect<void, ProjectionRepositoryError>;
5077}
5178
79/**
80 * ProjectionThreadRepository - Service tag for thread projection persistence.
81 */
5282export class ProjectionThreadRepository extends ServiceMap.Service<
5383 ProjectionThreadRepository,
5484 ProjectionThreadRepositoryShape
apps/server/src/persistence/Services/ProviderSessionRuntime.ts+29
......@@ -1,3 +1,10 @@
1/**
2 * ProviderSessionRuntimeRepository - Repository interface for provider runtime sessions.
3 *
4 * Owns persistence operations for provider runtime metadata and resume cursors.
5 *
6 * @module ProviderSessionRuntimeRepository
7 */
18import {
29 IsoDateTime,
310 ProviderSessionId,
......@@ -33,25 +40,47 @@ export const DeleteProviderSessionRuntimeInput = Schema.Struct({
3340});
3441export type DeleteProviderSessionRuntimeInput = typeof DeleteProviderSessionRuntimeInput.Type;
3542
43/**
44 * ProviderSessionRuntimeRepositoryShape - Service API for provider runtime records.
45 */
3646export interface ProviderSessionRuntimeRepositoryShape {
47 /**
48 * Insert or replace a provider runtime row.
49 *
50 * Upserts by `providerSessionId`, including JSON payload/cursor fields.
51 */
3752 readonly upsert: (
3853 runtime: ProviderSessionRuntime,
3954 ) => Effect.Effect<void, ProviderSessionRuntimeRepositoryError>;
4055
56 /**
57 * Read provider runtime state by provider session id.
58 */
4159 readonly getBySessionId: (
4260 input: GetProviderSessionRuntimeInput,
4361 ) => Effect.Effect<Option.Option<ProviderSessionRuntime>, ProviderSessionRuntimeRepositoryError>;
4462
63 /**
64 * List all provider runtime rows.
65 *
66 * Returned in ascending last-seen order.
67 */
4568 readonly list: () => Effect.Effect<
4669 ReadonlyArray<ProviderSessionRuntime>,
4770 ProviderSessionRuntimeRepositoryError
4871 >;
4972
73 /**
74 * Delete provider runtime state by provider session id.
75 */
5076 readonly deleteBySessionId: (
5177 input: DeleteProviderSessionRuntimeInput,
5278 ) => Effect.Effect<void, ProviderSessionRuntimeRepositoryError>;
5379}
5480
81/**
82 * ProviderSessionRuntimeRepository - Service tag for provider runtime persistence.
83 */
5584export class ProviderSessionRuntimeRepository extends ServiceMap.Service<
5685 ProviderSessionRuntimeRepository,
5786 ProviderSessionRuntimeRepositoryShape
apps/server/src/persistence/Services/ProviderSessions.ts+30
......@@ -1,3 +1,11 @@
1/**
2 * ProviderSessionRepository - Repository interface for provider session lookup.
3 *
4 * Owns persistence operations that map internal sessions to provider kinds and
5 * optional thread ownership.
6 *
7 * @module ProviderSessionRepository
8 */
19import type { ProviderKind, ProviderSessionId, ThreadId } from "@t3tools/contracts";
210import { Option, ServiceMap } from "effect";
311import type { Effect } from "effect";
......@@ -26,25 +34,47 @@ export interface GetProviderSessionInput {
2634 readonly sessionId: ProviderSessionId;
2735}
2836
37/**
38 * ProviderSessionRepositoryShape - Service API for provider-session records.
39 */
2940export interface ProviderSessionRepositoryShape {
41 /**
42 * Insert or replace a provider-session row.
43 *
44 * Upserts by `sessionId`.
45 */
3046 readonly upsertSession: (
3147 input: UpsertProviderSessionInput,
3248 ) => Effect.Effect<void, ProviderSessionRepositoryError>;
3349
50 /**
51 * Read a provider-session row by session id.
52 */
3453 readonly getSession: (
3554 input: GetProviderSessionInput,
3655 ) => Effect.Effect<Option.Option<ProviderSessionEntry>, ProviderSessionRepositoryError>;
3756
57 /**
58 * List all provider-session rows.
59 *
60 * Returned in deterministic creation order.
61 */
3862 readonly listSessions: () => Effect.Effect<
3963 ReadonlyArray<ProviderSessionEntry>,
4064 ProviderSessionRepositoryError
4165 >;
4266
67 /**
68 * Delete a provider-session row by session id.
69 */
4370 readonly deleteSession: (
4471 input: DeleteProviderSessionInput,
4572 ) => Effect.Effect<void, ProviderSessionRepositoryError>;
4673}
4774
75/**
76 * ProviderSessionRepository - Service tag for provider-session persistence.
77 */
4878export class ProviderSessionRepository extends ServiceMap.Service<
4979 ProviderSessionRepository,
5080 ProviderSessionRepositoryShape
apps/server/src/provider/Errors.ts-71
......@@ -51,23 +51,6 @@ export class ProviderAdapterSessionClosedError extends Schema.TaggedErrorClass<P
5151 }
5252}
5353
54/**
55 * ProviderAdapterProtocolError - Invalid/unexpected provider protocol payload.
56 */
57export class ProviderAdapterProtocolError extends Schema.TaggedErrorClass<ProviderAdapterProtocolError>()(
58 "ProviderAdapterProtocolError",
59 {
60 provider: Schema.String,
61 operation: Schema.String,
62 detail: Schema.String,
63 cause: Schema.optional(Schema.Defect),
64 },
65) {
66 override get message(): string {
67 return `Provider adapter protocol error (${this.provider}) in ${this.operation}: ${this.detail}`;
68 }
69}
70
7154/**
7255 * ProviderAdapterRequestError - Provider protocol request failed or timed out.
7356 */
......@@ -164,61 +147,10 @@ export class ProviderSessionDirectoryPersistenceError extends Schema.TaggedError
164147 }
165148}
166149
167/**
168 * ProviderCheckpointUnavailableError - Checkpointing unavailable for this session.
169 */
170export class ProviderCheckpointUnavailableError extends Schema.TaggedErrorClass<ProviderCheckpointUnavailableError>()(
171 "ProviderCheckpointUnavailableError",
172 {
173 sessionId: Schema.String,
174 detail: Schema.String,
175 cause: Schema.optional(Schema.Defect),
176 },
177) {
178 override get message(): string {
179 return `Provider checkpoint unavailable for session ${this.sessionId}: ${this.detail}`;
180 }
181}
182
183/**
184 * ProviderCheckpointRangeError - Requested checkpoint range is invalid.
185 */
186export class ProviderCheckpointRangeError extends Schema.TaggedErrorClass<ProviderCheckpointRangeError>()(
187 "ProviderCheckpointRangeError",
188 {
189 sessionId: Schema.String,
190 fromTurnCount: Schema.Number,
191 toTurnCount: Schema.Number,
192 detail: Schema.String,
193 cause: Schema.optional(Schema.Defect),
194 },
195) {
196 override get message(): string {
197 return `Provider checkpoint range error for session ${this.sessionId}: ${this.fromTurnCount}..${this.toTurnCount} (${this.detail})`;
198 }
199}
200
201/**
202 * ProviderFilesystemError - Filesystem checkpoint capture/restore failure.
203 */
204export class ProviderFilesystemError extends Schema.TaggedErrorClass<ProviderFilesystemError>()(
205 "ProviderFilesystemError",
206 {
207 sessionId: Schema.String,
208 detail: Schema.String,
209 cause: Schema.optional(Schema.Defect),
210 },
211) {
212 override get message(): string {
213 return `Provider filesystem checkpoint error for session ${this.sessionId}: ${this.detail}`;
214 }
215}
216
217150export type ProviderAdapterError =
218151 | ProviderAdapterValidationError
219152 | ProviderAdapterSessionNotFoundError
220153 | ProviderAdapterSessionClosedError
221 | ProviderAdapterProtocolError
222154 | ProviderAdapterRequestError
223155 | ProviderAdapterProcessError;
224156
......@@ -227,8 +159,5 @@ export type ProviderServiceError =
227159 | ProviderUnsupportedError
228160 | ProviderSessionNotFoundError
229161 | ProviderSessionDirectoryPersistenceError
230 | ProviderCheckpointUnavailableError
231 | ProviderCheckpointRangeError
232 | ProviderFilesystemError
233162 | ProviderAdapterError
234163 | CheckpointServiceError;
apps/server/src/provider/Services/CodexAdapter.ts+3
......@@ -15,6 +15,9 @@ import { ServiceMap } from "effect";
1515import type { ProviderAdapterError } from "../Errors.ts";
1616import type { ProviderAdapterShape } from "./ProviderAdapter.ts";
1717
18/**
19 * CodexAdapterShape - Service API for the Codex provider adapter.
20 */
1821export interface CodexAdapterShape extends ProviderAdapterShape<ProviderAdapterError> {
1922 readonly provider: "codex";
2023}
apps/server/src/provider/Services/ProviderAdapterRegistry.ts+3
......@@ -14,6 +14,9 @@ import type { Effect } from "effect";
1414import type { ProviderAdapterError, ProviderUnsupportedError } from "../Errors.ts";
1515import type { ProviderAdapterShape } from "./ProviderAdapter.ts";
1616
17/**
18 * ProviderAdapterRegistryShape - Service API for adapter lookup by provider kind.
19 */
1720export interface ProviderAdapterRegistryShape {
1821 /**
1922 * Resolve the adapter for a provider kind.
apps/server/src/provider/Services/ProviderService.ts+7
......@@ -28,6 +28,9 @@ import type { Effect, Stream } from "effect";
2828
2929import type { ProviderServiceError } from "../Errors.ts";
3030
31/**
32 * ProviderServiceShape - Service API for provider session and turn orchestration.
33 */
3134export interface ProviderServiceShape {
3235 /**
3336 * Start a provider session.
......@@ -67,6 +70,8 @@ export interface ProviderServiceShape {
6770
6871 /**
6972 * List active provider sessions.
73 *
74 * Aggregates runtime session lists from all registered adapters.
7075 */
7176 readonly listSessions: () => Effect.Effect<ReadonlyArray<ProviderSession>>;
7277
......@@ -83,6 +88,8 @@ export interface ProviderServiceShape {
8388 *
8489 * Persisted session bindings remain so stale sessions can be resumed after
8590 * process restart.
91 *
92 * Runtime alias mappings are cleared as part of shutdown.
8693 */
8794 readonly stopAll: () => Effect.Effect<void, ProviderServiceError>;
8895
apps/server/src/provider/Services/ProviderSessionDirectory.ts+6
......@@ -42,9 +42,15 @@ export type ProviderSessionDirectoryWriteError =
4242 | ProviderValidationError
4343 | ProviderSessionDirectoryPersistenceError;
4444
45/**
46 * ProviderSessionDirectoryShape - Service API for provider session ownership metadata.
47 */
4548export interface ProviderSessionDirectoryShape {
4649 /**
4750 * Record or update ownership for one provider session.
51 *
52 * Preserves existing persisted fields when omitted and shallow-merges
53 * runtime payload objects.
4854 */
4955 readonly upsert: (
5056 binding: ProviderSessionBinding,
apps/server/src/terminal/Services/Manager.ts+52
......@@ -1,3 +1,11 @@
1/**
2 * TerminalManager - Terminal session orchestration service interface.
3 *
4 * Owns terminal lifecycle operations, output fanout, and session state
5 * transitions for thread-scoped terminals.
6 *
7 * @module TerminalManager
8 */
19import {
210 TerminalClearInput,
311 TerminalCloseInput,
......@@ -40,17 +48,61 @@ export interface TerminalStartInput extends TerminalOpenInput {
4048 rows: number;
4149}
4250
51/**
52 * TerminalManagerShape - Service API for terminal session lifecycle operations.
53 */
4354export interface TerminalManagerShape {
55 /**
56 * Open or attach to a terminal session.
57 *
58 * Reuses an existing session for the same thread/terminal id and restores
59 * persisted history on first open.
60 */
4461 readonly open: (input: TerminalOpenInput) => Effect.Effect<TerminalSessionSnapshot, unknown>;
62
63 /**
64 * Write input bytes to a terminal session.
65 */
4566 readonly write: (input: TerminalWriteInput) => Effect.Effect<void, unknown>;
67
68 /**
69 * Resize the PTY backing a terminal session.
70 */
4671 readonly resize: (input: TerminalResizeInput) => Effect.Effect<void, unknown>;
72
73 /**
74 * Clear terminal output history.
75 */
4776 readonly clear: (input: TerminalClearInput) => Effect.Effect<void, unknown>;
77
78 /**
79 * Restart a terminal session in place.
80 *
81 * Always resets history before spawning the new process.
82 */
4883 readonly restart: (input: TerminalOpenInput) => Effect.Effect<TerminalSessionSnapshot, unknown>;
84
85 /**
86 * Close an active terminal session.
87 *
88 * When `terminalId` is omitted, closes all sessions for the thread.
89 */
4990 readonly close: (input: TerminalCloseInput) => Effect.Effect<void, unknown>;
91
92 /**
93 * Subscribe to terminal runtime events.
94 */
5095 readonly subscribe: (listener: (event: TerminalEvent) => void) => Effect.Effect<() => void>;
96
97 /**
98 * Dispose all managed terminal resources.
99 */
51100 readonly dispose: () => Effect.Effect<void>;
52101}
53102
103/**
104 * TerminalManager - Service tag for terminal session orchestration.
105 */
54106export class TerminalManager extends ServiceMap.Service<TerminalManager, TerminalManagerShape>()(
55107 "terminal/TerminalManager",
56108) {}
apps/server/src/terminal/Services/PTY.ts+17
......@@ -1,3 +1,11 @@
1/**
2 * PtyAdapter - Terminal PTY adapter service contract.
3 *
4 * Defines the process primitives required by terminal session management
5 * without binding to a specific PTY implementation.
6 *
7 * @module PtyAdapter
8 */
19import { ServiceMap } from "effect";
210
311export interface PtyExitEvent {
......@@ -23,10 +31,19 @@ export interface PtySpawnInput {
2331 env: NodeJS.ProcessEnv;
2432}
2533
34/**
35 * PtyAdapterShape - Service API for spawning and controlling PTY processes.
36 */
2637export interface PtyAdapterShape {
38 /**
39 * Spawn a PTY process for a terminal session.
40 */
2741 spawn(input: PtySpawnInput): PtyProcess;
2842}
2943
44/**
45 * PtyAdapter - Service tag for PTY process integration.
46 */
3047export class PtyAdapter extends ServiceMap.Service<PtyAdapter, PtyAdapterShape>()(
3148 "terminal/PtyAdapter",
3249) {}
apps/server/src/wsServer.ts+21
......@@ -1,3 +1,11 @@
1/**
2 * Server - HTTP/WebSocket server service interface.
3 *
4 * Owns startup and shutdown lifecycle of the HTTP server, static asset serving,
5 * and WebSocket request routing.
6 *
7 * @module Server
8 */
19import fs from "node:fs";
210import http from "node:http";
311import path from "node:path";
......@@ -49,15 +57,28 @@ const MIME_TYPES: Record<string, string> = {
4957 ".map": "application/json",
5058};
5159
60/**
61 * ServerShape - Service API for server lifecycle control.
62 */
5263export interface ServerShape {
64 /**
65 * Start HTTP and WebSocket listeners.
66 */
5367 readonly start: Effect.Effect<
5468 http.Server,
5569 unknown,
5670 Scope.Scope | ServerRuntimeServices | ServerConfig
5771 >;
72
73 /**
74 * Wait for process shutdown signals.
75 */
5876 readonly stopSignal: Effect.Effect<unknown, never>;
5977}
6078
79/**
80 * Server - Service tag for HTTP/WebSocket lifecycle management.
81 */
6182export class Server extends ServiceMap.Service<Server, ServerShape>()("server/Server") {}
6283
6384const isServerNotRunningError = (error: unknown): boolean => {