Skip to content

fix(cli): auto-start the runtime for non-watch run and tasks run - #690

Open
EnmaJim wants to merge 1 commit into
andresharpe:mainfrom
EnmaJim:feat/start-runtime-detached-for-cli-runs
Open

fix(cli): auto-start the runtime for non-watch run and tasks run#690
EnmaJim wants to merge 1 commit into
andresharpe:mainfrom
EnmaJim:feat/start-runtime-detached-for-cli-runs

Conversation

@EnmaJim

@EnmaJim EnmaJim commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Linked issue

Closes #682

Summary of changes

dotbot run <workflow> kept its runtime precondition and auto-start nested inside
if ($Watch), so the default (detached) path launched the task runner with no runtime,
printed ✓ Workflow '<name>' started. and exited 0. The runner then parked every task in
needs-input with "Dotbot runtime endpoint not available" — asynchronously, after
minting a run and pushing an integration branch — leaving callers and CI no way to detect
the failure. The runtime is now settled on both paths before any run is minted, with
--no-auto-runtime as the synchronous, non-zero opt-out.

Changes

  • src/cli/workflow-run.ps1: hoist the runtime check out of if ($Watch) so
    --no-auto-runtime refuses with exit 1 on every path, and auto-start a detached runtime
    for non-watch runs. Both happen before Initialize-WorkflowRun, so a runtime that cannot
    start leaves no run, no tasks and no integration branch behind. --watch still hosts its
    runtime in-process and tears it down on exit.
  • src/runtime/.../Private/Lifecycle.psm1: add Start-DotbotRuntimeDetached, which spawns
    dotbot serve as a child and waits for .control/runtime.json (written only once the
    listener accepts). Start-DotbotRuntime hosts in-process and records its own $PID, so
    it cannot serve a CLI that exits right after spawning a detached runner. The result
    carries no listener handle — a caller can never stop a runtime it does not own. Exported
    from Dotbot.Runtime.psd1.
  • src/cli/tasks-run.ps1: apply the same auto-start before launching the task runner.
  • bin/dotbot.ps1: propagate tasks run exit codes (exit inside a &-invoked script only
    ends that script, so every tasks run failure previously reported success); and stop
    --poll-interval-ms from consuming the following token when it is another flag, which made
    --poll-interval-ms --watch cast [int]"--watch" and silently drop --watch into the
    broken detached path.
  • tests/Test-ProcessDispatch.ps1: cover the --poll-interval-ms parser guard and the
    runtime precondition end to end.
  • CHANGELOG.md: Added / Changed / Fixed entries.

Testing notes

  • pwsh -File tests/Run-Tests.ps1 (or pwsh -File tests/Test-ProcessDispatch.ps1 for the
    targeted suite). New assertions: --watch still binds after a valueless
    --poll-interval-ms; --poll-interval-ms 500 --watch still parses both; and
    dotbot run smoke-test --no-auto-runtime without a live runtime exits 1, says the runtime
    is not running, and mints zero WorkflowRun directories.
  • Manual repro from dotbot run <workflow> without --watch never starts the runtime and fails asynchronously #682: in a fresh dotbot init project with no dotbot go/serve
    running, dotbot run smoke-test (no --watch) now prints › Starting headless runtime...,
    completes the task, and writes .bot/.control/smoke-test.json.
  • dotbot run smoke-test --no-auto-runtime exits non-zero immediately and leaves
    .bot/workspace/tasks/workflow-runs empty (no pushed integration branch — see A failed task discards commits the agent already made; every run leaves an unreapable branch #683).
  • Confirm --watch is unchanged: runtime is hosted in-process and stopped on exit.
  • dotbot runtime-status after a non-watch run should report the auto-started runtime's PID —
    it deliberately outlives the command.

Checklist

  • Tests added or updated
  • Docs updated (if behaviour changed)
  • Linked issue exists
  • Follows the contribution guide

…resharpe#682)

The runtime precondition and the auto-start were both nested inside
the --watch branch, so the default path spawned the detached task
runner with no runtime and exited 0. The runner then parked every task
in needs-input with a runtime-endpoint-not-available note -- in a log
file the caller never sees, and only after an integration branch had
been created and pushed. --no-auto-runtime was bound but unreachable
without --watch; it now refuses synchronously with exit 1, before
Initialize-WorkflowRun, so a refused run leaves nothing behind.

Start-DotbotRuntimeDetached brings the runtime up in a process of its
own by spawning dotbot serve and waiting for the connection file, which
Start-DotbotRuntime writes only once the listener is accepting.
Start-DotbotRuntime hosts the listener in-process and records its own
PID, so it cannot serve a CLI that exits immediately after spawning a
detached runner. The result carries no listener handle, so a caller can
never tear down a runtime it does not own. A runtime auto-started this
way deliberately outlives the command; --watch still hosts its own
in-process and tears it down on exit.

dotbot tasks run takes the same path, and its exit code now propagates
through bin/dotbot.ps1 -- exit inside an ampersand-invoked script only
ends that script, so every tasks run failure previously reported
success to the shell. --poll-interval-ms also consumed the token that
followed it even when that token was another flag, so
--poll-interval-ms --watch silently dropped --watch and took the very
path above.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Inbox

Development

Successfully merging this pull request may close these issues.

dotbot run <workflow> without --watch never starts the runtime and fails asynchronously

1 participant