Schemas:
rundocket.run.v2rundocket.evidence.v2rundocket.await.v1rundocket.signals.v1
The execution kernel runs verified Expo start, build, launch, and test
operations as managed local processes with an explicit completion contract.
A development command frequently finishes its useful work long before its
process ends. expo run:ios builds the app, installs it, launches it, and then
keeps Metro in the foreground indefinitely. expo start never exits at all. An
agent that waits for process exit therefore waits for a signal that never
arrives, and raw output gives it no way to tell "still compiling" from "finished
twenty minutes ago".
RunDocket separates the two timelines:
- Process lifecycle —
state:running,cancelling,succeeded,failed,cancelled. - Work lifecycle —
lifecycle.phasealong the ordered milestonesstarting -> resolving -> compiling -> built -> installed -> serving.
Each adapter operation declares which milestone means done and whether the
command exits by itself. The plan carries that contract in adapter.signals
before execution, so an agent knows what it will be waiting for.
- Call
operation_plan. Readadapter.signalsfor the completion milestone, the expected milestone chain, andcompletesOnExit. - Call
operation_applywith theplanIdandapproved: true. - Call
operation_awaitwith the returnedrunId. It blocks until there is something to report and returns arundocket.await.v1result. - Use
operation_statusfor an unblocking snapshot,operation_cancelto stop the process group, andevidence_getfor the full bundle.
Do not poll operation_status in a loop. operation_await exists so that a
single call returns exactly when the state changes.
| Input | Meaning |
|---|---|
runId |
The managed run. |
until |
completion (default), exit, or a specific milestone. |
timeoutMs |
1000–3600000, default 300000. |
The wait ends on any of: the requested target, a fatal diagnostic, process exit, or the timeout. Silence is never treated as success.
outcome |
Meaning |
|---|---|
completed |
The requested target was reached. |
failed |
A fatal diagnostic fired, or the process exited non-zero. |
exited |
The process ended before reaching the target. |
cancelled |
The run was cancelled. |
timeout |
Nothing terminal happened within timeoutMs. |
cause carries the diagnostic that ended the wait when the outcome is failed.
The run snapshot reports lifecycle.completion.processOutlivesCompletion. When
it is true, the work is done and the process is still serving on purpose —
waiting for exit at that point would never return.
| Adapter | Operation | Command | Completion | Exits |
|---|---|---|---|---|
| Expo | start |
expo start --port |
serving |
no |
| Expo | launch |
expo run:<platform> |
installed |
no |
| Expo | build |
expo run:<platform> --no-bundler |
installed |
yes |
| Expo | test |
npm test |
built |
yes |
| Flutter | launch |
flutter run |
serving |
no |
| Flutter | build |
flutter build <platform> |
built |
yes |
| Xcode | build/test |
xcodebuild |
built |
yes |
Only the Expo rows are executable today. The remaining rows are planned and carry their signal contract so the intent stays visible.
Expo has no local build command that stops before installing, so build is
run:<platform> --no-bundler: it performs the same work but terminates instead
of holding a bundler. The plan discloses this in diagnostics, because the
installed app then needs a separately running development server.
Output lines are classified as they arrive. Structured diagnostics carry
severity, code, message, and fatal. A fatal diagnostic — a failed
xcodebuild, a failed Gradle task, a CommandError, an unavailable device,
missing signing — ends a wait even while the process keeps running.
Non-fatal diagnostics such as individual compiler errors are recorded but do not end a wait, because a build can log an error line and still recover.
adapter.command.env carries the environment an adapter requires, applied on top
of the inherited process environment and included in the plan hash. Apple builds
receive LANG/LC_ALL=en_US.UTF-8, because CocoaPods under Ruby 3.4+ aborts
with an ASCII-8BIT normalization error when a process is spawned by an agent
runtime with no UTF-8 locale.
approved: trueis mandatory for local mutation.- Apply recomputes the plan; a different source/toolchain fingerprint returns
STALE_PLAN. - An occupied requested port returns
PORT_IN_USEforstart. - Any operation outside verified Expo start/build/launch/test returns
EXECUTION_UNAVAILABLEorEXECUTION_NOT_IMPLEMENTED. - Cancellation targets only the process group of the supplied
runId.
Stdout and stderr are captured separately and bounded to 32 KiB from the head
plus 96 KiB from the tail, with the omitted middle marked inline and counted in
output.omittedBytes. The head is preserved deliberately: a build failure names
its cause near the start and repeats only follow-up noise at the end.
Evidence contains the immutable plan, the run snapshot, source binding, and
whether current sources are fresh, stale, or unverified.
Runs are session-local in this phase. Closing the MCP connection cancels active managed processes; no detached daemon is left behind. Runs therefore do not survive an agent restart — that gate is open.