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
Agents could be marked done while leaving work uncommitted or unpushed:
the Stop gate only ran the test suite, and the headless supervisor's
fallback marked any still-working agent done on a clean process exit
even when the Stop gate never recorded a verdict. Checkmarks also only
ever carried hook-written boilerplate, so the webui had no real
checkpoint to show.
The Stop gate now blocks the turn on any of: failing tests, uncommitted
changes, or commits no origin/* ref contains (rev-list --not
--remotes=origin, so it works for the --no-track worktree branches).
All blockers are reported at once; status 'done' only ever accompanies
a fully passing gate. Working dirs that aren't git checkouts, and repos
without an origin remote, skip the git half so local-only projects
can't deadlock. The supervisor's clean-exit fallback now reconciles a
still-working agent to blocked instead of done — done is a gate
verdict, not an exit code (operator cancels still settle as done).
The agent's final message is captured deterministically from the
session transcript onto the checkmark's where_it_stopped, so the
dashboard always shows a real checkpoint regardless of whether the
agent thought to leave one; a blocked checkmark shows the blockers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7mF6qeryi9nJthaxYkfPm
Some repos an operator wants to manage don't yet define the `.mise.toml`
`[tasks.test]` task the spawn gate hard-requires — a chicken-and-egg,
since you can't run an agent to author that file without it. This adds a
one-click bootstrap.
Ticking "Initialize mise" on the add step enqueues a `mise_init` command
after the clone. The worker launches a dedicated agent that detects the
repo's stack, writes a `.mise.toml` with a canonical `[tasks.test]` task,
and commits + pushes it. That agent runs with the test-task gate off
(creating the task is the point) and a `HANDLER_MISE_INIT` marker on, so
its hooks enforce a bootstrap contract instead of the normal test gate:
- Stop hook blocks the turn until `.mise.toml` defines `[tasks.test]` and
the change is committed (clean tree) and pushed (no commits ahead of an
upstream) — so claude cannot end before the work has actually landed.
- git-push hook lets the bootstrap push through, skipping the test/build
gate (there may be no working suite yet) so the file reaches the remote.
Backend: `mise_init` command type (+ migration 0006), a shared
`control.mise` helper for the test-task check, `spawn(require_tests=,
mise_init=)`, gitops `is_clean`/`ahead_count`, and `init_mise` on the
project-create API (only acts when a git remote exists to push to).
Frontend: the checkbox, plumbed through the store, following the launch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BxKY28XKCM6o4ag3nmaVsZ
Implements the Phase 1 MVP from the README: a stateless control layer + HTTP
API over a centralized database, with hook-enforced test/push gates.
- DB layer: SQLAlchemy Core, one schema rendering both Postgres (BIGSERIAL /
TIMESTAMPTZ / JSONB) and SQLite (INTEGER PK / TEXT / JSON) via portable types;
native ON CONFLICT DO UPDATE checkmark upsert on both dialects.
- Alembic dual-dialect migrations (render_as_batch for SQLite); tests run a real
`alembic upgrade head`.
- FastAPI: projects/agents/checkmark/log/answer/resume + shared log/context
routes, single global bearer token, higher-trust token gating shared-context
writes, project isolation on every route.
- Hooks (`python -m handler.hooks <event>`): Stop test gate (block on red),
PreToolUse AskUserQuestion defer + `git push` gate (tests then throwaway
build), Notification generic webhook (no-op without WEBHOOK_URL). Identity via
env injected at spawn; verify is the mock seam.
- Control CLI: spawn/list/attach/kill, hard `.mise.toml [tasks.test]` gate,
generated per-agent settings.json, identity + DATABASE_URL injected via tmux;
tmux is the mock seam.
- 45 tests (SQLite), ruff clean. Live claude/tmux/mise spawning deferred behind
the mocked seams.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W5ZuS5pV1NS6eKsRZHXonY