Use Match for workbench dispatch

This commit is contained in:
elpresidank 2026-06-04 06:07:20 -05:00
parent dfc79bb050
commit e46fc64275
3 changed files with 44 additions and 27 deletions

View file

@ -1855,6 +1855,26 @@ Notes:
- `cd ts && bun run lint` - `cd ts && bun run lint`
- `git diff --check` - `git diff --check`
### 2026-06-04: Workbench Match Dispatch Slice
- Status: migrated and root-verified.
- Completed:
- `ts/packages/workbench/src/qa/mock-api.ts` now dispatches mock RPC
service requests with `effect/Match` and keeps the existing unknown-service
`{}` fallback.
- `ts/packages/workbench/src/atoms/workbench.ts` now dispatches chat submit
behavior for `graph-rag`, `document-rag`, and `agent` through
`Match.exhaustive` over the `ChatMode` union.
- The strict scan for native `switch` statements in `ts/packages` is clean:
`rg -n "\bswitch\s*\(" ts/packages --glob '*.ts' --glob '*.tsx'` returns
no matches.
- Verification:
- `cd ts && bun run check:tsgo`
- `cd ts && bun run build`
- `cd ts && bun run test`
- `cd ts && bun run lint`
- `git diff --check`
## Subagent Findings To Preserve ## Subagent Findings To Preserve
- MCP/workbench: - MCP/workbench:
@ -1982,6 +2002,9 @@ Notes:
- ConfigService and KnowledgeCore operation dispatch now use `effect/Match` - ConfigService and KnowledgeCore operation dispatch now use `effect/Match`
with `Match.exhaustive`; FlowManager and Librarian operation dispatch now with `Match.exhaustive`; FlowManager and Librarian operation dispatch now
use `effect/Match` with runtime-preserving `Match.orElse` fallbacks. use `effect/Match` with runtime-preserving `Match.orElse` fallbacks.
- Native `switch` statements are now clean in `ts/packages`; future branch
drift should keep service dispatch on `effect/Match` or Schema tagged-union
helpers.
- Client RPC/BaseApi connection-state fanout now uses - Client RPC/BaseApi connection-state fanout now uses
`effect/SubscriptionRef`; remaining gateway/client P1 work is broader API `effect/SubscriptionRef`; remaining gateway/client P1 work is broader API
design, not listener bookkeeping. design, not listener bookkeeping.
@ -2167,7 +2190,7 @@ Notes:
## Recommended PR Order ## Recommended PR Order
1. MCP protocol parity tests and legacy stdio flip/removal decision. 1. MCP protocol parity tests and legacy stdio flip/removal decision.
2. Flow/client RPC stream and remaining service operation `Match` follow-ups. 2. Flow/client RPC stream API design beyond callback/Promise compatibility.
3. Long-lived ref-backed `HashMap` state cleanup where clone helpers remain. 3. Long-lived ref-backed `HashMap` state cleanup where clone helpers remain.
4. Sibling service `Effect.fn` normalization where arrow-returned generators 4. Sibling service `Effect.fn` normalization where arrow-returned generators
still appear. still appear.

View file

@ -2,7 +2,7 @@ import { Clipboard as BrowserClipboard } from "@effect/platform-browser";
import * as BrowserHttpClient from "@effect/platform-browser/BrowserHttpClient"; import * as BrowserHttpClient from "@effect/platform-browser/BrowserHttpClient";
import * as BrowserKeyValueStore from "@effect/platform-browser/BrowserKeyValueStore"; import * as BrowserKeyValueStore from "@effect/platform-browser/BrowserKeyValueStore";
import { BaseApi, type ConnectionState, type DocumentMetadata, type ExplainEvent, type StreamingMetadata, type Term, type Triple } from "@trustgraph/client"; import { BaseApi, type ConnectionState, type DocumentMetadata, type ExplainEvent, type StreamingMetadata, type Term, type Triple } from "@trustgraph/client";
import { Cause, Clock, Context, Effect, Layer, Metric, Option, Random, Schema as S } from "effect"; import { Cause, Clock, Context, Effect, Layer, Match, Metric, Option, Random, Schema as S } from "effect";
import * as Otlp from "effect/unstable/observability/Otlp"; import * as Otlp from "effect/unstable/observability/Otlp";
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
import * as Atom from "effect/unstable/reactivity/Atom"; import * as Atom from "effect/unstable/reactivity/Atom";
@ -1576,14 +1576,14 @@ export const submitMessageAtom = commandAtom<{ input: string }, void>(
explainEvents.push(event); explainEvents.push(event);
}; };
switch (chatMode) { Match.value(chatMode).pipe(
case "graph-rag": Match.when("graph-rag", () => {
flow.graphRagStreaming(trimmed, onChunk, onError, undefined, collection, onExplain); flow.graphRagStreaming(trimmed, onChunk, onError, undefined, collection, onExplain);
break; }),
case "document-rag": Match.when("document-rag", () => {
flow.documentRagStreaming(trimmed, onChunk, onError, undefined, collection, onExplain); flow.documentRagStreaming(trimmed, onChunk, onError, undefined, collection, onExplain);
break; }),
case "agent": Match.when("agent", () => {
flow.agent( flow.agent(
trimmed, trimmed,
(chunk, complete) => { (chunk, complete) => {
@ -1633,8 +1633,9 @@ export const submitMessageAtom = commandAtom<{ input: string }, void>(
onExplain, onExplain,
collection, collection,
); );
break; }),
} Match.exhaustive,
);
}), }),
); );

View file

@ -1,5 +1,5 @@
import { makeBaseApiWithRpc, type BaseApi, type DocumentMetadata, type ProcessingMetadata, type StreamingMetadata, type Triple } from "@trustgraph/client"; import { makeBaseApiWithRpc, type BaseApi, type DocumentMetadata, type ProcessingMetadata, type StreamingMetadata, type Triple } from "@trustgraph/client";
import { Clock, Effect, Option, Schema as S } from "effect"; import { Clock, Effect, Match, Option, Schema as S } from "effect";
type ConfigValues = Record<string, Record<string, unknown>>; type ConfigValues = Record<string, Record<string, unknown>>;
@ -284,22 +284,15 @@ function addDocument(state: MockState, metadata: DocumentMetadata): DocumentMeta
} }
function dispatchRequest(state: MockState, service: string, request: Record<string, unknown>, flow: string | undefined): unknown { function dispatchRequest(state: MockState, service: string, request: Record<string, unknown>, flow: string | undefined): unknown {
switch (service) { return Match.value(service).pipe(
case "flow": Match.when("flow", () => dispatchFlow(state, request)),
return dispatchFlow(state, request); Match.when("config", () => dispatchConfig(state, request)),
case "config": Match.when("librarian", () => dispatchLibrarian(state, request)),
return dispatchConfig(state, request); Match.when("knowledge", () => dispatchKnowledge(state, request)),
case "librarian": Match.when("collection-management", () => dispatchCollections(state, request)),
return dispatchLibrarian(state, request); Match.when("triples", () => dispatchTriples(state, request, flow)),
case "knowledge": Match.orElse(() => ({})),
return dispatchKnowledge(state, request); );
case "collection-management":
return dispatchCollections(state, request);
case "triples":
return dispatchTriples(state, request, flow);
default:
return {};
}
} }
function dispatchFlow(state: MockState, request: Record<string, unknown>): unknown { function dispatchFlow(state: MockState, request: Record<string, unknown>): unknown {