From ac28da8094721a76e7b53c348af2c2d8b706ae85 Mon Sep 17 00:00:00 2001 From: Helix <267227783+helix-nine@users.noreply.github.com> Date: Wed, 23 Sep 2026 19:13:14 +0000 Subject: [PATCH] fix(start-os): let effects.action.run carry the form's eventId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The container runtime tags every effect call with the calling procedure's event id under `eventId`, overwriting any `eventId` the call itself named. effects.action.run now names the event id its form was opened under (#4062), so a service's run reached StartOS with its own procedure's id, or none, and the target refused it: "getActionInput has not been called for EventID …". Seen on a VM running #4062's build: Tor's form opened under E4VNHKM6…, and the run executed under a fresh SVS5F3TC…. An `eventId` the effect names now wins over the tag. Nothing in StartOS reads the tag. Helix-Harness: claude-code Helix-Model: claude-opus-5-5 --- .../src/Adapters/EffectCreator.test.ts | 22 +++++++++++++++++++ .../src/Adapters/EffectCreator.ts | 10 ++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 projects/start-os/container-runtime/src/Adapters/EffectCreator.test.ts diff --git a/projects/start-os/container-runtime/src/Adapters/EffectCreator.test.ts b/projects/start-os/container-runtime/src/Adapters/EffectCreator.test.ts new file mode 100644 index 0000000000..caf2ebd7b6 --- /dev/null +++ b/projects/start-os/container-runtime/src/Adapters/EffectCreator.test.ts @@ -0,0 +1,22 @@ +import { effectParams } from './EffectCreator' + +describe('effectParams', () => { + test('tags the call with the calling procedure', () => { + expect(effectParams({ actionId: 'a' }, 'procedure')).toEqual({ + actionId: 'a', + eventId: 'procedure', + }) + }) + + test('keeps the event id an action run names', () => { + expect( + effectParams({ actionId: 'a', eventId: 'form' }, 'procedure'), + ).toEqual({ actionId: 'a', eventId: 'form' }) + }) + + test('sends no event id outside a procedure', () => { + expect(JSON.stringify(effectParams({ actionId: 'a' }, null))).toBe( + '{"actionId":"a"}', + ) + }) +}) diff --git a/projects/start-os/container-runtime/src/Adapters/EffectCreator.ts b/projects/start-os/container-runtime/src/Adapters/EffectCreator.ts index a1e7e19d57..2bbed4e48b 100644 --- a/projects/start-os/container-runtime/src/Adapters/EffectCreator.ts +++ b/projects/start-os/container-runtime/src/Adapters/EffectCreator.ts @@ -46,6 +46,14 @@ export type EffectContext = { constRetry?: () => void } +/** The effect call's params, tagged with the calling procedure's event id. An `eventId` the effect names itself wins. */ +export function effectParams( + params: Record, + eventId: string | null, +): Record { + return { eventId: eventId ?? undefined, ...params } +} + const rpcRoundFor = (eventId: string | null) => ( @@ -58,7 +66,7 @@ const rpcRoundFor = JSON.stringify({ id, method, - params: { ...params, eventId: eventId ?? undefined }, + params: effectParams(params, eventId), }) + '\n', ) })