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. Every decision on this page is backed by the conductor-mode architectural locks for both the pack surfaces and the 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. 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. 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. 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 backpressure split rule 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:
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-042 and run its gates. The wire trace, in order:
Operator → cloud UI. “Claim WU-042 on Content: Site Comms.”
Cloud → sidecar. POST /tools/software-delivery:wu_claim with
{wu_id: "WU-042", 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-042"}.
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.