From 98dd9e4afbda5f10111257572354afb77672b8bc Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Tue, 5 May 2026 19:21:31 -0400 Subject: [PATCH] refactor(sdk): add runtime bridge seam for query dispatch --- sdk/src/gsd-tools.ts | 20 ++++----- sdk/src/query-gsd-tools-runtime.ts | 14 +++++-- sdk/src/query-runtime-bridge.ts | 66 ++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 sdk/src/query-runtime-bridge.ts diff --git a/sdk/src/gsd-tools.ts b/sdk/src/gsd-tools.ts index a6fdbbd90..17f019766 100644 --- a/sdk/src/gsd-tools.ts +++ b/sdk/src/gsd-tools.ts @@ -15,13 +15,12 @@ import type { InitNewProjectInfo, PhaseOpInfo, PhasePlanIndex, RoadmapAnalysis } import type { GSDEventStream } from './event-stream.js'; import { toToolsErrorFromUnknown } from './query-tools-error-factory.js'; import { GSDToolsError } from './gsd-tools-error.js'; -import { resolveQueryCommand, type QueryCommandResolution } from './query/query-command-resolution-strategy.js'; -import { QueryExecutionPolicy } from './query-execution-policy.js'; -import { QueryNativeHotpathAdapter } from './query-native-hotpath-adapter.js'; +import type { QueryCommandResolution } from './query/query-command-resolution-strategy.js'; import { resolveGsdToolsPath } from './query-gsd-tools-path.js'; import { createGSDToolsRuntime } from './query-gsd-tools-runtime.js'; import { QueryCommandExecutor } from './query-command-executor.js'; import { QueryHotpathMethods } from './query-hotpath-methods.js'; +import { QueryRuntimeBridge } from './query-runtime-bridge.js'; export { GSDToolsError } from './gsd-tools-error.js'; @@ -35,10 +34,8 @@ export class GSDTools { private readonly gsdToolsPath: string; private readonly timeoutMs: number; private readonly workstream?: string; - private readonly registry: ReturnType['registry']; + private readonly bridge: QueryRuntimeBridge; private readonly preferNativeQuery: boolean; - private readonly executionPolicy: QueryExecutionPolicy; - private readonly nativeHotpathAdapter: QueryNativeHotpathAdapter; private readonly commandExecutor: QueryCommandExecutor; private readonly hotpathMethods: QueryHotpathMethods; @@ -76,12 +73,10 @@ export class GSDTools { execRawFallback: (legacyCommand, legacyArgs) => this.execRaw(legacyCommand, legacyArgs), }); - this.registry = runtime.registry; - this.executionPolicy = runtime.executionPolicy; - this.nativeHotpathAdapter = runtime.nativeHotpathAdapter; + this.bridge = runtime.bridge; this.commandExecutor = new QueryCommandExecutor({ nativeMatch: (command, args) => this.nativeMatch(command, args), - execute: async (input) => this.executionPolicy.execute({ + execute: async (input) => this.bridge.execute({ legacyCommand: input.legacyCommand, legacyArgs: input.legacyArgs, registryCommand: input.registryCommand, @@ -89,7 +84,6 @@ export class GSDTools { mode: input.mode, projectDir: this.projectDir, workstream: this.workstream, - preferNativeQuery: this.shouldUseNativeQuery(), }), }); @@ -104,7 +98,7 @@ export class GSDTools { } private nativeMatch(command: string, args: string[]): QueryCommandResolution | null { - return resolveQueryCommand(command, args, this.registry); + return this.bridge.resolve(command, args); } private async dispatchNativeHotpath( @@ -115,7 +109,7 @@ export class GSDTools { mode: 'json' | 'raw', ): Promise { return this.executeWithToolsError(legacyCommand, legacyArgs, () => - this.nativeHotpathAdapter.dispatch( + this.bridge.dispatchHotpath( legacyCommand, legacyArgs, registryCommand, diff --git a/sdk/src/query-gsd-tools-runtime.ts b/sdk/src/query-gsd-tools-runtime.ts index c86efc6cd..b29384d9d 100644 --- a/sdk/src/query-gsd-tools-runtime.ts +++ b/sdk/src/query-gsd-tools-runtime.ts @@ -7,11 +7,10 @@ import { QueryNativeDirectAdapter } from './query-native-direct-adapter.js'; import { QueryNativeHotpathAdapter } from './query-native-hotpath-adapter.js'; import { formatQueryRawOutput } from './query-raw-output-projection.js'; import { createQueryNativeErrorFactory, createQueryToolsErrorFactory } from './query-tools-error-factory.js'; +import { QueryRuntimeBridge } from './query-runtime-bridge.js'; export interface GSDToolsRuntime { - registry: ReturnType; - executionPolicy: QueryExecutionPolicy; - nativeHotpathAdapter: QueryNativeHotpathAdapter; + bridge: QueryRuntimeBridge; } export function createGSDToolsRuntime(opts: { @@ -65,5 +64,12 @@ export function createGSDToolsRuntime(opts: { opts.execRawFallback, ); - return { registry, executionPolicy, nativeHotpathAdapter }; + const bridge = new QueryRuntimeBridge( + registry, + executionPolicy, + nativeHotpathAdapter, + opts.shouldUseNativeQuery, + ); + + return { bridge }; } diff --git a/sdk/src/query-runtime-bridge.ts b/sdk/src/query-runtime-bridge.ts new file mode 100644 index 000000000..e249214a6 --- /dev/null +++ b/sdk/src/query-runtime-bridge.ts @@ -0,0 +1,66 @@ +import type { QueryRegistry } from './query/registry.js'; +import type { TransportMode } from './gsd-transport-policy.js'; +import type { QueryCommandResolution } from './query/query-command-resolution-strategy.js'; +import { resolveQueryCommand } from './query/query-command-resolution-strategy.js'; +import { QueryExecutionPolicy } from './query-execution-policy.js'; +import { QueryNativeHotpathAdapter } from './query-native-hotpath-adapter.js'; + +export interface RuntimeBridgeExecuteInput { + legacyCommand: string; + legacyArgs: string[]; + registryCommand: string; + registryArgs: string[]; + mode: TransportMode; + projectDir: string; + workstream?: string; +} + +/** + * SDK Runtime Bridge Module. + * Owns dispatch routing through the execution policy seam and hotpath/native fallback behavior. + */ +export class QueryRuntimeBridge { + constructor( + private readonly registry: QueryRegistry, + private readonly executionPolicy: QueryExecutionPolicy, + private readonly nativeHotpathAdapter: QueryNativeHotpathAdapter, + private readonly shouldUseNativeQuery: () => boolean, + ) {} + + getRegistry(): QueryRegistry { + return this.registry; + } + + resolve(command: string, args: string[]): QueryCommandResolution | null { + return resolveQueryCommand(command, args, this.registry); + } + + async execute(input: RuntimeBridgeExecuteInput): Promise { + return this.executionPolicy.execute({ + legacyCommand: input.legacyCommand, + legacyArgs: input.legacyArgs, + registryCommand: input.registryCommand, + registryArgs: input.registryArgs, + mode: input.mode, + projectDir: input.projectDir, + workstream: input.workstream, + preferNativeQuery: this.shouldUseNativeQuery(), + }); + } + + async dispatchHotpath( + legacyCommand: string, + legacyArgs: string[], + registryCommand: string, + registryArgs: string[], + mode: TransportMode, + ): Promise { + return this.nativeHotpathAdapter.dispatch( + legacyCommand, + legacyArgs, + registryCommand, + registryArgs, + mode, + ); + } +}