Skip to content

feat(cli): claw run hands the run to the server, starting one if needed - #92

Merged
EdmondDantes merged 2 commits into
mainfrom
feat/cli-thin-client-run
Jul 27, 2026
Merged

feat(cli): claw run hands the run to the server, starting one if needed#92
EdmondDantes merged 2 commits into
mainfrom
feat/cli-thin-client-run

Conversation

@EdmondDantes

@EdmondDantes EdmondDantes commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What

The second slice of TODO item 2. claw run stops opening a project db and running the solver in its own process; it becomes a thin client that hands the run to the workspace's server — the one process allowed to write.

  • New ServerClient (src/ServerClient.php):
    • running() — a live server's address, or null.
    • ensure() — that address, starting a server when none answers and waiting for it to come up.
    • startRun()POST /api/projects/{key}/issues/{id}/start, mapping 202 → ok, 409 → "already active", anything else → an error the CLI surfaces.
  • runIssue() now resolves the issue, then ensure() + startRun() — no Config, no agent, no IssueRunner in the CLI process.

Autospawn (cross-platform)

When no server answers, the CLI starts one. The launch command is rebuilt from PHP_BINARY and CLAW_SERVER_EXTENSION (the server extension, defaulting to true_async_server.so; on Windows a .dll, so set the env var), because the CLI itself runs without the server extension.

spawnCommand() returns an argv list, not a shell string, so it carries no quoting to platform-mismatch. launchDetached() then branches:

  • POSIXsetsid --fork gives the server its own session and returns at once (without --fork, setsid execs the server in place and proc_close blocks for its whole life — found by the CLI hanging in the live test).
  • Windowsproc_open with create_new_console + bypass_shell, cutting the server from the CLI's console so it survives the exit.

Either way stdio is pointed at files (/dev/null or NUL) so the server never inherits — or blocks — the pipe the CLI was invoked through. The loser of a bind race exits on the duplicate-guard from #91, and the readiness poll finds the winner.

Verified live (POSIX)

Against the real server extension on a Linux/WSL machine:

  • cold start — no server → starting one… → run launched, CLI returns in the same second;
  • warm reuse — a second run finds the running server, no respawn;
  • 409 — a start for an in-flight issue prints a run for this issue is already active;
  • create → runclaw -i opens an issue, claw run starts it on the server (run row created, status running).

Windows is written to the API but unverified — no machine to run it on here.

Behaviour changes (intended)

  • No live console trace and no terminal solver-approval prompt on claw run. The approval prompt was a human-approval gate the project does not want; the live trace returns in the next slice (poll GET …/runs/{id}/trace?since= + answer the gate over POST …/answer).
  • claw run now needs a server — one running, or the server extension available to start one. This is the "server becomes required" cost item 2 records and accepts.

Not in this PR

  • /start returning the run id, and the polling tail that renders the trace and drives the human gate (next slice).
  • claw -i / -c still write locally; folding the remaining writers behind the server is later work.

Stacking

Based on feat/server-discovery-file (#91), which adds the ServerLocator this builds on. Retarget to main once #91 merges.

Checks

composer qa green — cs, PHPStan level 8, Testo (452 tests). New tests/ServerClientTest.php covers the start status mapping, the target URL, running() on an empty workspace, and the spawn-argv shape.

@EdmondDantes
EdmondDantes changed the base branch from feat/server-discovery-file to main July 27, 2026 18:23
The CLI stops opening a project db for a run: it locates the workspace's server (ServerClient over ServerLocator), starts one detached when none answers, and POSTs the issue's start. One process writes the db. Autospawn rebuilds the launch command from PHP_BINARY and CLAW_SERVER_EXTENSION and detaches it with setsid --fork so it outlives the CLI and holds none of its stdio. Verified live: cold autospawn, warm reuse, 409 on an already-active run, and a create-then-run cycle.
spawnCommand returns argv rather than a shell string, so it carries no quoting to platform-mismatch. launchDetached branches: POSIX uses 'setsid --fork', Windows uses proc_open's create_new_console/bypass_shell; stdio goes to files (/dev/null or NUL) either way. POSIX re-verified live; Windows is written to the API but unverified on this machine.
@EdmondDantes
EdmondDantes force-pushed the feat/cli-thin-client-run branch from 9f57ff0 to 2b429f8 Compare July 27, 2026 18:24
@EdmondDantes
EdmondDantes merged commit 9514eef into main Jul 27, 2026
2 checks passed
@EdmondDantes
EdmondDantes deleted the feat/cli-thin-client-run branch July 27, 2026 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant