mirror of
https://github.com/0xWheatyz/handler.git
synced 2026-09-06 13:46:24 +00:00
Agents can hand work to agents: dispatch_agent
A schedule is a time trigger, and only the first step of a pipeline is really waiting on time — every later step waits on the previous step's result. Modeling "watch a source -> write a spec -> implement it" as three schedules made each fire blind: on a quiet day the coding agent still spawned, paid a full model run to find there was nothing to do, and left an empty run in Activity. So an agent can start the next step itself. `dispatch_agent` (a tool on the bundled MCP server, and on the pi bridge through the same --call seam) enqueues an ordinary spawn command in the agent's own project, tagged requested_by=agent:<id> — so a handoff is visible in Activity with no new surface to build. - Project-scoped by construction: project_id is read from the spawn environment and never from the tool arguments. - Bounded rather than gated: MAX_DISPATCH_PER_RUN counts the command rows the agent already wrote; MAX_DISPATCH_DEPTH rides in the spawn payload and is recovered by spawn._dispatch_depth, so a chain keeps its place across a resume and a cycle terminates instead of fanning out. - New scout and planner roles, with built-in skills (handler-scout, handler-planner, handler-dispatch) carrying the judgment code can't: dedupe against a memory-note watermark, treat "nothing new" as a complete run, and write a task the receiving cold-start agent can act on. - A scout ending on a clean tree skips the test gate and records the new tests_status='skipped' (migration 0017, additive CHECK widening). The gate promises `done` means tests passed for the work that shipped; nothing shipped. Rejected a `condition` field on schedules: "is this paper new and does it matter here?" is a semantic judgment, so it belongs to a model, not a scheduler column. The scout is the condition; dispatch is how it reports true — one mechanism that covers future pipelines too. 426 tests (14 new for dispatch, 3 for the gate exemption). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HcbDevyMcJWE6qPA56C7mZ
This commit is contained in:
@@ -6,6 +6,43 @@ the image workflows publish (plus `latest` from every push to `main`).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added — dynamic workflows: agents hand work to agents
|
||||
|
||||
Until now the only recurring primitive was a schedule, which fires an unconditional
|
||||
spawn on a timer. A pipeline (watch a source → write a spec → implement it) had to be
|
||||
N independent schedules, each firing blind: on a quiet day the coding agent still
|
||||
spawned, paid a full model run to discover there was no work, and left an empty run in
|
||||
Activity. Time is the wrong trigger for the later steps — the previous step's *result*
|
||||
is.
|
||||
|
||||
- **`dispatch_agent`**, a new tool on the bundled `handler-memory` MCP server (and on
|
||||
the pi bridge, via the same `--call` seam). An agent hands work to a fresh agent in
|
||||
**its own project** — `project_id` comes from the spawn environment, never from the
|
||||
arguments — by enqueuing an ordinary `spawn` command tagged
|
||||
`requested_by = agent:<id>`. It shows up in Activity like any other command, so
|
||||
nothing new had to be built to observe it.
|
||||
- **Guardrails, not an approval queue.** `MAX_DISPATCH_PER_RUN` (default 3) bounds one
|
||||
run's handoffs, counted off the command rows it already wrote; `MAX_DISPATCH_DEPTH`
|
||||
(default 3) bounds how far a chain reaches, so a cycle terminates instead of fanning
|
||||
out. Depth is recovered from the spawn command that created an agent, so it survives
|
||||
a resume. A refused dispatch tells the agent what to do instead.
|
||||
- **Two roles — `scout` and `planner`** — with built-in skills (`handler-scout`,
|
||||
`handler-planner`, `handler-dispatch`) carrying the judgment the code can't: dedupe
|
||||
against a memory watermark, treat "nothing new" as a complete run, write a task the
|
||||
receiving agent can act on cold.
|
||||
- **A quiet scout run is cheap.** A `scout` ending on a clean tree skips `mise run test`
|
||||
and records the new `tests_status = 'skipped'` (migration `0017_gate_skipped`) — the
|
||||
gate's promise is that `done` means tests passed *for the work that shipped*, and
|
||||
nothing shipped. A dirty tree, or any other role, keeps the full gate.
|
||||
|
||||
The net effect: one cheap schedule fires the scout; on a quiet day the pipeline costs
|
||||
one small-model call and stops. Only a scout that actually found something spends a
|
||||
coding-model run, and the spec it produced travels with the dispatch.
|
||||
|
||||
**Rollout:** run migrations (`0017_gate_skipped`, additive — it only widens a CHECK).
|
||||
The two new settings have working defaults. The three new skills seed themselves on the
|
||||
next API start, idempotently by name.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **The project-root checkout is now a ref store, never a working tree Handler moves.**
|
||||
|
||||
Reference in New Issue
Block a user