1. Enroll
Workspace mints an enrollment token whose subject claim identifies the workspace (or phone
device). Token goes to the cloud once, over out-of-band channels. No credentials in the repo.
Conductor mode is how LumenFlow lets a cloud control plane — or any remote surface speaking the control-plane SDK — observe and drive a local workspace without breaking the evidence-based control-plane contract. The kernel emits a schema-versioned event stream, packs expose their tools and approvals through a scoped HTTP surface, and every remote action lands in the same audit log as a local one.
This page walks the end-to-end flow: enrollment, tool discovery, scoped invocation, event stream, abort + recovery. For the architectural locks behind every decision on this page, see ADR-013 (conductor-mode pack surfaces) and ADR-011 (conductor-mode kernel surfaces).
1. Enroll
Workspace mints an enrollment token whose subject claim identifies the workspace (or phone
device). Token goes to the cloud once, over out-of-band channels. No credentials in the repo.
2. Discover
Cloud hits GET /tools on the sidecar. Response lists every pack-registered tool, its scope,
its required approvals, and its schema — derived from the pack manifest, not from prose.
3. Invoke
Cloud issues POST /tools/:name with a scoped token. Kernel routes the call through the
tool-dispatch path, records an agent-runtime:tool_called event, and returns the tool’s
structured result.
4. Observe
Every emitter call — turn start, turn complete, gate outcome, approval request — flows through
buildKernelEventV2 and lands in the cloud event stream with a content-hashed event_id for
idempotent consumption.
Enrollment issues a token whose subject is the authoritative identity
for every outbound event and every inbound tool invocation. Per ADR-013
§5, cloud must not trust a from field in a request body; it trusts
the token subject on the authenticated connection.
Two enrollment shapes exist:
| Shape | Subject format | Use |
|---|---|---|
| Workspace | <workspace_id> | Outbound events, inbound tool POSTs |
| Phone device | <workspace_id>:phone:<device_id> | Phone-originated inbound commands |
The device-registration record maps device_id to a human-readable name.
Cloud writes both the raw subject and the resolved device name into the
audit log — per-device attribution is load-bearing for the conductor UI.
Response carries the issued token; the operator installs it into the
local workspace state (.lumenflow/state/conductor/enrollment.json).
The sidecar reads this token on startup and uses its subject claim as
the from field on every emitted event.
Once enrolled, the cloud can enumerate the pack-registered tool surface:
Response shape (excerpt):
Every tool in the response corresponds to a manifest-declared entry on a registered pack. Manifest discovery is declarative — no reflection, no code-gen. The cloud builds its registry from this response and refreshes on workspace state change events.
Tool calls are authenticated by the scoped token; the token’s scope
claim governs which tools it may invoke. A token issued for workspace
scope cannot call a tool that requires agent:execute-turn.
The kernel’s tool-dispatch path:
required_approvals surface, if any (approval providers
registered on the workspace).agent-runtime:tool_called with tool_name, a content hash of
the envelope, and the resolved tool result outcome.The tool call is the audit record. No fast path, no direct imports around the tool-dispatch layer. A tool invocation that did not go through this path did not happen, from the audit trail’s perspective.
The kernel and registered packs emit a stream of typed events to the
cloud. Every emission is built through buildKernelEventV2 (see
Events contract) and carries:
| Field | Meaning |
|---|---|
schema_version | 2 for conductor-mode events (the 1 variants are legacy task_* kinds). |
kind | <pack-slug>:<event_name> (or unprefixed for kernel-intrinsic kinds). |
event_id | Content-hashed string. Cloud dedupes replays by this key. |
timestamp | RFC 3339 emission time. |
from | Workspace or phone-device identity, sourced from the token subject claim. |
| Payload fields | Kind-specific. Tagged union in @lumenflow/conductor-sdk. |
Delivery is at-least-once (ADR-013 §2). Cloud subscribers must be
idempotent — content-hashed event_id provides the dedup key, but the
handler still has to tolerate re-delivery of the same event after a
network retry.
Ordering is per-channel only (ADR-013 §3). Within a single channel
(the HTTP sidecar transport, or a phone POST endpoint, or a sidekick
bridge instance), emission order is preserved. Across channels, cloud
reconciles from emitted_at + seq.
The §4 split rule in ADR-013 is enumerated, not per-emitter:
| Kind class | Path |
|---|---|
agent-runtime:turn_*, software-delivery:gate_*, observational telemetry | Ephemeral (fail-silent on disconnect) |
sidekick:approval_requested | Queue + replay |
sidekick:command_received (inbound) | Queue + replay |
conductor:recovery_requested (inbound) | Queue + replay |
Ephemeral events are dropped silently when the cloud endpoint is
unreachable — cloud reconstructs observational state from whatever
arrives later. Commands and approvals live in
.lumenflow/state/conductor/outbox/ until the sidecar reconnects, then
drain FIFO.
A turn that aborts mid-flight carries a cleanup_status and an optional
recovery_action string (ADR-013 §1):
clean — no state mutation; no recovery needed.partial — some mutations rolled back; state self-consistent but
incomplete. recovery_action names the next step as prose.needs_recovery — state inconsistent; recovery_action names a
verbatim command. Cloud surfaces it as a button/hint but does not
execute it. The operator confirms.wu:recover is not auto-triggered. State-mutating commands require
explicit operator consent; that rule is the seam between an observable
control plane and a remote-execution control plane, and it is
non-negotiable.
A concrete example. The operator, via the conductor UI, asks the cloud to claim WU-2744 and run its gates. The wire trace, in order:
Operator → cloud UI. “Claim WU-2744 on Content: Site Comms.”
Cloud → sidecar. POST /tools/software-delivery:wu_claim with
{wu_id: "WU-2744", lane: "Content: Site Comms"}.
Sidecar → cloud. Sync response with the resolved worktree path.
Kernel → cloud. Event: software-delivery:wu_claimed with
event_id and from: ws-42. Ephemeral. Cloud subscriber upserts.
Cloud → sidecar. POST /tools/agent:execute-turn with
{goal: "run gates on WU-2744"}.
Kernel → cloud. Event: agent-runtime:turn_started. Ephemeral.
Kernel → cloud. Event: agent-runtime:tool_called with
tool_name: "software-delivery:gates". Ephemeral.
Kernel → cloud. Event: software-delivery:gate_passed. Ephemeral.
Kernel → cloud. Event: agent-runtime:turn_completed. Ephemeral.
Sidecar → cloud. Sync response with the turn result and evidence references.
Every step is auditable. The local workspace has a stamp for every event; the cloud has a dedup-stable log; the operator never has to trust either side’s prose.