feat: include driver in workflow started event#1018
Conversation
Out-of-process execution drivers emit log lines via callbacks.onLog,
but these were only sent to the logger — not through the onEvent
stream. This meant WebSocket clients (swamp serve) never received
method_output events from drivers like Tensorlake.
Now onLog also emits MethodExecutionEvent { type: "output" } so
the output appears in the event stream alongside in-process output.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The workflow execution `started` event now includes the `driver` field from the workflow definition. This allows consumers to know the executor (e.g., tensorlake, raw) immediately when a workflow begins, rather than having to infer it from output or wait until completion. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Adversarial Review
Critical / High
-
HIGH —
driverfield never reaches consumers via the libswamp layer (src/libswamp/workflows/run.ts:82-90andsrc/libswamp/workflows/run.ts:324-334)The PR adds
driver?: stringto the domain event type (execution_events.ts) and the client protocol type (packages/client/protocol.ts), and sets it inexecution_service.ts. However, the intermediate libswampWorkflowRunEventtype (line 86-89) and themapEventfunction (lines 324-334) that translates domain events into libswamp events were not updated.The
mapEventfunction explicitly constructs a new object with onlykind,runId,workflowName, andjobs— thedriverfield is silently dropped:// src/libswamp/workflows/run.ts:324-334 case "started": return { kind: "started", runId: event.runId, workflowName: event.workflowName, jobs: event.jobs.map(…), // ← driver is not forwarded };
How it breaks: Run any workflow with
driver: "@john/tensorlake". The domain event will containdriver: "@john/tensorlake", but aftermapEventprocesses it, the libswamp event sent to the server (and serialized to the WebSocket client) will havedriver: undefined. The stated goal of the PR — "Allows consumers to know the executor immediately at workflow start" — does not work.Fix: Add
driver?: stringto thestartedvariant ofWorkflowRunEventinsrc/libswamp/workflows/run.ts(around line 86-89), and forwarddriver: event.driverin themapEventfunction (around line 326).
Medium
-
MEDIUM —
onLog→onEventforwarding only applies to out-of-process drivers (src/domain/models/method_execution_service.ts:666-673)The new
onEventcall inside theonLogcallback is only added for out-of-process (non-raw) drivers. The raw driver path (line 605-608) callsdriver.execute(executionRequest)with no callbacks at all. This is an inconsistency — output from raw driver execution won't emitoutputevents throughonEvent. This may be intentional (raw driver may emit events through a different mechanism), but it's worth verifying that the asymmetry is deliberate and that consumers aren't missing output from raw-driver runs.
Low
-
LOW — Protocol test doesn't exercise
driverfield (src/serve/protocol_test.ts:149-152)The existing
ServerMessagetest at line 152 constructs astartedevent withoutdriver, and no new test was added for thedriverfield. Given finding #1, this is academic until the field actually propagates, but once fixed, tests should cover both thedriver: "some-driver"anddriver: undefinedcases.
Verdict
FAIL — The driver field is added to the domain event and client protocol but is silently dropped by the libswamp mapping layer (mapEvent in src/libswamp/workflows/run.ts), so the feature does not work end-to-end. The libswamp WorkflowRunEvent type and its mapEvent function must be updated for the driver to reach consumers.
The driver field was added to the domain event and client protocol but silently dropped by the libswamp mapEvent function. Add driver to the libswamp WorkflowRunEvent type and forward it in mapEvent. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Code Review
Blocking Issues
-
driverfield missing from libswamp layer — data silently dropped before reaching clients
src/libswamp/workflows/run.ts:82-90— TheWorkflowRunEventtype'sstartedvariant does not includedriver?: string. ThemapEventfunction at line 324-334 explicitly constructs thestartedevent with onlyrunId,workflowName, andjobs— droppingdriver. Since the server serializer operates on libswamp events (not domain events), thedriverfield added topackages/client/protocol.tswill never actually be populated over the wire. This needs:- Add
driver?: stringto thestartedvariant ofWorkflowRunEventinsrc/libswamp/workflows/run.ts - Forward
driver: event.driverin themapEventfunction's"started"case
- Add
-
Undocumented behavioral change: registered drivers now emit output events
src/domain/models/method_execution_service.ts:666-673— TheonLogcallback for registered (non-raw) drivers now callscontext.onEvent?.({ type: "output", ... })in addition to logging. Previously only the raw driver emittedMethodExecutionEventoutput events. This means workflows using registered drivers will now producemethod_outputevents in the event stream, which is a user-visible behavioral change not mentioned in the PR title, description, or test plan. If this is intentional, please document it; if not, it should be a separate PR. -
No tests for either change
Per CLAUDE.md, new code needs unit test coverage. Neither thedriverfield on thestartedevent nor the newonEventoutput forwarding for registered drivers has test coverage. At minimum:- A test in
src/libswamp/workflows/run_test.tsverifyingdriveris present on the mappedstartedevent when provided, andundefinedwhen not - A test for the
onEventoutput forwarding in the registered driver path
- A test in
Suggestions
- The
protocol_test.tsstarted event at line 152 ({ kind: "started", runId: "r1" }) is missingworkflowName— while it type-checks because the type was loosened, updating it to include the full shape would make it a better regression guard.
There was a problem hiding this comment.
Adversarial Review
Critical / High
None.
Medium
None.
Low
-
src/domain/models/method_execution_service.ts:668-672—onEventhardcodesstream: "stdout": TheonLogcallback receives all driver log lines and forwards them asstream: "stdout". If a driver ever emits stderr-originated lines throughonLog, they will be misclassified. This is consistent with the existingonLogcontract (which doesn't distinguish streams), and the in-process raw driver path doesn't hit this code, so this is purely theoretical. No action needed now, but worth noting if driver log fidelity becomes important. -
src/domain/models/method_execution_service.ts:668—onEventfailure inonLogcallback: Ifcontext.onEventthrows, it will propagate out of theonLogcallback and abort driver execution. The originalonLogonly calledcontext.logger?.info(line)which is unlikely to throw. Addingcontext.onEvent?.()introduces a second call that could throw if the event consumer has a bug. The blast radius is limited (it would fail the step), but it's a change in failure characteristics. Theoretical risk.
Verdict
PASS — Clean, minimal change that threads an optional driver field through three type layers (domain events → libswamp → client protocol) and adds stdout event forwarding for out-of-process drivers. All types align correctly (workflow.driver: string | undefined → driver?: string on all event types). The serializer uses generic jsonSafeClone so no serialization changes are needed. No logic errors, no security concerns, no breaking API changes.
There was a problem hiding this comment.
Code Review
Blocking Issues
None.
Suggestions
-
Undocumented behavioral change in
method_execution_service.ts: The PR description mentions only addingdriverto the workflow started event, but there's also a change to forward driveronLoglines asonEvent({ type: "output", ... })events. This is actually a useful addition (mirrors the pattern inshell_model.ts:125), but worth calling out in the PR description for reviewers. -
No new tests for the
onEventforwarding: There are no existing tests covering theonLogcallback in the driver execution path (method_execution_service_test.ts), so this PR doesn't regress test coverage. However, a unit test verifying that driver log lines are forwarded as output events would be a nice addition. Not blocking since the existing test file doesn't cover this code path. -
Type-only changes are consistent across all three layers (domain events → libswamp → client protocol) — nicely done. The
mapEventfunction correctly passes through the newdriverfield.
Overall: Small, focused change. The driver field addition is clean and consistent with the existing domain model. The onEvent forwarding for driver logs is a correct pattern match with shell_model.ts.
🤖 Generated with Claude Code
There was a problem hiding this comment.
CLI UX Review
Blocking
None.
Suggestions
-
Log-mode: driver not shown on start —
src/presentation/renderers/workflow_run.ts:65-68: Thestartedhandler now receivesdriverbut only logs"Starting workflow". A user who passes--driver @john/tensorlakegets no visual confirmation that the override took effect. Consider appending it:"Starting workflow (driver: {driver})"whene.driveris set. -
JSON-mode: driver absent from
completedoutput —WorkflowRunView(insrc/libswamp/workflows/workflow_run_view.ts) has nodriverfield, so the JSON output emitted at completion doesn't include which driver was used. Scripts that parseswamp workflow run --jsonhave no way to confirm the driver. Consider addingdriver?: stringtoWorkflowRunViewand populating it intoRunData.
Verdict
PASS — no blocking issues. The type additions are clean and additive; existing output is unaffected.
Summary
driverfield to the workflowstartedexecution eventworkflow.driverin the execution service@swamp/clientprotocol typesTest plan
driver: "@john/tensorlake"and verify thestartedevent includesdriver: "@john/tensorlake"driveris undefined in the event🤖 Generated with Claude Code