ADR-0022: WM orchestrates multi-env handler execution; run context serializes at env boundaries¶
- Status: proposed
- Date: 2026-04-14
- Deciders: @Aksem
- Tags: actions, architecture, orchestration, extension-runner, wm-server
Context¶
An action can declare handlers in more than one execution environment. For
example, a formatting action might have one handler in a runtime env and
another in a dev_no_runtime env.
ADR-0003 established that each env runs in an isolated process (one ER per env). ADR-0016 established that the WM owns cross-runner coordination at project and workspace scope. Neither ADR addresses who owns handler sequencing when a single action run spans multiple ER processes.
The current design delegates the entire action run to one ER via actions/run,
which has two problems:
- Wrong ownership. A single ER cannot execute handlers that live in a different env — it can only run handlers installed in its own virtualenv.
- No context path. In sequential mode, each handler reads the accumulated
result of all previous handlers through
context.current_result. When handlers are in different ER processes there is no in-process path for that state to travel between them.
Related ADRs Considered¶
- ADR-0003 — establishes process isolation per env; this ADR defines handler-level coordination across those isolated processes.
- ADR-0013 — establishes that the action declares whether its handlers are sequential or concurrent; this ADR defines how that strategy is carried out when handlers span multiple envs.
- ADR-0016 — establishes WM ownership of cross-runner coordination at action/project/workspace scope; this ADR extends that ownership rule down to handler-granularity when handlers cross env boundaries.
Decision¶
Rule 1 — WM owns handler sequencing when handlers span multiple envs¶
When all of an action's handlers are in the same env, the ER runs them
internally: it already has the handler code in-process and manages
current_result without any wire round-trips. The WM delegates the full action
run to that ER with a single call.
When handlers span more than one env, the ER can no longer own sequencing: it does not have access to handlers installed in other envs. The WM takes over as the orchestrator and drives execution handler-by-handler across the relevant ER processes.
Rule 2 — Run context serializes only at actual env boundaries¶
The run context (the accumulated result available to sequential handlers as
context.current_result) travels between handlers as an in-memory value inside
a single ER. It must be serialized to cross a process boundary.
The serialization scope must match the architectural boundary: context crosses the wire only when it actually crosses an env boundary, not between every handler call. Within the same env, consecutive handlers share context in memory as they always have.
This rule determines the unit of WM orchestration: the WM groups consecutive handlers that share an env into a segment, issues one call per segment, and passes the serialized context only between segments.
handlers: [h1/env1, h2/env1, h3/env1, h4/env2]
segments: [(env1, [h1, h2, h3]), (env2, [h4])]
one call to ER_env1 one call to ER_env2
For sequential execution the WM calls each segment in order, threading the serialized context as input to the next segment. For concurrent execution the WM dispatches all per-env groups in parallel (each with no prior context) and merges the results.
Consequences¶
- Correct multi-env execution. Actions with handlers spread across envs now run correctly; previously only single-env actions behaved as intended.
- WM protocol grows a new primitive. The WM–ER protocol needs a new
request (
actions/runHandlers) that accepts an explicit handler list and an optional serialized prior result.actions/runremains unchanged for single-env actions. - Serialization at env boundaries only. Consecutive same-env handlers share context in memory; serialization cost scales with the number of env crossings, not the number of handlers.
- ER execution logic is reused. Within a segment the ER still manages sequential handler execution internally, so existing ER handler-sequencing code is not replaced.
- WM gains handler-level visibility. To form segments the WM must know which env each handler belongs to. That information is already present in the resolved project config.
Alternatives Considered¶
Single-handler calls (actions/runHandler) with context passed on every
call. The WM would call one handler at a time and pass the serialized context
after every call, including calls within the same env. Rejected because it
forces unnecessary serialization and deserialization for consecutive handlers
that share a process, and multiplies round-trips proportionally to the handler
count rather than the env-boundary count.
Delegating multi-env coordination to the ER via cross-runner calls. An ER
could invoke handlers in other ERs by calling back to the WM (finecode/runActionInProject)
and relying on the WM to re-route. Rejected because it hides orchestration
inside a handler invocation, conflicts with the ownership boundary in ADR-0016,
and makes the recursion depth harder to track.
Related Decisions¶
- Extends ADR-0016: WM ownership of cross-runner coordination now applies at handler granularity, not only at action/project/workspace granularity.
- Applies ADR-0013: the action's declared execution strategy (sequential / concurrent) governs how the WM forms segments and whether it threads context or dispatches in parallel.
Implementation Notes¶
The new WM–ER request actions/runHandlers carries: the action name, an
ordered list of handler names to execute, an optional serialized prior result
(previousResult), and the standard run options. The ER seeds
context.current_result from previousResult before running the first handler
in the list, then executes the remaining handlers in sequence as it does today.
The response includes both a serialized final result (for context chaining) and
the formatted output requested by the caller.