Addresses Greptile P1: command stages held the engine's serialization lock
for their entire execution (up to DEFAULT_COMMAND_TIMEOUT_MS = 10 min).
Every other operation — cancelRun, tick, sibling stages, concurrent
dispatch — queued behind lockTail for that whole window. The agent
executor avoids this with a startStage/pollStage/cancelStage handoff; the
command executor now matches.
**Executor refactor** (`packages/core/src/pipeline/executors/command.ts`):
- New `startStage(input)` spawns the child and returns a
`RunningCommandStage` handle immediately. Synthetic failures (fork
refusal / spawn error) produce a handle whose `outcome` is already
populated and `done` already resolved.
- New `pollStage(handle)` is synchronous: returns the cached outcome or
`{ status: "running" }`.
- New `cancelStage(handle)` is idempotent: clears timers, SIGTERMs the
process tree, schedules SIGKILL after `COMMAND_KILL_GRACE_MS`, and
settles the outcome synchronously so callers awaiting `done` unblock
without waiting on `close`.
- `CommandExecutorSpawnError` added (mirrors `AgentExecutorSpawnError`)
for kind-mismatch — the engine catches and dispatches STAGE_FAILED.
- `run()` is now a thin sugar: `startStage(input)` then `await done`.
Test ergonomics unchanged.
**Engine wiring** (`packages/core/src/pipeline/engine.ts`):
- Added `commandInflight: Map<StageRunId, RunningCommandStage>` (parallel
to the agent `inflight` map).
- `runCommandStage` now awaits the fast `startStage`, then either
dispatches an already-settled outcome (refusal / spawn fail) inline OR
stores the handle in `commandInflight` and returns. **The lock
releases immediately after spawn.**
- `tick()` polls `commandInflight` after agent stages and dispatches
STAGE_COMPLETED / STAGE_FAILED on terminal outcomes.
- `CANCEL_STAGE` handler extended to check `commandInflight` and call
`cancelStage`.
**Tests**:
- pipeline-command-executor: +6 tests for `startStage`/`pollStage`/
`cancelStage` directly, including idempotency, spawn-fast, sleep-30
cancellation, kind-mismatch throw, fork-refusal handle.
- pipeline-engine-executors: regression test proving `cancelRun`
completes in <500ms against two parallel `sleep 5` stages — would have
timed out in the pre-refactor code.
**Bonus** (round-3 P2 deferred from previous review):
- Added regression test for the stdout-overflow path including stderr in
the error message (`packages/core/src/pipeline/executors/command.ts`
line 268). The earlier round-3 fix was correct but had no test
coverage; now it does.
All 1372 core tests pass. Typecheck clean. Zero lint errors.