From 79a76b534c827e5333d0d85eb137ca92c1c47dd6 Mon Sep 17 00:00:00 2001 From: kjgbot Date: Sat, 13 Jun 2026 02:32:48 +0200 Subject: [PATCH 1/2] Default factory CLI to real clients --- packages/factory-sdk/src/cli/fleet.test.ts | 80 ++++++++++++++++++++++ packages/factory-sdk/src/cli/fleet.ts | 15 ++-- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/packages/factory-sdk/src/cli/fleet.test.ts b/packages/factory-sdk/src/cli/fleet.test.ts index 3fd007c2..4886da83 100644 --- a/packages/factory-sdk/src/cli/fleet.test.ts +++ b/packages/factory-sdk/src/cli/fleet.test.ts @@ -122,6 +122,86 @@ describe('fleet CLI parsing', () => { }) describe('fleet CLI runtime', () => { + it('uses real fleet and cloud mount for fixture-less factory configs on the operator path', async () => { + const root = await mkdtemp(join(tmpdir(), 'fleet-cli-real-default-')) + try { + const configPath = await writeConfig(root) + const realFleet = new FakeFleetClient() + const realMount = new FakeMountClient({ [issuePath]: issueFile }) + const createFleetCalls: unknown[] = [] + const cloudMountCalls: unknown[] = [] + const output = buffer() + + const code = await runFleetCli([ + 'factory', + 'run-once', + '--dry-run', + '--config', + configPath, + ], { + createFleet: (opts) => { + createFleetCalls.push(opts) + return realFleet + }, + cloudMountFromConfig: async (opts) => { + cloudMountCalls.push(opts) + return realMount + }, + stdout: output, + stderr: buffer(), + }) + + expect(code).toBe(0) + expect(createFleetCalls).toHaveLength(1) + expect(cloudMountCalls).toHaveLength(1) + const report = JSON.parse(output.text()) + expect(report).toMatchObject({ + dryRun: true, + pulled: [{ key: 'AR-77' }], + dispatched: [{ issue: { key: 'AR-77' } }], + }) + } finally { + await rm(root, { recursive: true, force: true }) + } + }) + + it('keeps explicit fixtureFiles configs on Fake fleet and mount for harness runs', async () => { + const root = await mkdtemp(join(tmpdir(), 'fleet-cli-fixture-opt-in-')) + try { + const configPath = await writeConfig(root, { + fixtureFiles: { [issuePath]: issueFile }, + }) + const output = buffer() + + const code = await runFleetCli([ + 'factory', + 'run-once', + '--dry-run', + '--config', + configPath, + ], { + createFleet: () => { + throw new Error('real fleet should not be selected for fixture config') + }, + cloudMountFromConfig: async () => { + throw new Error('real mount should not be selected for fixture config') + }, + stdout: output, + stderr: buffer(), + }) + + expect(code).toBe(0) + const report = JSON.parse(output.text()) + expect(report).toMatchObject({ + dryRun: true, + pulled: [{ key: 'AR-77' }], + dispatched: [{ issue: { key: 'AR-77' } }], + }) + } finally { + await rm(root, { recursive: true, force: true }) + } + }) + it('runs factory run-once dry-run over FleetClient and MountClient fakes with zero writes and zero spawns', async () => { const fleet = new FakeFleetClient() const mount = new FakeMountClient({ [issuePath]: issueFile }) diff --git a/packages/factory-sdk/src/cli/fleet.ts b/packages/factory-sdk/src/cli/fleet.ts index dbc4172d..ef7088ba 100644 --- a/packages/factory-sdk/src/cli/fleet.ts +++ b/packages/factory-sdk/src/cli/fleet.ts @@ -26,6 +26,8 @@ import { FakeFleetClient, FakeMountClient } from '../testing' interface FleetCliDeps { fleet?: FleetClient mount?: MountClient + createFleet?: typeof createFleet + cloudMountFromConfig?: typeof RelayfileCloudMountClient.fromConfig stdout?: Pick stderr?: Pick probeCloser?: ProbeCloser @@ -274,14 +276,14 @@ async function loadConfig(path?: string): Promise { const record = asRecord(raw) return { config: FactoryConfigSchema.parse(record.factoryConfig ?? record), - fixtureFiles: asRecord(record.fixtureFiles), + fixtureFiles: record.fixtureFiles ? asRecord(record.fixtureFiles) : undefined, } } async function buildFleet(globals: GlobalOptions, loaded: LoadedConfig | undefined, deps: FleetCliDeps): Promise { if (deps.fleet) return deps.fleet - if (globals.backend === 'internal' && loaded?.fixtureFiles) return new FakeFleetClient() - return createFleet({ + if (globals.backend === 'internal' && hasExplicitFixtureFiles(loaded)) return new FakeFleetClient() + return (deps.createFleet ?? createFleet)({ backend: globals.backend, cwd: process.cwd(), connectionPath: resolveBrokerConnectionPath(process.cwd()), @@ -305,15 +307,18 @@ export function resolveBrokerConnectionPath(startCwd = process.cwd()): string | async function buildMount(loaded: LoadedConfig, deps: FleetCliDeps): Promise { if (deps.mount) return deps.mount - if (loaded.fixtureFiles) return new FakeMountClient(loaded.fixtureFiles) + if (hasExplicitFixtureFiles(loaded)) return new FakeMountClient(loaded.fixtureFiles) let mount: MountClient - mount = await RelayfileCloudMountClient.fromConfig({ + mount = await (deps.cloudMountFromConfig ?? RelayfileCloudMountClient.fromConfig)({ workspaceId: loaded.config.workspaceId, isAllowedDraft: (path, content, opts) => isAllowedFactoryDraft(path, content, opts, mount, loaded.config), }) return mount } +const hasExplicitFixtureFiles = (loaded: LoadedConfig | undefined): loaded is LoadedConfig & { fixtureFiles: Record } => + loaded?.fixtureFiles !== undefined + async function isAllowedFactoryDraft( path: string, content: unknown, From 51545c0548a0742c13ba7e0f2adeae3e40eb0d85 Mon Sep 17 00:00:00 2001 From: kjgbot Date: Sat, 13 Jun 2026 02:40:25 +0200 Subject: [PATCH 2/2] Widen CLI cloud mount seam type --- packages/factory-sdk/src/cli/fleet.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/factory-sdk/src/cli/fleet.ts b/packages/factory-sdk/src/cli/fleet.ts index ef7088ba..e13014d9 100644 --- a/packages/factory-sdk/src/cli/fleet.ts +++ b/packages/factory-sdk/src/cli/fleet.ts @@ -20,6 +20,7 @@ import { type FleetClient, type MountClient, type ProbeCloser, + type RelayfileCloudMountClientConfig, } from '../index' import { FakeFleetClient, FakeMountClient } from '../testing' @@ -27,7 +28,7 @@ interface FleetCliDeps { fleet?: FleetClient mount?: MountClient createFleet?: typeof createFleet - cloudMountFromConfig?: typeof RelayfileCloudMountClient.fromConfig + cloudMountFromConfig?: (config?: RelayfileCloudMountClientConfig) => Promise stdout?: Pick stderr?: Pick probeCloser?: ProbeCloser