Skip to content

Commit 2f4bb27

Browse files
committed
fix(runtime-host): refuse a retired fake-backend session with the product reason
Dropping the `fake` registration left activation dispatching off a durable header value with no factory behind it, so a session or Automation persisted by an older build failed its next turn with `No backend factory registered for kind="fake"`. The Desktop composer does gate these rows — `sendBlocked` reads a destructive `sessionHealthNotice`, which `projectSessionSendOutcome` raises for `fake_backend` — but that is a renderer gate, not an authority: `maka run` resumes an existing session through a readiness check that only inspects the connection catalog, and a persisted Automation template still accepts `execution.backend: 'fake'`. Register an explicit refusal where the test backend used to be. It throws the canonical `NO_REAL_CONNECTION:fake_backend` error, which `parseNoRealConnectionError` already turns into the copy both surfaces show for these rows: the task came from the retired local simulation, add a real model and start a new one. Rewriting the durable header to `ai-sdk` on the read path was the alternative and is worse: it destroys the fact that drives that copy, and leaves a session that looks runnable while its `llmConnectionSlug` still points at nothing, so the failure would resurface later and less specifically. Generated-by: Claude Code
1 parent 4a19d4e commit 2f4bb27

2 files changed

Lines changed: 56 additions & 0 deletions

File tree

packages/runtime-host/src/__tests__/execution-composition.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import assert from 'node:assert/strict';
2+
import { parseNoRealConnectionError } from '@maka/core/connection-error-copy';
23
import { createRequire } from 'node:module';
34
import { mkdir, mkdtemp, rm, stat, writeFile } from 'node:fs/promises';
45
import { tmpdir } from 'node:os';
@@ -378,6 +379,45 @@ test('production composition commits automatic titles through Host-owned Session
378379
});
379380
});
380381

382+
test('a legacy fake-backend session is refused with the product reason, not a registry error', async () => {
383+
await withCompositionRoot(async ({ root, owner }) => {
384+
const { composition, manager } = await createCapturedExecutionComposition(owner);
385+
try {
386+
// Written by an older build: this one never produces `fake`, but the
387+
// durable header survives and activation dispatches straight off it.
388+
const legacy = await manager.createSession({
389+
cwd: root,
390+
backend: 'fake',
391+
llmConnectionSlug: 'fake',
392+
model: 'fake-model',
393+
permissionMode: 'ask',
394+
});
395+
const failure = await composition.handlers['turn.start'](
396+
{
397+
sessionId: legacy.id,
398+
turnId: 'turn-legacy-fake',
399+
content: { text: 'resume a retired local simulation' },
400+
},
401+
{
402+
hostEpoch: 'execution-composition-test',
403+
connectionId: 'legacy-fake-client',
404+
surface: 'tui',
405+
principal: 'local_os_user',
406+
acquireResidency: () => ({ release() {} }),
407+
},
408+
).then(
409+
(result) => result,
410+
(error: unknown) => error,
411+
);
412+
const message = failure instanceof Error ? failure.message : JSON.stringify(failure);
413+
assert.doesNotMatch(message, /No backend factory registered/);
414+
assert.equal(parseNoRealConnectionError(message).reason, 'fake_backend');
415+
} finally {
416+
await composition.close();
417+
}
418+
});
419+
});
420+
381421
test('production composition orphans ownerless ShellRuns before serving Resource queries', async () => {
382422
await withCompositionRoot(async ({ root, owner }) => {
383423
const stores = await openInteractiveExecutionStoresForWrite(owner.lease);

packages/runtime-host/src/server/execution-composition.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import { createHash, randomUUID } from 'node:crypto';
2+
import {
3+
describeChatConfigurationReason,
4+
NO_REAL_CONNECTION_CODE,
5+
} from '@maka/core/connection-error-copy';
26
import type { RuntimeExecutionConnection } from '@maka/core/llm-connections';
37
import { generalizedErrorMessage } from '@maka/core/redaction';
48
import { emptyPlanSessionState } from '@maka/core/plan';
@@ -285,6 +289,18 @@ export async function createExecutionRuntimeHostComposition(
285289
});
286290
await stores.messageReceiptStore.beginHostEpoch(context.hostEpoch);
287291
const backends = new BackendRegistry();
292+
// `fake` is a retired backend kind: this build never writes it, but a
293+
// session or Automation persisted by an older one still can, and activation
294+
// dispatches straight off that durable value. Registering an explicit
295+
// refusal — rather than the test backend, or a read-path rewrite of the
296+
// durable header — is what turns "no factory for kind=fake" into the
297+
// product's existing answer for these rows: this task came from the retired
298+
// local simulation, configure a real model and start a new one.
299+
backends.register('fake', () => {
300+
throw new Error(
301+
`${NO_REAL_CONNECTION_CODE}:fake_backend: ${describeChatConfigurationReason('fake_backend')}`,
302+
);
303+
});
288304
const runtimePolicyActivation = new RuntimePolicyActivationGate();
289305
const runtimePolicy = new HostRuntimePolicyCoordinator(
290306
runtimePolicyStores,

0 commit comments

Comments
 (0)