mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-01 09:29:38 +02:00
108 KiB
108 KiB
TrustGraph Effect-Native Rewrite Opportunity Audit
This is the current backlog snapshot for the playbook in
ts/EFFECT_NATIVE_REWRITE_PLAYBOOK.md. The branch is ts-port-effect-v4.
The unrelated local file .idea/effect.intellij.xml must stay uncommitted.
Inputs
Verified source roots:
- TrustGraph TS port:
/home/elpresidank/YeeBois/dev/trustgraph/ts - Effect v4 subtree:
/home/elpresidank/YeeBois/projects/beep-effect2/.repos/effect-v4 - Installed Effect beta used by this workspace:
ts/node_modules/effect
Current signal counts from ts/packages after the 2026-06-02 dispatcher
Effect collections slice:
| Signal | Count |
|---|---|
Effect.runPromise |
175 |
Effect.runPromiseWith |
0 |
Effect.cached |
0 |
Layer.succeed |
13 |
Map< |
86 |
WebSocket |
72 |
new Map |
56 |
new Set |
15 |
Set< |
9 |
toPromiseRequestor |
0 |
makeAsyncProcessor |
19 |
receive( |
17 |
while ( |
2 |
new Error |
7 |
new Promise |
9 |
JSON.parse |
4 |
localStorage |
9 |
JSON.stringify |
8 |
setTimeout |
3 |
process.env |
3 |
Notes:
- The remaining
process.envhits are inpackages/workbench/playwright.config.ts. - In production
packages/base,packages/cli, andpackages/mcpsources, the strict scans fornew Error,new Promise,setTimeout,JSON.parse,JSON.stringify, and directprocess.envreads are clean. Effect.runPromiseis expected at external Promise compatibility boundaries, but each match should still be audited for avoidable internal runtime ownership.- The dispatcher Effect collections slice removed native
Map/Setfrom the gateway service registries, streaming membership set, and scoped requestor cache. Remaining broadMap/Setmatches include tests/fakes, WeakMap compatibility caches, short-lived pure traversal collections, and larger ref-backed service state that still needs focusedHashMap/MutableHashMapcleanup. - The
Effect.runPromiseandWebSocketcounts dropped in this snapshot becauseEffectRpcClientnow owns its RPC/socket layer withManagedRuntimeand uses Effect's WebSocket constructor layer. - The raw
WebSocketcount increased in this snapshot because the adapter slice added focused tests and typed adapter names; productionwebsocket-adapter.tsis now clean oftry/catch, normalError, and the previous constructor assertions. - The
new Errorcount dropped becausewebsocket-adapter.tsnow throwsS.TaggedErrorClassadapter errors. - The latest client socket slice removed the remaining production
trustgraph-socket.tsnormalError, rawJSON.parse, and listenertry/catchmatches. The remaining client socket modernization signal is the sharednewableFactoryconstructor assertion pattern. - The service entrypoint runtime slice dropped the
Effect.runPromisecount by replacing remaining flow servicerun()program facades withManagedRuntimeand routing localts/scripts/run-*launchers throughrunMain()/NodeRuntime.runMain. - The base processor compatibility runtime slice dropped the
Effect.runPromisecount again by movingAsyncProcessor,Flow, andFlowProcessorPromise compatibility facades ontoManagedRuntime. - The base flow definition schema slice removed hand-rolled
Predicate/object narrowing fromflow-processor.ts; signal counts are unchanged because this was a validation-quality migration. - The text completion stream sentinel slice removed the duplicated
Effect.void as Effect.Effect<undefined>assertions from provider stream unfold branches. Counts are unchanged because this was an Effect diagnostic and type-channel cleanup. - The text completion generator boundary slice removed the
Effect.runPromise(Effect.fail(...))fallback and the relatedAsyncGenerator/IteratorResultassertions frommodel/text-completion/common.ts. - The text completion provider status slice replaced manual status/statusCode
record assertions with
effect/Predicatenarrowing. - The base parameter spec accessor slice added Schema-backed
ParameterSpec<T>values plusflow.parameterEffect(spec)andflow.parameter(spec). Bare string parameter lookup remains available as anunknowncompatibility escape, while typed parameter access now decodes through Schema and fails with a taggedFlowParameterDecodeError. - The base producer/requestor spec accessor slice added typed spec-object
accessors for
ProducerSpec<T>andRequestResponseSpec<TReq, TRes>, then migrated flow service producer/requestor lookups off caller-chosen generic string calls. Spec object handles are scoped perFlowthrough WeakMaps and finalizers delete only the handle they registered. - The native PubSub boundary slice removed the unused legacy
messaging/subscriber.tsasync queue/fanout implementation. Effect's nativePubSubis an in-process hub and does not replace the broker-backedPubSubBackend/NATS boundary, but it should be preferred for future in-process broadcast/fanout needs. - The base producer scoped runtime slice moved the legacy
makeProducerPromise facade onto the existingmakeEffectProducerFromPubSubscoped factory. Publicstart/send/stopremain Promise compatibility boundaries, while producer allocation, flush, and finalizer close now go through the Effect runtime path. - The NATS typed boundary slice removed the dynamic
import("nats")header path and maps header construction plusack()/nak()failures into taggedPubSubErrors withEffect.try. Thereceive(andJSON.stringifycount increases are from the new mocked NATS backend test, not production code. - The NATS selective 404 slice replaced catch-all stream/consumer create
fallbacks with an internal
S.TaggedErrorClasslookup wrapper plusEffect.catchIfrecovery only for NATS JetStream missing-resource errors. Non-missing lookup failures now stay on the typed failure path without attempting to create streams or durable consumers. - The consumer rate-limit retry slice wired the previously unused
rateLimitTimeoutMsoption andTG_RATE_LIMIT_TIMEOUT_MSconfig into both legacy and Effect-native consumers. RepeatedTooManyRequestsErrorfailures now retry withSchedule.spaceduntil success or a tagged rate-limit timeout. Thenew Errorcount dropped by one because a touched consumer test fixture no longer uses a normalError. - The consumer concurrency ownership slice changed the Effect-native consumer
runtime so
concurrency > 1allocates one backend consumer per worker instead of sharing a singleBackendConsumer.receive()handle.stopis now idempotent throughRef, so explicit stop and scoped finalizers do not close workers twice. - The request-response stop signal slice added a
Deferredshutdown signal tomakeEffectRequestResponseFromPubSub. Pending requests now race response waiting against runtime stop and fail promptly with a taggedMessagingLifecycleErrorinstead of waiting for timeout. - The legacy consumer facade slice moved
makeConsumerontomakeEffectConsumerFromPubSubwith aManagedRuntimePromise boundary and a closeableScope. Consumer workers now useEffect.forkScopedso their lifetime is owned by the caller scope rather than the parent fiber. TheEffect.runPromise,receive(,new Promise, andsetTimeoutcounts dropped because the old blocking facade loop and its test timer shim were removed. - The workbench theme storage slice stopped mirroring
themeAtominto the legacytg-themelocalStorage key. The canonicaltrustgraph-workbench-theme-v1value remains owned byAtom.kvsoverBrowserKeyValueStore.layerLocalStorage; the first-paint host script reads that JSON-encoded key before React mounts and falls back totg-themeonly for legacy installs. - The Effect AI
LanguageModeladapter slice added a reusablemakeLanguageModelProviderbridge in text-completion common code. It mapsgenerateTextresponses toLlmResult, maps streamingtext-deltaand finalfinish.usageparts to TrustGraph chunks, and converts Effect AI rate and quota failures intoTooManyRequestsError. No concrete provider has been flipped yet. - The native request/response PubSub slice removed the local
Map<string, Queue>response subscriber fanout inmakeEffectRequestResponseFromPubSub. Response dispatch now publishes{ id, value }envelopes through nativeeffect/PubSub, and each request uses a scopedPubSub.SubscriptionplusStream.fromSubscriptionto wait for its matching response. - The Claude Effect AI slice moved the Claude provider off the direct
@anthropic-ai/sdkwrapper and onto@effect/ai-anthropicAnthropicLanguageModelthroughmakeLanguageModelProvider. The direct SDK dependency was removed from@trustgraph/flow. - A focused broker-backend scout found no remaining P0 broker runtime rewrite
after the producer, NATS, consumer concurrency, rate-limit, and
request-response stop slices.
PubSubBackendremains an intentional Promise-returning adapter boundary wrapped byPubSub/Effect services. - The gateway streaming callback slice added Effect-returning dispatcher
streaming methods, switched the RPC stream server off nested
Effect.runPromiseWith(context)queue offers, and replaced the clientStopStreamingsentinel error withStream.runForEachWhile. - The FalkorDB scoped client lifecycle slice removed the remaining
Effect.cachedmatches fromts/packages. FalkorDB triples store/query Live layers and direct compatibility factories now acquire clients throughEffect.acquireReleaseand disconnect them on scope close. TheEffect.runPromisecount increased by two because the new lifecycle tests run scoped programs at the test boundary. - The Qdrant config/schema/fakeability slice removed direct production
new QdrantClient, sync config loading, payload casts, and QdrantLayer.succeedservice construction from graph/doc store/query modules. The installed Qdrant client exposes no public close/disconnect method, so this remains a fakeable construction and Schema decode slice rather than a scoped finalizer slice.Effect.runPromiseincreased because the new tests and legacy service initialization logs run Effects at compatibility boundaries. - The client streaming facade slice did not change signal counts. It
centralized the legacy streaming
{ response, complete, error }envelope decode intrustgraph-socket.ts, uses Schema pluseffect/Predicateproperty narrowing for streaming payload reads, and leaves service-specific legacy completion markers only where they preserve public callback behavior. - The Ollama embeddings effectful layer slice dropped one
Layer.succeedmatch by makingOllamaEmbeddingsLiveeffectful and mapping config/load failures toEmbeddingsError. TheJSON.stringifycount increased by one because the new layer test uses a JSON response fixture. - The text completion provider stream helper slice removed all provider-local
Stream.unfoldpull loops, dropped thewhile (count from 9 to 3, and removed the Mistralcontent as stringassertion. The only remaining text-completioniterator.nextmatch is thetoAsyncGeneratorcompatibility adapter that exposes Effect streams through the publicAsyncGenerator<LlmChunk>provider contract. - The request-response queue stream slice replaced the Effectful
waitForResponsegenerator loop withStream.fromQueue,Stream.filterMapEffect,Result, andStream.runHead, dropping the remainingwhile (count from 3 to 2. The two remaining productionwhilehits are synchronous parsing/CLI traversal loops, not async polling loops. - The gateway RPC WebSocket cause-handling slice removed the Promise
.catcharound the socket program by sandboxing the Effect and handling the resultingCausein the Effect pipeline before the Fastify fire-and-forgetrunPromiseboundary. - The client RPC acquisition cause tap slice removed the Promise
.catchused only to update connection state on runtime/client acquisition failure.effect-rpc-client.tsnow usesEffect.tapCauseandCause.prettybefore the public Promise boundary. - The client socket close Effect boundary slice removed the Promise
.catchfromBaseApi.close(). The void public facade now runsrpc.close()throughEffect.tryPromiseand logs the tagged socket close error throughEffect.catch. - The client streaming callback Effect boundary slice removed the remaining
production Promise
.catchmatches fromtrustgraph-socket.tsby centralizing legacy callback request failures inrunLegacyStreamingRequest. The public callback facades still return/ignore Promises where required, but failure mapping now usesEffect.tryPromiseandEffect.catch. - The text completion provider effectful layer slice dropped six
Layer.succeedmatches by moving OpenAI, OpenAI-compatible, Azure OpenAI, Claude, Mistral, and Ollama processor layers ontomakeTextCompletionLayer(makeXProviderEffect(config)). SDK construction and config lookup now live in Effect; syncmakeXProviderexports remain compatibility facades. - The gateway dispatcher ownership and serialization slice did not change broad
signal counts. It stopped closing injected pubsub backends, brackets
one-shot publish producers with
Effect.acquireUseRelease, and routes gateway request/response translation throughEffect.trywrappers returning taggedDispatchSerializationErrorfailures. Record<string, any>andthrowLibrarianServiceErrorare now clean ints/packages.
Loop Passes
2026-06-02: Base Request/Response Facade
- Status: migrated and verified.
- Completed:
- Request/response startup now owns a scoped Effect runtime handle and maps failures to TrustGraph tagged messaging errors.
- Runtime shutdown is idempotent and uses scoped fibers.
- Tests cover Promise compatibility, tagged timeout errors, and tagged lifecycle errors.
- Verification:
bun run --cwd ts/packages/base testbun run --cwd ts/packages/base buildbun run --cwd ts check:tsgobun run --cwd ts buildbun run --cwd ts test
2026-06-02: Gateway Dispatcher Requestor Cache
- Status: migrated and package-verified.
- Completed:
- Gateway dispatcher caches scoped
EffectRequestResponsehandles instead ofPromise<RequestResponse>values. - Lazy requestor creation is serialized with
SynchronizedRef.modifyEffect. - Streaming final-marker detection is centralized.
- Dispatcher cleanup uses Effect scope/error handling instead of manual
try/catch.
- Gateway dispatcher caches scoped
- Verification:
bun run --cwd ts/packages/flow testbun run --cwd ts/packages/flow buildbun run --cwd ts check:tsgo
2026-06-02: Gateway Dispatcher Effect Collections Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/gateway/dispatch/manager.tsnow stores the flow/global service registries ineffect/HashMapinstead of nativeReadonlyMap, while explicit entry arrays preserve the public service-name ordering.- Streaming service membership now uses
effect/HashSetinstead of nativeSet. - The scoped requestor cache now stores
HashMap<string, EffectRequestResponse<unknown, unknown>>in the existingSynchronizedRef, replacingnew Mapcloning with immutableHashMap.set. - Cache hits and service topic lookups now use
HashMap.getpluseffect/Option, and ref update tuples useeffect/Tuple.makeinstead ofas constassertions. - Gateway dispatcher tests now cover concurrent same-key dispatches so the cache still creates exactly one scoped producer/consumer pair.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/gateway-dispatcher.test.tscd ts && bun run check:tsgo
2026-06-02: Strict Base, CLI, MCP, And tsgo Slice
- Status: migrated, root-verified, committed, and pushed.
- Completed:
- Base messaging, NATS backend, producer, consumer, subscriber,
request/response, runtime factories, processor programs, flow specs, and
LLM service now use Effect-native boundaries, schema codecs, scoped
cleanup, and
S.TaggedErrorClass.make(...)errors. - CLI commands now run Effect programs at the command boundary, wrap socket
lifecycle with
Effect.acquireUseRelease, encode JSON through Effect Schema, and write output withoutconsole.log. - MCP Effect server now loads env/config through
Config, wraps gateway calls withEffect.tryPromise, constructs schema classes with.make, and uses tagged errors. - MCP stdio compatibility server keeps
createMcpServerandrun, but uses Effect callbacks/tryPromise/schema encoding internally.run()usesManagedRuntime;runMain()usesNodeRuntime.runMain. - Flow stateful service launch sites now pass an explicit
Context.Contextinto the base processor runtime instead of hiding requirements behind assertions.
- Base messaging, NATS backend, producer, consumer, subscriber,
request/response, runtime factories, processor programs, flow specs, and
LLM service now use Effect-native boundaries, schema codecs, scoped
cleanup, and
- Verification:
cd ts && bun run checkcd ts && bun run testcd ts && bun run buildgit diff --check
2026-06-02: ConfigService Ref-Backed State Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/config/service.tsnow models runtime state as aSynchronizedRef<ConfigServiceState>instead of adding mutablestore,version, consumer, and producer fields onto the processor object.- Config operations have Effect-returning handlers with Promise facades only on the exported compatibility methods.
- Request narrowing now uses
effect/Predicaterather than request-record type assertions. - Persistence remains schema-backed and now reads/writes snapshots from the ref-backed state.
- The consume loop now uses
Effect.whileLoop; the remainingconsumer.receive(2000)call is a pubsub boundary for this service. - Service startup now exposes
runMain()throughNodeRuntime.runMain. The legacyrun()Promise facade usesManagedRuntime, andts/scripts/run-config.tsdelegates directly torunMain()instead of owning its own catch/process-exit wrapper. - Config-service tests cover tagged invalid mutation errors, workspace persistence, legacy load, concurrent ref-backed mutations, and push publishing from the stored producer handle.
- Verification:
bun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --checkgit diff --check
2026-06-02: ConfigService Operation Match Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/config/service.tsnow dispatchesConfigOperationwitheffect/Matchinstead of a nativeswitch.- The dispatcher is a named
Effect.fnand usesMatch.exhaustiveagainst the schema-derivedConfigOperationunion. - The per-message response sender now uses
Effect.fnUntracedinstead of an arrow function returningEffect.gen(...). - Config-service tests now cover all seven operations through
handleOperation, including tagged invalid mutation failures. - Existing explicit
neverannotations onpersistEffect,loadFromDiskEffect,persistStateEffect, andreadPersistedConfigEffectwere removed so Effect can infer the channel.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/config-service.test.tscd ts && bun run check:tsgo
2026-06-02: Librarian Operation Match Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/librarian/service.tsnow dispatches librarian and collection-management operations witheffect/Matchinstead of nativeswitchstatements.- Both dispatchers intentionally use
Match.orElserather thanMatch.exhaustivebecause raw broker message values can still contain unknown runtime operations before a schema boundary rejects them. - Existing tagged
LibrarianServiceErroroperation labels are preserved for promise, sync, Effect-helper, stream-only, and unknown-operation branches. - Librarian-service tests now cover representative Match-backed librarian dispatch paths, collection list/update/delete dispatch, and runtime fallback errors without type assertions.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/librarian-service.test.tscd ts && bun run check:tsgo
2026-06-04: FlowManager Operation Match Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/flow-manager/service.tsnow dispatches flow operations witheffect/Matchinstead of a nativeswitch.- The dispatcher keeps the existing config refresh behavior before routing
and uses
Match.orElsebecauseFlowRequest.operationis a public wire-levelstring, not a closed schema literal union. - Existing tagged
FlowManagerErrorbehavior is preserved for unknown operations and branch-specific failures. - Flow-manager tests now cover all eight flow operations through
handleOperation, including config-client blueprint mutations and the runtime unknown-operation fallback.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/flow-manager-service.test.tscd ts && bun run check:tsgo
2026-06-04: Client Connection State SubscriptionRef Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/client/src/socket/effect-rpc-client.tsnow owns RPC connection state ineffect/SubscriptionRefinstead of a mutable state variable plus manual listenerSet.ts/packages/client/src/socket/trustgraph-socket.tsnow bridges UI connection-state listeners throughSubscriptionRef.changesinstead of a hand-rolled listener array.- Both public
subscribeAPIs preserve synchronous immediate replay and unsubscribe compatibility while using Effect fibers for later updates. - Client tests now drive a fake RPC state source to prove immediate replay, connection updates, and unsubscribe behavior.
- Verification:
bun run --cwd ts/packages/client test -- src/__tests__/rpc-timeout.test.tscd ts && bun run check:tsgo
2026-06-04: Client Callback Match Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/client/src/socket/trustgraph-socket.tsnow maps RPC connection status witheffect/Matchinstead of a nativeswitch.- Flow agent streaming chunk callbacks now use
effect/Matchforthought,observation,answer,final-answer, andaction, while preserving ignored behavior for unknown chunk types withMatch.orElse. - Client RPC tests now drive agent stream chunks through the fake RPC stream to prove callback dispatch, ignored fallback behavior, completion signals, and metadata forwarding.
- Verification:
bun run --cwd ts/packages/client test -- src/__tests__/rpc-timeout.test.tscd ts && bun run check:tsgo
2026-06-04: Gateway Term Service HashSet Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/gateway/dispatch/serialize.tsnow useseffect/HashSetfor static term-bearing request/response service membership instead of nativeSet.- Request and response translators preserve the same deep client/internal
term conversion behavior via
HashSet.hasmembership checks.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/gateway-dispatcher.test.tscd ts && bun run check:tsgo
2026-06-04: Effect AI Stream Part Match Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/model/text-completion/common.tsnow mapsResponse.StreamPartvalues witheffect/Matchinstead of a nativeswitch.- The matcher handles
text-delta,finish, anderrorexplicitly, while preserving ignored behavior for other valid stream parts withMatch.orElse. - Text-completion common tests now include an ignored
text-startstream part before text deltas to prove the fallback path remains silent.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/text-completion-common.test.tscd ts && bun run check:tsgo
2026-06-04: MCP Tool Transport Adapter Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/agent/mcp-tool/service.tsno longer uses anas unknown as Parameters<Client["connect"]>[0]assertion when connecting the MCP SDK client.- The concrete
StreamableHTTPClientTransportis wrapped in a smallTransportadapter that forwards callbacks, send, close, and protocol version updates while avoiding the SDK's exact-optionalsessionIdtype mismatch. - The transport release path still closes the concrete SDK transport.
- Verification:
bun run --cwd ts/packages/flow testcd ts && bun run check:tsgo
2026-06-04: Agent Service Match Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/agent/react/service.tsno longer uses nativeswitchfor configured tool construction, live tool wiring, or ReAct continuation parsing.- Configured tool construction now uses
Effect.fnpluseffect/Matchand preserves unknown-tool logging/fallback behavior. - The ReAct parser is exported for focused tests and uses an exhaustive
Matchover continuation sections. - Agent service tests cover configured-tool loading, unknown configured-tool fallback, default descriptions, MCP tool args, continuation parsing, and final-answer parsing.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/agent-service.test.tscd ts && bun run check:tsgo
2026-06-04: CLI Library MIME Match Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/cli/src/commands/library.tsnow useseffect/Matchfor extension-to-MIME mapping instead of a nativeswitch.guessMimeTypeis exported for focused CLI helper coverage.- CLI tests now cover known extensions, case normalization,
html/htmaliases, and fallback behavior for unknown or extensionless paths. ts/packages/cli/package.jsonexcludesdist/**from Vitest so built test output is not executed a second time after root build lanes.
- Verification:
bun run --cwd ts/packages/cli test -- src/__tests__/library.test.tscd ts && bun run check:tsgo
2026-06-04: Streaming ReAct Parser Match Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/agent/react/parser.tsnow useseffect/Matchfor ReAct state emission instead of a nativeswitch.- Marker states now use an inferred
MarkerStatetype instead of per-entryas ReActStateassertions. - Flush now processes an unterminated final buffer line through marker
detection, fixing
Final Answer:chunks that arrive without a trailing newline. - Parser tests cover split markers, pre-marker thought fallback, continuations, action input, and final-answer routing.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/agent-parser.test.tscd ts && bun run check:tsgo
2026-06-02: RAG And Agent Requestor Bridge Slice
- Status: migrated, root-verified, committed, and pushed.
- Completed:
ts/packages/flow/src/retrieval/graph-rag.tsandts/packages/flow/src/retrieval/document-rag.tsnow acceptEffectRequestResponseclients directly. The engines no longer adapt Effect requestors back to Promise requestors and then wrap those calls inEffect.tryPromise.ts/packages/flow/src/retrieval/graph-rag-service.tsandts/packages/flow/src/retrieval/document-rag-service.tsnow pass native flow requestors directly into the engines.ts/packages/flow/src/agent/react/tools.tsnow acceptsEffectRequestResponseclients directly for graph RAG, document RAG, triples, and MCP tool calls. Tool input narrowing uses Schema andeffect/Predicaterather than local request/response type assertions.ts/packages/flow/src/agent/react/service.tswires default and configured tools with native Effect requestors instead oftoPromiseRequestor.- Graph RAG, document RAG, and agent service startup now expose
runMain()throughNodeRuntime.runMain; their legacyrun()Promise facades useManagedRuntime. ts/scripts/run-graph-rag.ts,ts/scripts/run-document-rag.ts, andts/scripts/run-agent.tsnow delegate torunMain().
- Verification:
bun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run test
2026-06-02: KnowledgeCore Ref-Backed State Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/cores/service.tsnow exposes a typedKnowledgeCoreServiceinstead ofAsyncProcessorRuntime & Record<string, any>.- Runtime state now lives in
SynchronizedRef<KnowledgeCoreServiceState>withkgCores,deCores, the request consumer, and response producer. - Knowledge operations now have Effect-returning handlers with Promise facades only on exported compatibility methods.
- Persistence now decodes legacy and current snapshot shapes with Effect
Schema and encodes JSON through Schema rather than raw
JSON.parse/JSON.stringifyplus assertions. - The consume loop now uses
Effect.whileLoop; the remainingconsumer.receive(2000)call is a pubsub boundary for this service. - The service exposes
runMain()throughNodeRuntime.runMain; legacyrun()usesManagedRuntime, andts/scripts/run-knowledge.tsdelegates torunMain(). ts/packages/base/src/schema/messages.tsnow models legacy hyphenated knowledge request/response aliases so the service can preserve the wire shape without response type assertions.- New knowledge-core tests cover ref-backed mutation, graph embedding alias responses, concurrent state updates, and legacy persistence loading.
- Verification:
bun run --cwd ts/packages/base buildbun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run test
2026-06-02: KnowledgeCore Operation Match Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/cores/service.tsnow dispatchesKnowledgeOperationwitheffect/Matchinstead of a nativeswitch.- The dispatcher is a named
Effect.fnand usesMatch.exhaustiveagainst the schema-derivedKnowledgeOperationunion, so newly modeled operations should surface as type/check failures until handled.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/knowledge-core-service.test.tscd ts && bun run check:tsgocd ts && bun run buildcd ts && bun run testcd ts && bun run lintgit diff --check
2026-06-02: Flow Manager And Librarian Runtime Normalization
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/flow-manager/service.tsandts/packages/flow/src/librarian/service.tsnow exposerunMain()throughNodeRuntime.runMain.- Their legacy
run()Promise facades now useManagedRuntimeinstead of directly owningEffect.runPromise. ts/scripts/run-flow-manager.tsandts/scripts/run-librarian.tsnow delegate torunMain()instead of wrapping startup with local.catch(console.error/process.exit)handlers.
- Verification:
bun run --cwd ts/packages/flow buildcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: FlowManager Ref-Backed State Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/flow-manager/service.tsnow exposes a typedFlowManagerServiceinstead ofAsyncProcessorRuntime & Record<string, any>.- Runtime state now lives in
SynchronizedRef<FlowManagerServiceState>withflows,blueprints, the request consumer, response producer, and config request client. - Flow operations now have Effect-returning handlers with Promise facades only on exported compatibility methods.
- Blueprint config loading now narrows runtime values before constructing
Blueprintrecords, replacing the priorparsed as Blueprintshortcut. start-flowandstop-flowmutate the flow map throughSynchronizedRef.modifyEffect, making duplicate checks and map updates atomic.- The consume loop now uses
Effect.whileLoop; the remainingconsumer.receive(2000)call is a pubsub boundary for this service. - New flow-manager tests cover tagged errors, ref-backed flow mutation, config push/delete requests, blueprint narrowing, duplicate concurrent starts, and message-level flow-error responses.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/flow-manager-service.test.tsbun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: FlowManager Effect.fn Normalization Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/flow-manager/service.tsno longer defines reusable helpers as arrow functions that immediately returnEffect.gen(...).- Config request, blueprint refresh, flow refresh, blueprint handlers, flow
handlers, config push/delete, resource close, consume, run, and local
operation handling now use named
Effect.fnproviders. - Hot local helpers for one-message consumption and response sending use
Effect.fnUntraced. pushFlowsConfigEffectkeeps its best-effort logging/swallowing contract through theEffect.fnpipeable form instead of a wrapper generator.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/flow-manager-service.test.tscd ts && bun run check:tsgocd ts && bun run buildcd ts && bun run testcd ts && bun run lintgit diff --check
2026-06-02: Librarian Schema And Assertion Cleanup Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/base/src/schema/messages.tsnow models librarian upload and stream request/response fields directly, instead of requiring service-sideas LibrarianResponsecasts for the existing wire protocol.ts/packages/flow/src/librarian/service.tsnow decodes persisted librarian state through a concreteS.fromJsonStringschema instead of a generic JSON decode plusas A.- Document metadata
metadatatriples now narrow through Schema decoding withOptionbefore being included in normalized metadata. - Upload, stream, and complete-upload request/response constructors now rely on the schema-modeled fields instead of local type assertions.
- New librarian tests cover modeled upload fields, concrete persisted-state loading, and schema-backed metadata triple normalization.
- Remaining:
- Resolved by the typed runtime loop and ref-backed state slices below.
- Verification:
bun run --cwd ts/packages/base buildbun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow test -- src/__tests__/librarian-service.test.tsbun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Librarian Tagged Operation Helper Slice
- Status: migrated and root-verified.
- Completed:
- Removed the librarian
throwLibrarianServiceErrorhelper. get-document-metadata,list-children,upload-chunk,get-upload-status, andabort-uploadnow dispatch through local Effect-returning helpers that fail withLibrarianServiceError.- Compatibility methods for those operations now return Promise facades
backed by
Effect.runPromise. - The librarian tests now await the Promise compatibility facade for upload status.
- Removed the librarian
- Remaining:
- Resolved by the typed runtime loop and ref-backed state slices below.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/librarian-service.test.tsbun run --cwd ts/packages/flow buildcd ts && bun run checkbun run --cwd ts/packages/flow testcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Librarian Typed Runtime Loop Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/librarian/service.tsnow exposes a typedLibrarianServiceinterface instead ofAsyncProcessorRuntime & Record<string, any>.- Service construction now uses
makeAsyncProcessor<LibrarianServiceError>withrunEffect; the old method-bagrunoverride andas LibrarianServicecast are gone. - The librarian startup poller now uses
Effect.whileLoop. - The local operation helpers retrieve the initialized service through an Effect gate rather than closing over an unsafe partially built value.
- Remaining:
- Resolved by the ref-backed state slice below.
- Verification:
bun run --cwd ts/packages/flow buildcd ts && bun run checkbun run --cwd ts/packages/flow testcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Librarian Ref-Backed State Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/librarian/service.tsnow stores documents, processing records, upload sessions, collection manager, and pubsub handles inSynchronizedRef<LibrarianServiceState>.- Document, processing, upload, collection, persistence, load, and stop paths now read snapshots or mutate cloned maps/managers through the ref instead of writing fields on the service object.
- Upload chunk updates clone nested
UploadSession.chunksbefore replacing the upload map entry, avoiding mutable nested state hidden behind the ref. - Librarian response producers and consumers are read/nullified through ref-backed handles.
- Verification:
bun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow test -- src/__tests__/librarian-service.test.tscd ts && bun run checkbun run --cwd ts/packages/flow testcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Client RPC Managed Runtime Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/client/src/socket/effect-rpc-client.tsnow builds oneManagedRuntimefrom the RPC client layer instead of manually creating aScope, building the layer, and callingEffect.runPromisefor every operation.- RPC dispatch and stream dispatch continue to expose the existing
Promise-returning
EffectRpcClientfacade, but they run through the managed runtime and close withruntime.dispose(). - The Effect RPC socket path now consumes
Socket.layerWebSocketConstructorGlobalinstead of a duplicate local WebSocket constructor layer. - Dispatch payload construction now uses
DispatchPayload.make(...)so schema classes are not instantiated withnew. - Client socket logging and timestamp creation now use Effect
LoggerandClockinstead of direct console andDate.now()calls in the touched surface.
- Verification:
bun run --cwd ts/packages/client buildcd ts && bun run checkbun run --cwd ts/packages/client testcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Client WebSocket Adapter Error Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/client/src/socket/websocket-adapter.tsnow models host fallback failures withWebSocketAdapterErrorviaS.TaggedErrorClass.- Synchronous
getWebSocketConstructor()andgetRandomValues()facades keep their public signatures while usingResult.tryinstead of localtry/catchblocks. - Runtime predicates now narrow WebSocket constructor modules and crypto modules without the previous constructor/result type assertions.
- New adapter tests cover global WebSocket selection, optional
wsfallback, global crypto, typed crypto failure, and typed adapter errors.
- Verification:
bun run --cwd ts/packages/client buildbun run --cwd ts/packages/client test -- src/__tests__/websocket-adapter.test.tsbun run --cwd ts/packages/client testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --checkgit diff --check
2026-06-02: Client Socket Tagged Error And JSON Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/client/src/socket/trustgraph-socket.tsnow models socket API failures withTrustGraphSocketErrorviaS.TaggedErrorClass.- Flow/blueprint JSON response parsing now uses Schema decoding through
S.UnknownFromJsonStringinstead of rawJSON.parse. - Token-cost config JSON keeps the previous invalid-string fallback behavior while decoding through Schema/Option.
- Connection-state listener isolation now uses
Result.tryand typed socket errors instead of a localtry/catch. - Flow start, row embeddings, collection update, and response-error
failures now reject with tagged socket errors instead of normal
Error. - Flow API tests cover invalid JSON and response-error rejections.
- Verification:
bun run --cwd ts/packages/client buildbun run --cwd ts/packages/client test -- src/__tests__/flows-api.test.tscd ts && bun run checkbun run --cwd ts/packages/client testcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Client Newable Factory Compatibility Decision
- Status: documented no-op for the current loop.
- Evidence:
ts/packages/workbench/src/atoms/workbench.tsconstructsnew BaseApi(...).ts/packages/client/src/__tests__/flows-api.test.tsconstructsnew FlowsApi(...), and sibling API facades expose the same constructor shape.EffectRpcClientandBaseApialso preserve callable factory exports for compatibility with the vendored TrustGraph client shape.
- Decision:
- The remaining
newableFactory(... ) as unknown as NewableFactory<...>assertions in client socket files are TypeScript compatibility boundaries, not Effect error/requirement channel assertions and not replacements for an Effect primitive. - Removing them safely requires a deliberate public API redesign or explicit class implementations for every API facade, not a local Effect-native rewrite.
- The remaining
- Verification:
- Current client/root verification from the tagged error slice covers this no-op decision.
2026-06-02: Service Entrypoint Runtime Slice
- Status: migrated and root-verified.
- Completed:
- Remaining flow service
run(): Promise<void>program facades now useManagedRuntime.make(Layer.empty)instead of directEffect.runPromise(program). - Remaining flow service modules now expose
runMain()throughNodeRuntime.runMain(program). - Local
ts/scripts/run-*launchers for gateway, prompt, chunker, extractor, PDF decoder, embeddings, triples, graph/document embeddings, text-completion providers, and MCP tool service now delegate directly torunMain(). - Direct
Effect.runPromise(program)matches ints/packages/flow/srcare clean. RemainingEffect.runPromisematches are callback/Promise compatibility boundaries for later slices.
- Remaining flow service
- Verification:
bun run --cwd ts/packages/flow buildgit diff --checkcd ts && bun run checkbun run --cwd ts/packages/flow testcd ts && bun run buildcd ts && bun run test
2026-06-02: Base Processor Compatibility Runtime Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/base/src/processor/async-processor.tsnow uses aManagedRuntimefor Promise compatibility methods, signal-shutdown execution, and legacyAsyncProcessor.launch.ts/packages/base/src/processor/flow.tsnow owns a per-flowManagedRuntimeforstart,stop,runInCompatibilityScope, and Promise resource facades.ts/packages/base/src/processor/flow-processor.tsnow uses aManagedRuntimefor the publicstart(context)facade instead of a localEffect.runPromiseWithrunner.ts/packages/base/src/spec/parameter-spec.tsnow routes legacyaddthroughflow.runInCompatibilityScope(...), matching the other specs.- Subagent checks confirmed
NodeRuntimeis process-entrypoint-only here;@trustgraph/baseshould not add an@effect/platform-nodedependency for these compatibility facades.
- Remaining:
- Constructor
as unknown asshims in base processors preserve callable-plus-newable public exports and are compatibility boundaries for this loop. - Typed string lookup casts in
Flowneed a real typed-spec/key redesign;HashMap/MutableHashMapalone cannot inferTfrom a bare string.
- Constructor
- Verification:
bun run --cwd ts/packages/base buildbun run --cwd ts/packages/base testcd ts && bun run checkgit diff --checkcd ts && bun run buildcd ts && bun run test
2026-06-02: Base Flow Definition Schema Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/base/src/processor/flow-processor.tsnow validatesconfig.flowswith Effect Schema instead of localPredicate/object/string-record guards.- Invalid flow definition payloads still log/skip and preserve the existing config-handler and acknowledgement behavior.
ts/packages/base/src/__tests__/flow-processor-runtime.test.tsnow covers an invalid nested flow definition that is acknowledged without starting resources.
- Verification:
bun run --cwd ts/packages/base test -- src/__tests__/flow-processor-runtime.test.tsbun run --cwd ts/packages/base buildcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Text Completion Stream Sentinel Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/model/text-completion/{ollama,openai,mistral,azure-openai,claude,openai-compatible}.tsnow return theStream.unfoldend sentinel withEffect.as(Effect.void, undefined).- Removed six
Effect.void as Effect.Effect<undefined>assertions without replacing them withEffect.succeed(undefined), which@effect/tsgoflags as a diagnostic.
- Verification:
bun run --cwd ts/packages/flow buildcd ts && bun run checkbun run --cwd ts/packages/flow testcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Text Completion Generator Boundary Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/model/text-completion/common.tsnow rejects fallbackAsyncGenerator.throw(...)calls with the mapped tagged provider error directly instead of runningEffect.fail(...)throughEffect.runPromise.- The custom generator object no longer uses
as AsyncGenerator,as Promise<IteratorResult<LlmChunk>>, oras LlmChunkassertions. - Added a focused unit test for fallback throw mapping.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/text-completion-common.test.tsbun run --cwd ts/packages/flow buildcd ts && bun run checkbun run --cwd ts/packages/flow testcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Text Completion Provider Status Narrowing Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/model/text-completion/common.tsnow useseffect/Predicatenarrowing for providerstatus/statusCodeinspection instead of local record assertions.ts/packages/flow/src/__tests__/text-completion-common.test.tscovers both rate-limit status fields.
- Verification:
bun run --cwd ts/packages/flow test -- src/__tests__/text-completion-common.test.tsbun run --cwd ts/packages/flow buildcd ts && bun run checkbun run --cwd ts/packages/flow testcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Base Parameter Spec Accessor Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/base/src/spec/parameter-spec.tsnow modelsParameterSpec<T>with an Effect Schema codec. Legacy parameter specs default toS.Unknown, preserving name-based registration while making typed access schema-backed.ts/packages/base/src/processor/flow.tsnow exposesflow.parameterEffect(spec)andflow.parameter(spec)for inferred, Schema-decoded parameter values. String lookup remains available as anunknowncompatibility escape instead of a caller-chosen generic type.- Parameter schema failures now fail with the tagged
FlowParameterDecodeErrorrather than a normalError. ts/packages/flow/src/chunking/service.tsnow declares numeric chunk parameters once and retrieves them through the typed spec-object accessor.ts/packages/base/src/__tests__/flow-spec-runtime.test.tscovers typed parameter decoding, legacy string lookup, missing parameter errors, sync accessor decoding, and schema mismatch errors.
- Remaining:
- Add typed spec-object accessors for producers and requestors so call sites can stop spelling generic string lookups for those registries too.
- Verification:
bun run --cwd ts/packages/base test -- src/__tests__/flow-spec-runtime.test.tsbun run --cwd ts/packages/base buildbun run --cwd ts/packages/flow buildcd ts && bun run checkbun run --cwd ts/packages/base testbun run --cwd ts/packages/flow testcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Base Producer And Requestor Spec Accessor Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/base/src/spec/producer-spec.tsnow exposesProducerSpec<T>.producerEffect(flow)and stores typed producer handles in a per-spec WeakMap keyed byFlow.ts/packages/base/src/spec/request-response-spec.tsnow exposesRequestResponseSpec<TReq, TRes>.requestorEffect(flow)and stores typed requestor handles in a per-spec WeakMap keyed byFlow.- Spec finalizers remove only the exact handle they registered, avoiding stale finalizers deleting newer registrations for the same flow/spec pair.
ts/packages/base/src/processor/flow.tsnow supportsflow.producerEffect(spec),flow.requestorEffect(spec),flow.producer(spec), andflow.requestor(spec)while keeping string accessors as untyped compatibility escapes.- Base service adapters and flow service handlers now reuse the same hoisted producer/requestor spec object in their spec arrays and handler lookups.
ts/packages/base/src/__tests__/flow-spec-runtime.test.tscovers typed spec-object lookups, duplicate spec identity failures, and scoped finalizer cleanup for producer and requestor handles.
- Remaining:
- Bare string
Flowproducer/requestor accessors remain compatibility escapes for external/legacy callers, but new Effect service code should use spec objects.
- Bare string
- Verification:
bun run --cwd ts/packages/base test -- src/__tests__/flow-spec-runtime.test.tsbun run --cwd ts/packages/base buildbun run --cwd ts/packages/flow buildbun run --cwd ts/packages/base testbun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Native PubSub Boundary Slice
- Status: migrated and package-verified.
- Completed:
- Confirmed Effect's native
PubSubmodule is an in-process asynchronous hub with scoped subscriptions, not a NATS/Pulsar-compatible broker boundary. - Kept TrustGraph's
PubSubBackendandPubSubservice as the broker adapter layer because it owns topics, broker producers/consumers, acknowledgement, schema codecs, and backend lifecycle. - Removed the unused legacy
ts/packages/base/src/messaging/subscriber.tsimplementation, which duplicated in-process async queue/fanout behavior. - Removed the corresponding
makeAsyncQueue,makeSubscriber,Subscriber, andAsyncQueuebarrel exports fromts/packages/base/src/messaging/index.ts.
- Confirmed Effect's native
- Remaining:
- Future in-process fanout or request-streaming code should use
effect/PubSub,Queue,Stream.fromPubSub, orChannel.fromPubSubrather than adding another local async queue implementation. - Do not replace
PubSubBackendwitheffect/PubSubunless the code path is explicitly local-only and does not need broker semantics.
- Future in-process fanout or request-streaming code should use
- Verification:
bun run --cwd ts/packages/base buildbun run --cwd ts/packages/base testcd ts && bun run check
2026-06-02: Gateway Streaming Callback Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/gateway/dispatch/manager.tsnow exposesdispatchGlobalServiceStreamingEffectanddispatchFlowServiceStreamingEffectso Effect callers can handle stream chunks without Promise callback re-entry.- The existing Promise-returning streaming methods remain as compatibility
facades and wrap responders with
Effect.tryPromise. ts/packages/flow/src/gateway/rpc-server.tsnow writes stream chunks into the RPC queue through the dispatcher Effect path, removing the priorEffect.contextplusEffect.runPromiseWith(context)bridge.ts/packages/client/src/socket/effect-rpc-client.tsnow usesStream.runForEachWhilefor early stream termination instead of throwing a syntheticStopStreamingtagged error.- Gateway dispatcher tests now exercise both the Promise compatibility streaming path and the Effect-native responder path.
- Remaining:
ts/packages/flow/src/gateway/rpc-protocol.tsremains a Fastify socket compatibility bridge, not a direct replacement target for Effect RPC server layers yet.
- Verification:
bun run --cwd ts/packages/flow buildbun run --cwd ts/packages/client buildbunx --bun vitest run src/__tests__/gateway-dispatcher.test.tsbunx --bun vitest run src/__tests__/rpc-timeout.test.tscd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Client Streaming Facade Normalization Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/client/src/socket/trustgraph-socket.tsnow decodes the legacy streaming envelope with Schema before service-specific callback handling.- Streaming payload reads now use
effect/Predicateproperty narrowing helpers instead of repeated response-wrapper assertions. - Graph RAG, document RAG, text completion, prompt, agent, and document
stream callbacks now use a shared
streamComplete(...)helper. The RPCDispatchStreamChunk.completebit is the default transport completion source, with legacy service markers preserved for public compatibility. - Explainability triples are decoded through a recursive Schema instead of
as Triple[]. - The focused client test now proves normalized
DispatchStreamChunkcompletion flows throughgraphRagStreamingand final metadata.
- Verification:
bunx --bun vitest run src/__tests__/rpc-timeout.test.tsbun run --cwd ts/packages/client buildcd ts && bun run check:tsgobun run --cwd ts/packages/client testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Ollama Embeddings Effectful Layer Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/embeddings/ollama.tsnow exposesmakeOllamaEmbeddingsEffectfor effectful config loading and service construction.OllamaEmbeddingsLivenow usesLayer.effectand maps config/load failures intoEmbeddingsErrorinstead of preconstructing the service withLayer.succeed.- The direct
makeOllamaEmbeddings(config)factory remains as a compatibility facade, while the canonicalprogramentrypoint preserves the provider tagged error channel. - Ollama response JSON parsing no longer uses a Promise type assertion.
- The focused embeddings tests now cover both direct factory use and the
effectful
OllamaEmbeddingsLivelayer.
- Verification:
bunx --bun vitest run src/__tests__/ollama-embeddings.test.tsbun run --cwd ts/packages/flow buildcd ts && bun run check:tsgobun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run test
2026-06-02: FalkorDB Scoped Client Lifecycle Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/storage/triples/falkordb.tsandts/packages/flow/src/query/triples/falkordb.tsnow model FalkorDB client acquisition withEffect.acquireRelease.- FalkorDB Live layers now use
Layer.effectand own Redis client disconnect finalizers through the layer scope. - Direct Promise compatibility factories and direct service factories now
bracket each operation with scoped acquisition instead of hiding mutable
Effect.cachedconnection slots. - Legacy
makeTriplesStoreServiceandmakeTriplesQueryServiceprovider hooks now acquire scoped FalkorDB services and map acquisition failures toProcessorLifecycleError; modernprogramentrypoints preserve the FalkorDB tagged layer error type. - FalkorDB query row field extraction now uses
effect/Predicatenarrowing instead of record/string type assertions. - New lifecycle tests use fake clients/graphs to prove connect on acquire and disconnect on scope close for both triples store and triples query.
- Verification:
bunx --bun vitest run src/__tests__/falkordb-lifecycle.test.tsbun run --cwd ts/packages/flow buildcd ts && bun run checkbun run --cwd ts/packages/flow testcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Qdrant Config, Schema, And Fakeable Construction Slice
- Status: migrated and root-verified.
- Completed:
- Added
ts/packages/flow/src/qdrant/client.tsas the narrow fakeable Qdrant surface used by graph/doc embedding store/query modules. - Graph and document Qdrant store/query constructors now create clients
through
Effect.try, load Qdrant config in Effect, and map config/client failures into their existingS.TaggedErrorClasserrors. - Graph and document query payload extraction now uses
Schema.decodeUnknownEffect(...).pipe(Effect.option)and skips malformed Qdrant payloads without type assertions. - Qdrant graph/doc query Live layers and graph store Live layer now use
Layer.effectinstead of preconstructing services withLayer.succeed. - Legacy graph store/query/doc query processor providers now acquire Qdrant
services with named
Effect.fnproviders and map startup failures toProcessorLifecycleError. - The installed Qdrant client still has no public close/disconnect method,
so no
Effect.acquireReleasefinalizer was added for Qdrant.
- Added
- Verification:
bunx --bun vitest run src/__tests__/qdrant-embeddings.test.tsbun run --cwd ts/packages/flow buildcd ts && bun run check:tsgobun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Text Completion Provider Stream Helper Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/model/text-completion/common.tsnow exposesstreamTextCompletionChunks, an Effect-native helper built onStream.fromAsyncIterable,Ref,Stream.filterMap/Result, and a final token chunk append.- OpenAI, Azure OpenAI, OpenAI-compatible, Mistral, Ollama, and Claude
streaming providers now share the helper instead of each hand-rolling
Stream.unfoldplusiterator.nextloops. - Mistral non-streaming and streaming content normalization now uses
effect/PredicateandOptionnarrowing throughtextFromContent, removing the priorcontent as stringassertion. - The helper uses the installed Effect beta's
Option.fromNullishOrand Result-shapedStream.filterMapAPI, verified bycheck:tsgo. ts/packages/flow/src/__tests__/text-completion-common.test.tscovers token accumulation/final chunk emission and non-string content narrowing.
- Remaining:
- Full Effect AI provider swaps still need parity tests first; current OpenAI and Azure behavior is Chat Completions based, no installed Azure/Mistral/Ollama Effect AI provider exists, and Anthropic needs explicit text/token/streaming/rate-limit parity coverage before replacement.
- Verification:
bunx --bun vitest run src/__tests__/text-completion-common.test.tscd ts && bun run check:tsgobun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Request-Response Queue Stream Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/base/src/messaging/runtime.tsnow waits for accepted request-response replies by converting the responseQueueto aStream.fromQueue.- Recipient filtering now uses
Stream.filterMapEffectwithResultto skip partial responses until the recipient returnstrue. Stream.runHeadreplaces the priorwhile (true)/Queue.takeloop and preserves the existing timeout behavior around request-response calls.ts/packages/base/src/__tests__/messaging-runtime.test.tsnow covers recipient filtering across partial and final responses.
- Remaining:
- The two remaining production
while (matches areagent/react/parser.tsline-buffer parsing andcli/src/commands/util.tsCommander parent traversal; neither is async polling or resource ownership.
- The two remaining production
- Verification:
bunx --bun vitest run src/__tests__/messaging-runtime.test.tsbun run --cwd ts/packages/base buildbun run --cwd ts/packages/base testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Gateway RPC WebSocket Cause Handling Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/flow/src/gateway/server.tsnow handles RPC WebSocket program defects and interruptions inside the Effect pipeline withEffect.sandbox,Effect.catch, andCause.pretty.- The previous Promise
.catch(...)aroundEffect.runPromise(...), plus the nestedEffect.runPromiseused only for logging and socket close, is removed. - The outer
Effect.runPromiseremains as the Fastify WebSocket host boundary.
- Verification:
cd ts && bun run check:tsgobun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Client RPC Acquisition Cause Tap Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/client/src/socket/effect-rpc-client.tsnow observes runtime/client acquisition failures withEffect.tapCauseandCause.pretty.- Removed the Promise
.catch(...)that only updated local connection state afterruntime.runPromise(TrustGraphRpcClientService). - Removed the local
errorMessagehelper and its message-field assertion. - Public
dispatch,dispatchStream, andclosePromise facades remain compatibility boundaries.
- Verification:
cd ts && bun run check:tsgobun run --cwd ts/packages/client buildbun run --cwd ts/packages/client testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Client Socket Close Effect Boundary Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/client/src/socket/trustgraph-socket.tsnow wrapsrpc.close()withEffect.tryPromiseinside the publicclose(): voidfacade.- Close failures are mapped to the existing tagged
TrustGraphSocketErrorshape and logged throughEffect.catchinstead of a Promise.catch. - The remaining client socket Promise
.catchmatches were streaming callback compatibility bridges and are now handled by the follow-up slice.
- Verification:
cd ts && bun run check:tsgobun run --cwd ts/packages/client buildbun run --cwd ts/packages/client testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Client Streaming Callback Effect Boundary Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/client/src/socket/trustgraph-socket.tsnow routes the legacyagent,graphRagStreaming, anddocumentRagStreamingcallback request failures throughrunLegacyStreamingRequest.runLegacyStreamingRequestusesEffect.tryPromiseto map failures into taggedTrustGraphSocketErrorvalues, then usesEffect.catchto invoke the public legacy error callback.- Production
trustgraph-socket.tsno longer has Promise.catchmatches; remaining matches in that file areEffect.catchonly. - Rechecked the PubSub replacement question against Effect v4 source:
Effect's native
PubSubis an in-process async hub over Effect queues. TrustGraph'sPubSubBackendremains the broker adapter boundary for NATS/Pulsar-style topics, subscriptions, acknowledgement, schema codecs, and backend lifecycle.
- Verification:
cd ts && bun run check:tsgobun run --cwd ts/packages/client buildbun run --cwd ts/packages/client testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Text Completion Provider Effectful Layer Slice
- Status: migrated and root-verified.
- Completed:
- Added shared
makeTextCompletionLayerfor constructingLlmfrom an effectfulLlmProvider. - Added
makeOpenAIProviderEffect,makeOpenAICompatibleProviderEffect,makeAzureOpenAIProviderEffect,makeClaudeProviderEffect,makeMistralProviderEffect, andmakeOllamaProviderEffect. - Processor
program.layerdefinitions now useLayer.effectvia the shared helper instead of constructing providers insideLayer.succeed. - Provider object assembly is split into pure
makeXProviderFromClienthelpers so Promise-returning provider methods remain external compatibility facades and do not triggereffect(runEffectInsideEffect). - Added tests for explicit provider config, shared
Llmlayer provisioning, and tagged missing-config errors.
- Added shared
- Verification:
bunx --bun vitest run src/__tests__/text-completion-providers.test.ts src/__tests__/text-completion-common.test.tsbun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow testcd ts && bun run check:tsgocd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Gateway Dispatcher Ownership And Serialization Slice
- Status: migrated and root-verified.
- Completed:
makeDispatcherManagernow tracks whether it owns the pubsub backend and no longer closes injectedPubSubBackendinstances onstop().publishToTopicnow usesEffect.acquireUseReleaseso the one-shot producer is closed even whensendfails.- Gateway dispatch paths now call
translateRequestEffectandtranslateResponseEffect, which wrap serialization withEffect.tryand return taggedDispatchSerializationErrorfailures. - Streaming dispatch recipients are named
Effect.fncallbacks, satisfying strict Effect diagnostics while preserving responder behavior. - Tests cover injected backend ownership, typed serialization failure before requestor startup, and producer close on send failure.
- Verification:
bunx --bun vitest run src/__tests__/gateway-dispatcher.test.tsbun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow testcd ts && bun run check:tsgocd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Base Producer Scoped Runtime Slice
- Status: migrated and root-verified.
- Completed:
- Kept
PubSubBackendas the broker adapter boundary; Effect nativePubSubremains an in-process primitive and is not a replacement for broker-backed topics, subscriptions, acknowledgements, codecs, or backend lifecycle. - Reworked
makeProducerso the legacy Promise facade allocates producers throughmakeEffectProducerFromPubSubinside a closeableScope. stop()now flushes the Effect producer and closes the scope with the registered producer finalizer, including the flush-failure path.- Added focused producer facade coverage for send routing, idempotent stop, tagged not-started lifecycle errors, and close-on-flush-failure behavior.
- Kept
- Verification:
cd ts && bun run check:tsgobun run --cwd ts/packages/base buildcd ts/packages/base && bunx --bun vitest run src/__tests__/producer.test.tsbun run --cwd ts/packages/base testcd ts && bun run checkcd ts && bun run buildcd ts && bun run test
2026-06-02: NATS Typed Boundary Slice
- Status: migrated and root-verified.
- Completed:
- Replaced the dynamic header import inside
makeNatsProducerwith the static NATSheadersexport. - Wrapped publish header construction in
Effect.try, so invalid header names/values fail as taggedPubSubErrorvalues instead of defects. - Wrapped NATS
ack()andnak()calls inEffect.try, preserving the existing wrong-message guard and mapping thrown acknowledgement failures into taggedPubSubErrors. - Added a mocked NATS backend test covering invalid publish headers and
thrown ack/nak failures through the public
makeNatsBackendpath.
- Replaced the dynamic header import inside
- Verification:
cd ts && bun run check:tsgocd ts/packages/base && bunx --bun vitest run src/__tests__/nats-backend.test.tsbun run --cwd ts/packages/base buildbun run --cwd ts/packages/base testcd ts && bun run checkcd ts && bun run buildcd ts && bun run test
2026-06-02: NATS Selective 404 Slice
- Status: migrated and root-verified.
- Completed:
- Added an internal
NatsLookupErrortagged error to preserve lookup causes without leavingunknownin Effect error channels. - Stream creation now happens only when
manager.streams.info()fails with a NATS JetStream 404/missing-resource error. - Durable consumer creation now happens only when
js.consumers.get()fails with a NATS JetStream 404/missing-resource error. - Added mocked NATS tests proving 404 lookups create resources while permission-style lookup failures do not.
- Added an internal
- Verification:
cd ts && bun run check:tsgocd ts/packages/base && bunx --bun vitest run src/__tests__/nats-backend.test.tsbun run --cwd ts/packages/base buildbun run --cwd ts/packages/base testcd ts && bun run checkcd ts && bun run buildcd ts && bun run test
2026-06-02: Consumer Rate-Limit Retry Slice
- Status: migrated and root-verified.
- Completed:
- Added
rateLimitTimeoutMsto the Effect-native messaging runtime config, backed byTG_RATE_LIMIT_TIMEOUT_MSand the Python-compatible default of7_200_000ms. - Reworked legacy
makeConsumerretry handling to useSchedule.spaced, retry repeatedTooManyRequestsErrors, and fail with a taggedMessagingTimeoutErrorwhen the rate-limit timeout elapses. - Reworked
makeEffectConsumerFromPubSubhandler retry handling with the same schedule/timeout behavior while keeping handler failures in typed Effect error channels. - Added legacy and Effect-native tests for repeated rate-limit retry until success and negative acknowledgement after retry timeout.
- Added
- Verification:
cd ts && bun run check:tsgocd ts/packages/base && bunx --bun vitest run src/__tests__/consumer.test.ts src/__tests__/messaging-runtime.test.tsbun run --cwd ts/packages/base buildbun run --cwd ts/packages/base testcd ts && bun run checkcd ts && bun run buildcd ts && bun run test
2026-06-02: Consumer Concurrency Ownership Slice
- Status: migrated and root-verified.
- Completed:
makeEffectConsumerFromPubSubnow creates one backend consumer per concurrency worker rather than sharing a single backend consumer across parallel receive loops.- Consumer runtime
stopis idempotent viaRef.getAndSet, so explicitconsumer.stopand scope finalization do not double-close worker handles. - Added Effect-native runtime coverage proving
concurrency: 3creates and closes three independent backend consumers exactly once.
- Verification:
cd ts && bun run check:tsgocd ts/packages/base && bunx --bun vitest run src/__tests__/messaging-runtime.test.tsbun run --cwd ts/packages/base buildbun run --cwd ts/packages/base testcd ts && bun run checkcd ts && bun run buildcd ts && bun run test
2026-06-02: Request-Response Stop Signal Slice
- Status: migrated and root-verified.
- Completed:
makeEffectRequestResponseFromPubSubnow owns aDeferredstop signal for the lifetime of the scoped request-response runtime.request()races response waiting against that stop signal before applying the request timeout, so pending calls fail promptly when the runtime stops.stop()fails the stop signal with a taggedMessagingLifecycleErrorbefore interrupting the dispatch loop and closing the producer/consumer resources.- Flow PDF decoder and graph embeddings service error unions now include
MessagingLifecycleErrorbecause requestor failures can surface shutdown. - Added Effect-native runtime coverage proving a pending request fails with the tagged lifecycle error when the request-response runtime stops.
- Verification:
cd ts && bun run check:tsgocd ts/packages/base && bunx --bun vitest run src/__tests__/messaging-runtime.test.tsbun run --cwd ts/packages/base buildbun run --cwd ts/packages/base testbun run --cwd ts/packages/flow buildbun run --cwd ts/packages/flow testcd ts && bun run checkcd ts && bun run buildcd ts && bun run test
2026-06-02: Legacy Consumer Facade Slice
- Status: migrated and root-verified.
- Completed:
makeConsumeris now a Promise compatibility facade overmakeEffectConsumerFromPubSubinstead of owning a separate mutable backend,runningflag, retry loop, and directBackendConsumer.receive.- The facade uses a module
ManagedRuntime,Scope.make,Scope.provide, andScope.closeto keep publicstart()/stop()Promises at the boundary while the actual consumer lifetime stays scoped. - Legacy Promise handlers are adapted with
Effect.tryPromiseand preserveTooManyRequestsErroras a typed retry signal. makeEffectConsumerFromPubSubnow forks workers withEffect.forkScoped, so a caller-owned scope keeps workers alive until stop/finalization.consumer.test.tsno longer encodesstart()as the blocking consume-loop join; it waits for observable handler/ack/nak effects and then stops the scoped consumer.
- Verification:
cd ts/packages/base && bunx --bun vitest run src/__tests__/consumer.test.tscd ts/packages/base && bunx --bun vitest run src/__tests__/messaging-runtime.test.ts src/__tests__/flow-spec-runtime.test.tscd ts && bun run check:tsgobun run --cwd ts/packages/base buildbun run --cwd ts/packages/base testcd ts && bun run checkcd ts && bun run buildcd ts && bun run testgit diff --check
2026-06-02: Workbench Theme KeyValueStore Slice
- Status: migrated and root-verified.
- Completed:
themeClassAtomno longer writes the legacytg-themelocalStorage mirror. Theme persistence stays in the canonicaltrustgraph-workbench-theme-v1Atom.kvsentry backed byBrowserKeyValueStore.layerLocalStorage.- The pre-paint host script in
index.htmlnow restores the canonical JSON-encoded theme key before React mounts, withtg-themeretained only as a legacy fallback. - Workbench QA now asserts that changing theme writes the canonical key and
does not recreate
tg-theme.
- Verification:
cd ts && bun run checkcd ts && bun run workbench:qacd ts && bun run buildcd ts && bun run testcd ts && bun run lint
2026-06-02: Effect AI LanguageModel Adapter Slice
- Status: migrated and package-verified.
- Completed:
- Added
makeLanguageModelProvider, a bridge fromeffect/unstable/ai/LanguageModelinto the existing TrustGraphLlmProvidercontract. - Covered non-streaming text/token mapping, streaming text/final-token
mapping, and Effect AI rate/quota failure mapping with fake
LanguageModeltests. - Kept concrete provider swaps deferred until provider-specific parity is proven.
- Added
- Verification:
cd ts && bun run check:tsgocd ts/packages/flow && bunx --bun vitest run src/__tests__/text-completion-common.test.ts src/__tests__/text-completion-providers.test.ts
2026-06-02: Native Request/Response PubSub Fanout Slice
- Status: migrated and package-verified.
- Completed:
- Replaced the request/response runtime's hand-managed
Map<string, Queue>response fanout with nativeeffect/PubSub. - Each request subscribes before sending, consumes through
Stream.fromSubscription, filters by response id, and releases the subscription at scope exit. - Kept
PubSubBackendas the broker boundary because Effect native PubSub is in-process only and does not provide NATS topics, ack/nack, durable subscriptions, schema codecs, or backend lifecycle.
- Replaced the request/response runtime's hand-managed
- Verification:
cd ts && bun run check:tsgocd ts/packages/base && bunx --bun vitest run src/__tests__/messaging-runtime.test.ts src/__tests__/request-response.test.ts src/__tests__/flow-spec-runtime.test.ts
2026-06-02: Claude Effect AI Provider Slice
- Status: migrated and package-verified.
- Completed:
- Replaced the direct
@anthropic-ai/sdkprovider implementation with@effect/ai-anthropicAnthropicLanguageModelplus the sharedmakeLanguageModelProvideradapter. - Preserved
CLAUDE_KEY, default model, temperature, max output, processor, and publicLlmProvidercompatibility behavior. - Added Claude provider config coverage for
CLAUDE_KEYenv fallback and the missing-key tagged config error. - Removed the direct
@anthropic-ai/sdkdependency and its lockfile entries from@trustgraph/flow.
- Replaced the direct
- Verification:
cd ts && bun run check:tsgocd ts/packages/flow && bunx --bun vitest run src/__tests__/text-completion-providers.test.ts src/__tests__/text-completion-common.test.tscd ts/packages/flow && bun run buildcd ts && bun run checkcd ts && bun run buildcd ts && bun run testcd ts && bun run lintgit diff --check
2026-06-02: RPC Dispatch Tagged Error Slice
- Status: migrated and package-verified.
- Completed:
- Replaced the remaining production
S.ErrorClassusage in the Flow gateway RPC contract withS.TaggedErrorClass. - Normalized the client-side RPC
DispatchErrorcounterpart to the same tagged-error schema shape so both wire contract copies stay aligned. - Closed the scratch-note
S.ErrorClassfinding for production code. The remaining plainnew Errormatches are test-only helpers or external host-boundary simulations.
- Replaced the remaining production
- Verification:
cd ts && bun run check:tsgocd ts/packages/flow && bunx --bun vitest run src/__tests__/gateway-dispatcher.test.tscd ts/packages/client && bunx --bun vitest run src/__tests__/rpc-timeout.test.tscd ts && bun run checkcd ts && bun run buildcd ts && bun run testcd ts && bun run lintgit diff --check
2026-06-02: Effect Metrics Prometheus Slice
- Status: migrated and root-verified.
- Completed:
- Replaced the
@trustgraph/baseprom-clientmetric wrappers with Effect-nativeMetric.counter,Metric.histogram, andeffect/unstable/observabilityPrometheusMetrics.format. - Kept the existing Prometheus metric names and gateway
/api/v1/metricsscrape boundary while removing the directprom-clientdependency and lockfile entries. - Changed producer metric recording from a sync callback to an Effect value that runs inside the producer send pipeline.
- Added isolated metric-registry tests for producer and consumer Prometheus formatting.
- Replaced the
- Verification:
cd ts && bun run check:tsgocd ts/packages/base && bunx --bun vitest run src/__tests__/metrics-effect.test.ts src/__tests__/producer.test.ts src/__tests__/messaging-runtime.test.tscd ts/packages/base && bun run buildcd ts/packages/base && bun run testcd ts/packages/flow && bun run buildcd ts/packages/flow && bunx --bun vitest run src/__tests__/gateway-dispatcher.test.tscd ts && bun run checkcd ts && bun run buildcd ts && bun run testcd ts && bun run lintgit diff --check
2026-06-02: MCP Effect Stdio Entrypoint Slice
- Status: migrated and root-verified.
- Completed:
- Added an Effect-native stdio layer and process entrypoint with
McpServer.layerStdio,NodeStdio.layer, andNodeRuntime.runMain. - Reused the same
TrustGraphMcpToolkitLivepath for HTTP and stdio through a shared toolkit-layer helper. - Kept the legacy SDK/Zod stdio export as a compatibility surface until
protocol-level
tools/listandtools/callparity tests prove it can be flipped or removed. - Added focused coverage that the Effect toolkit names remain stable and the stdio layer/entrypoint are exported.
- Updated the MCP test script to ignore compiled
dist/**output so root builds do not cause duplicate Vitest runs from generated tests.
- Added an Effect-native stdio layer and process entrypoint with
- Scratch-note triage:
- Metrics, in-process PubSub fanout, Claude Effect AI, RPC
S.TaggedErrorClass, and@effect/tsgosetup are already migrated. - Remaining valid scratch targets are MCP protocol parity/flip, Duration
config cleanup, Term/ClientTerm tagged-union matching, service
Effect.fnnormalization,@effect/cli, stream/RPC follow-ups, chunkingChunk, cores Promise APIs, and long-livedMap/Setstate.
- Metrics, in-process PubSub fanout, Claude Effect AI, RPC
- Verification:
cd ts/packages/mcp && bun run testcd ts/packages/mcp && bun run buildcd ts && bun run check:tsgocd ts && bun run buildcd ts && bun run testcd ts && bun run lintgit diff --check
2026-06-02: Term And ClientTerm Match Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/base/src/schema/primitives.tsnow exposesTermas a recursiveS.toTaggedUnion("type")schema and alignsTriple.gwith the Python/client wire contract as an optional graph string.ts/packages/flow/src/gateway/dispatch/serialize.tsnow defines compact client-term schemas withS.tag, decodes unknown term-shaped values with Schema/Option, and translates terms withMatch.discriminatorsExhaustive.- Removed the gateway serializer's native term switches and unsafe pass-through casts. Malformed known-tag objects now stay ordinary payload objects during deep translation instead of being cast into invalid terms.
- Replaced pure term helper switches in FalkorDB triples store/query,
Qdrant graph embeddings store, Graph RAG, agent tools, and workbench graph
utilities with exhaustive
Matchdiscriminators. - Added tests for named graph string decoding, nested compact/internal term round trips, and malformed known-tag payload preservation.
- Remaining:
- The client socket streaming term schema is still a local recursive union and can be centralized later if drift appears. It has no native term switch in the current scan.
- Operation dispatch switches in config, cores, librarian, and flow-manager are separate service-command refactors, not part of this term wire slice.
- Verification:
cd ts && bun run check:tsgocd ts/packages/base && bunx --bun vitest run src/__tests__/schema-effect.test.tscd ts/packages/flow && bunx --bun vitest run src/__tests__/gateway-dispatcher.test.ts src/__tests__/falkordb-lifecycle.test.ts src/__tests__/qdrant-embeddings.test.ts src/__tests__/retrieval-rag.test.tscd ts/packages/flow && bun run testcd ts/packages/base && bun run buildcd ts/packages/flow && bun run buildcd ts/packages/workbench && bun run buildcd ts && bun run buildcd ts && bun run testcd ts && bun run lintgit diff --check
2026-06-02: Messaging Runtime Duration Config Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/base/src/runtime/messaging-config.tsnow stores internal runtime timing fields asDuration.Durationinstead of number-shaped millisecond fields.loadMessagingRuntimeConfig()readsTG_*timing env vars withConfig.durationwhile preserving legacy bare-number millisecond env values through aConfig.number(...).map(Duration.millis)fallback.ts/packages/base/src/messaging/runtime.tsnow usesDurationvalues directly for sleeps, retry schedules, and timeout options. It converts back to milliseconds only at the brokerreceive(timeoutMs)boundary and for existing timeout error payloads.- Public compatibility options such as
receiveTimeoutMs,rateLimitRetryMs,rateLimitTimeoutMs, and requesttimeoutMsremain numeric millisecond inputs. - Tests now cover both legacy numeric env values and native Effect duration strings.
- Verification:
cd ts && bun run check:tsgocd ts/packages/base && bunx --bun vitest run src/__tests__/schema-effect.test.ts src/__tests__/messaging-runtime.test.ts src/__tests__/consumer.test.tscd ts/packages/base && bun run buildcd ts && bun run buildcd ts && bun run testcd ts && bun run lintgit diff --check
2026-06-02: Qdrant MutableHashSet Cache Slice
- Status: migrated and package-verified.
- Completed:
ts/packages/flow/src/storage/embeddings/qdrant-graph.tsnow usesMutableHashSet<string>for its known-collection cache.ts/packages/flow/src/storage/embeddings/qdrant-doc.tsnow usesMutableHashSet<string>inside its store effect shape instead of a nativeSet<string>.- Collection cache membership, insertion, and invalidation now use
MutableHashSet.has,MutableHashSet.add, andMutableHashSet.remove. - Qdrant tests now prove the graph store service avoids repeated collection existence checks while the collection is cached and checks again after delete invalidates the cache.
- Remaining:
- The document store's public direct facade still constructs a fresh store effect per operation, so its cache lifetime is per constructed effect shape. Changing that facade to own a shared runtime/service is a separate lifecycle design slice, not part of this collection primitive migration.
- Verification:
cd ts && bun run check:tsgocd ts/packages/flow && bunx --bun vitest run src/__tests__/qdrant-embeddings.test.tscd ts/packages/flow && bun run buildcd ts && bun run buildcd ts && bun run testcd ts && bun run lintgit diff --check
2026-06-04: Workbench Match Dispatch Slice
- Status: migrated and root-verified.
- Completed:
ts/packages/workbench/src/qa/mock-api.tsnow dispatches mock RPC service requests witheffect/Matchand keeps the existing unknown-service{}fallback.ts/packages/workbench/src/atoms/workbench.tsnow dispatches chat submit behavior forgraph-rag,document-rag, andagentthroughMatch.exhaustiveover theChatModeunion.- The strict scan for native
switchstatements ints/packagesis clean:rg -n "\bswitch\s*\(" ts/packages --glob '*.ts' --glob '*.tsx'returns no matches.
- Verification:
cd ts && bun run check:tsgocd ts && bun run buildcd ts && bun run testcd ts && bun run lintgit diff --check
Subagent Findings To Preserve
- MCP/workbench:
- Make the Effect MCP server the canonical implementation. An Effect
McpServer.layerStdioentrypoint now exists; the old stdio server should remain only as compatibility until protocol-leveltools/listandtools/callparity is proved. Do not deleteserver.tsuntil that parity coverage exists, with special attention totext_completionbehavior. - Workbench BaseApi atoms can move toward
AtomRpcorAtomHttpApiafter the client API is less Promise-first. - MCP env is now Config-backed; continue that policy for future MCP settings.
- Workbench persistent theme storage is now canonicalized through
Atom.kvs/BrowserKeyValueStore. Remaining workbenchlocalStoragematches are legacy migration fallbacks, QA assertions, or the pre-paint host script.
- Make the Effect MCP server the canonical implementation. An Effect
- Flow stateful services:
- Config service, KnowledgeCore service, FlowManager, and Librarian ref-backed state slices are complete. Follow-up service work should focus on scoped layers, schedules where polling semantics allow, and managed persistence providers rather than direct mutable service fields.
- Flow service startup facades now consistently use
ManagedRuntime, and local scripts should delegate torunMain()instead of adding local.catch(console.error/process.exit)wrappers. - Persistence IO should move toward
FileSystemorKeyValueStorewhere the installed beta has the needed provider surface.
- Base messaging/processors:
- Processor/flow Promise compatibility now uses
ManagedRuntime; keepNodeRuntimeonly for processrunMain()entrypoints. - Subscriber queues/maps and dynamic flow state should continue moving
toward
Queue,Deferred,SynchronizedRef,Schedule, and scoped layers. - The legacy
messaging/subscriber.tsasync queue/fanout implementation is removed. Use nativeeffect/PubSubfor future in-process fanout, while keepingPubSubBackendfor broker-backed messaging. - The legacy producer and consumer facades now delegate to scoped Effect
runtime factories. Public
start()/send()/stop()Promises remain compatibility boundaries. - NATS header construction, ack/nak operations, and lookup create-on-missing
behavior now stay typed.
PubSubBackendremains an intentional broker adapter contract rather than a directeffect/PubSubreplacement target. - Consumer rate-limit retry timeout behavior is now wired in both legacy and Effect-native consumer paths. Effect-native consumer concurrency now owns one backend consumer per worker, and request-response pending shutdown now fails through a tagged lifecycle error. The legacy consumer facade blocking shape is complete; do not reopen it unless a public API compatibility issue appears.
- Existing constructor shims preserve callable-plus-newable public exports; removing them needs a public API split or real class redesign.
- Typed string registries in
Flownow have Schema-backed parameter specs and typed producer/requestor spec-object accessors. New service handlers should hoist spec objects and use those accessors; bare string accessors remain compatibility escapes. - Base metrics are now Effect-native and Prometheus-formatted through
PrometheusMetrics.format; do not reopenprom-clientunless a future scrape requirement cannot be represented by Effect metrics. - Numeric public timeout fields such as
timeoutMsremain compatibility surfaces. Internal runtime config withConfig.number(...Ms)is still a validConfig.duration/Durationcleanup target.
- Processor/flow Promise compatibility now uses
- Gateway/client:
EffectRpcClientnow owns its socket/RPC layer withManagedRuntime. Socket errors/JSON parsing now use tagged errors and Schema decoding. The remaining clientnewableFactoryassertions are documented as public API compatibility boundaries for this loop.- Gateway/client
DispatchErrorcontracts now useS.TaggedErrorClass; do not reopenS.ErrorClassunless a new production match appears. - Gateway
DispatchStreamnow uses Effect-native dispatcher streaming callbacks instead of nestedEffect.runPromiseWith, and client streaming facade callbacks now decode the legacy envelope through Schema before applying service-specific public callback semantics. - Gateway dispatcher ownership and serialization cleanup is complete: injected pubsub backends are not closed by the manager, one-shot producers are acquire/use/release bracketed, and serialization failures are typed Effect errors.
- Do not make
gateway/rpc-protocol.tsthe next cleanup target: it is a Fastify socket compatibility bridge while the public Effect RPC server layers require SocketServer or Effect HTTP routing. - WebSocket adapter host fallbacks now use
Result.tryand tagged adapter errors while preserving sync exports.
- RAG/providers/storage:
- RAG and agent requestor bridges are complete:
toPromiseRequestorhas no remainingts/packagesmatches. - Provider SDKs and storage clients should become managed resources where they have meaningful lifecycle.
- Ollama embeddings now has an effectful canonical layer. There is no installed Effect AI Ollama provider package, so future Ollama work should focus on local Effect wrappers/adapters rather than provider replacement.
- Full text-completion provider swaps need parity tests first. OpenAI and
Azure currently use Chat Completions while
@effect/ai-openaiis Responses API oriented, and no installed Azure/Mistral/Ollama Effect AI provider is available. Anthropic is the closest direct provider swap, but must preserve text, token counts, streaming final usage, and rate-limit mapping. The local provider layer-construction cleanup is complete; remaining provider work is adapter/parity work, notLayer.succeedcleanup. - The
effect/unstable/ai/LanguageModelto TrustGraphLlmProvideradapter baseline is complete, and Claude now uses@effect/ai-anthropicthrough that adapter. Direct OpenAI, Azure, and OpenAI-compatible swaps are no-ops until Responses-vs-Chat-Completions parity is proven. - FalkorDB scoped lifecycle is complete for triples query/store. Use the fakeable client/graph factory pattern from that slice for future storage client tests.
- Qdrant config/schema/fakeability is complete for graph/doc embedding
store/query modules. Qdrant still has no close/disconnect surface in the
installed client, so do not reopen it as an
acquireReleaseclose slice without new SDK evidence. - Shared text-completion stream iteration and the Mistral content assertion are
complete, and the Effect AI stream-part adapter now uses
effect/Match. The remaining provider-layer item is parity-backed Effect AI adapter work, not a direct SDK swap.
- RAG and agent requestor bridges are complete:
- Scratch-note follow-ups:
Term/ compact client term serialization is complete for base schema, gateway translation, and pure term helper switches. Future work should only reopen this if client socket schema drift appears or a hidden consumer needs a different named-graph shape.- Messaging runtime
Config.durationcleanup is complete. Internal runtime config usesDuration.Duration; public timeout compatibility inputs and broker receive/error payload boundaries remain numeric milliseconds. - Qdrant graph/doc known-collection caches now use
MutableHashSet<string>. Short-lived local traversal sets remain no-ops. - Gateway dispatcher static service registries, streaming membership, and
scoped requestor cache now use Effect
HashMap/HashSet; gateway term-bearing service membership sets now use EffectHashSettoo. - FlowManager
() => Effect.gen(...)factories are normalized toEffect.fn/Effect.fnUntraced. Sibling service factories still need a focused scan before treating them as valid migration targets. - ConfigService and KnowledgeCore operation dispatch now use
effect/MatchwithMatch.exhaustive; FlowManager and Librarian operation dispatch now useeffect/Matchwith runtime-preservingMatch.orElsefallbacks. - Native
switchstatements are now clean ints/packages; future branch drift should keep service dispatch oneffect/Matchor Schema tagged-union helpers. - Client RPC/BaseApi connection-state fanout now uses
effect/SubscriptionRef; remaining gateway/client P1 work is broader API design, not listener bookkeeping. - Long-lived
Map/Setstate in ref-backed services can move toward Effect collections later; local pure traversal maps/sets remain no-ops.
Ranked Findings
No-op: Broker Backend Adapter Boundary
- Status:
- Closed as a P0 rewrite item after the 2026-06-02 broker scout and the legacy consumer facade slice.
- TrustGraph evidence:
ts/packages/base/src/backend/types.tsts/packages/base/src/backend/nats.tsts/packages/base/src/backend/pubsub.tsts/packages/base/src/messaging/runtime.ts
- Effect primitives:
Layer,Scope,Stream,Schedule,Queue,Effect.acquireRelease, andEffect.tryPromise.
- Evidence:
BackendProducer,BackendConsumer, andPubSubBackendare the external Promise broker adapter contract.backend/pubsub.tswraps that contract in Effect throughContext.Service,Layer,Effect.tryPromise, and scoped finalizers.- NATS boundary failures, selective stream/consumer lookup recovery, producer sends, ack/nak, and close paths are typed with Effect wrappers.
- Producer, consumer, and request-response runtime ownership now live in scoped Effect factories.
- Rule:
- Keep
PubSubBackendas the compatibility adapter boundary; Effect nativePubSubremains in-process only. - Treat the producer Promise facade as a completed compatibility wrapper; avoid reopening it unless backend runtime changes require a narrower adapter.
- Keep NATS SDK boundary failures typed and avoid catch-all create-on-failure behavior. Future backend slices should move connection/stream state into scoped Effect services.
- Treat rate-limit retry timeout semantics as complete; next consumer slices should focus on blocking compatibility and backend/layer ownership, not retry policy.
- Treat Effect-native per-worker consumer ownership as complete; do not flag
makeEffectConsumerFromPubSubconcurrency for shared backend receive handles. - Treat request-response pending shutdown semantics as complete; do not flag
waitForResponsetimeout behavior for stopped runtimes. - Treat request-response in-process fanout as complete: response routing now
uses native
effect/PubSubsubscriptions instead of a hand-managed subscriber map. - Treat the legacy consumer facade as a completed compatibility wrapper over
makeEffectConsumerFromPubSub; do not flag blockingstart()semantics.
- Keep
No-op: Remaining Effect AI Provider Swaps
- TrustGraph evidence:
ts/packages/flow/src/model/text-completion/*.ts
- Effect primitives:
effect/unstable/ai/LanguageModel,effect/unstable/ai/EmbeddingModel, Effect AI OpenAI/Anthropic provider layers.
- Rewrite shape:
- Adapter baseline is complete:
makeLanguageModelProviderbridgesLanguageModelintoLlmProvider. - Claude migration is complete through
@effect/ai-anthropic. - Do not directly swap OpenAI, Azure, or OpenAI-compatible providers yet:
current TrustGraph code uses Chat Completions/local-server semantics while
@effect/ai-openaiis Responses API backed. - Do not directly swap Mistral or Ollama until an installed Effect provider package exists or a parity-backed local Effect wrapper is designed.
- Adapter baseline is complete:
- Tests:
- Provider parity for
LlmResult, final streaming chunk token counts, 429 mapping, missing-token config failures, and OpenAI-compatible local-server behavior.
- Provider parity for
No-op: Base Metrics Prometheus Wrapper
- Status:
- Closed as a scratch-note migration target by the Effect Metrics Prometheus slice.
- TrustGraph evidence:
ts/packages/base/src/metrics/prometheus.tsts/packages/flow/src/gateway/server.ts
- Effect primitives:
Metric.counter,Metric.histogram, andeffect/unstable/observabilityPrometheusMetrics.format.
- Rule:
- Keep the gateway Fastify route as the external scrape boundary, but record
TrustGraph metrics through Effect
Metricvalues. - Use a fresh
Metric.MetricRegistryin tests that assert exact scrape content.
- Keep the gateway Fastify route as the external scrape boundary, but record
TrustGraph metrics through Effect
Complete: Term And ClientTerm Tagged-Union Normalization
- TrustGraph evidence:
ts/packages/base/src/schema/primitives.tsts/packages/flow/src/gateway/dispatch/serialize.tsts/packages/client/src/socket/trustgraph-socket.ts
- Effect primitives:
S.toTaggedUnion(...).matchandeffect/Matchdiscriminator helpers.
- Rewrite shape:
- Add tagged-union helpers for internal
Termand compact client terms. - Replace serializer native switches with tagged-union matching or
Match.discriminatorsExhaustive. - Remove unsafe default pass-through casts while preserving compact
gstring compatibility.
- Add tagged-union helpers for internal
- Tests:
- Base schema tests now cover recursive terms and graph strings.
- Gateway dispatcher tests now cover all compact term variants, nested triples, compact graph strings, malformed known-tag payloads, and malformed client triple failures.
Complete: Messaging Runtime Duration Config Cleanup
- TrustGraph evidence:
ts/packages/base/src/runtime/messaging-config.tsts/packages/base/src/messaging/runtime.ts
- Effect primitives:
Config.duration,Duration.Duration, and existingDuration.milliscompatibility conversions.
- Rewrite shape:
- Change internal runtime config fields such as
receiveTimeoutMs,requestTimeoutMs,retryDelayMs, andrateLimitTimeoutMstoDuration.Duration. - Load env-backed values with
Config.durationwhile preserving Python-style millisecond defaults and public numeric compatibility options. - Keep external
timeoutMsoption names numeric in request/response, processor, and client boundaries unless their public API is deliberately changed.
- Change internal runtime config fields such as
- Tests:
- Base runtime config tests cover legacy millisecond env values and Effect duration string env values.
- Messaging runtime and consumer tests preserve retry and timeout behavior.
Complete: Qdrant Known-Collection MutableHashSet Cleanup
- TrustGraph evidence:
ts/packages/flow/src/storage/embeddings/qdrant-doc.tsts/packages/flow/src/storage/embeddings/qdrant-graph.ts
- Effect primitives:
MutableHashSetfromeffect.
- Rewrite shape:
- Replace long-lived
Set<string>known-collection caches withMutableHashSet<string>in Qdrant graph/doc embedding stores. - Keep short-lived local
Setvalues for pure query traversal or fixture assertions as no-op boundaries.
- Replace long-lived
- Tests:
- Qdrant embeddings tests prove graph cache hits skip repeated collection existence checks and deletion invalidates the cache.
P2: Canonicalize MCP Around The Effect Server
- Status:
- MCP now builds under strict tsgo, the stdio server has an Effect-backed
compatibility implementation, and an Effect
McpServer.layerStdioentrypoint exists.
- MCP now builds under strict tsgo, the stdio server has an Effect-backed
compatibility implementation, and an Effect
- Remaining shape:
- Keep the old SDK/Zod stdio compatibility surface for now.
- Prove
tools/listandtools/callparity before deleting any public entry point or droppingzod/server-side MCP SDK dependencies. - Pay special attention to
text_completion: legacy calls the TrustGraph gateway, while the Effect server currently uses an Effect AILanguageModel/OpenAI layer.
- Tests:
cd ts && bun run --cwd packages/mcp build- Root
cd ts && bun run check
No-op: Workbench Theme And Browser Storage Boundaries
- Status: migrated and documented no-op for remaining direct browser matches.
- Evidence:
settingsAtom,themeAtom,flowIdAtom, andconversationAtomare canonicalAtom.kvsentries backed byBrowserKeyValueStore.layerLocalStorage.- The only remaining
tg-themeproduction read is a legacy migration fallback. The pre-paintindex.htmlscript is a host boundary that restores the canonical key before React and Effect services mount. - Graph canvas DOM/class inspection remains render-only host behavior.
- Rule:
- Do not reopen workbench
localStorageas a backend/runtime blocker unless a canonicalAtom.kvsentry is bypassed or a legacy migration fallback can be intentionally removed.
- Do not reopen workbench
Recommended PR Order
- MCP protocol parity tests and legacy stdio flip/removal decision.
- Flow/client RPC stream API design beyond callback/Promise compatibility.
- Long-lived ref-backed
HashMapstate cleanup where clone helpers remain. - Sibling service
Effect.fnnormalization where arrow-returned generators still appear.
No-Op Rules
Do not flag these as rewrite blockers without additional proof:
- Promise-returning CLI actions, MCP SDK callbacks, client compatibility methods, and Fastify route handlers at true external boundaries. Boundary code still must map failures into typed errors or wire errors.
try/catchblocks at host/tool boundaries only when the catch maps into a typed error or a wire-contract error. Internal exception capture should useEffect.try,Effect.tryPromise, orResult.try.S.Class,S.TaggedErrorClass,Context.Service,Rpc.make, andHttpApi.makewhen they are required or idiomatic for the Effect API.- Plain
Mapusage for local pure transformations, such as graph utility construction, unless the state is long-lived mutable service state. - Plain synchronous loops for parsing or tree traversal are not Effect migration blockers unless they hide async polling, resource ownership, or callback scheduling.
- JSON stringification in tests or wire-contract fixtures. Production JSON encode/decode should prefer schema codecs when the encoded form can be preserved.
- Workbench pre-paint
index.htmlstorage reads are host-boundary code used before React and Effect services mount. Keep canonical workbench state inAtom.kvs/BrowserKeyValueStore, and treat old-key reads as migration fallbacks unless backward compatibility is intentionally dropped. - The old MCP SDK/Zod stdio server is compatibility code until an Effect stdio entrypoint plus parity tests prove the same public protocol behavior.
- Client
newableFactoryassertions that preserve vendored callable-plus-new API facades are compatibility boundaries unless the public constructor API is intentionally redesigned. - Base
AsyncProcessor,Flow, andFlowProcessorcallable-plus-newable export assertions are compatibility boundaries unless the public constructor API is intentionally redesigned. - TrustGraph
PubSubBackend/ backendPubSubservice is a broker adapter boundary for NATS/Pulsar-style topics, acknowledgement, schema codecs, and backend lifecycle. Effect's nativePubSubcan replace in-process fanout helpers, but not the distributed broker abstraction by itself. This was rechecked against/home/elpresidank/YeeBois/projects/beep-effect2/.repos/effect-v4/packages/effect/src/PubSub.ts, whose exported API is local publish/subscribe over Effect queues. - Request-response pending shutdown semantics are complete in
makeEffectRequestResponseFromPubSub: pending calls race response waiting against aDeferredstop signal and fail with taggedMessagingLifecycleError. - Legacy
makeConsumerfacade blocking-loop ownership is complete:start()now initializes scoped Effect consumers and returns after startup, whilestop()closes the native consumer scope. ts/packages/flow/src/gateway/rpc-protocol.tsis a Fastify socket compatibility bridge. Do not flag its internal connection maps/sets as a standalone replacement target until the gateway is ready to move onto Effect SocketServer or Effect HTTP routing.
Acceptance For Final Loop Completion
The overall playbook loop is complete only when:
- All remaining playbook signal matches are migrated or documented as no-op external-boundary cases with concrete evidence.
- No P0/P1/P2 migration item remains in this audit.
cd ts && bun run check,cd ts && bun run build,cd ts && bun run test, andgit diff --checkpass after the final migration slice.