How a run works
Every change to a Drupal site runs through the same four phases. Each phase has an entry and an exit gate. Pass, and the run advances; fail, and it enters a bounded feedback loop or stops. What "pass" means is configured per repository in one version-controlled file.
plan → code → test → completeThe phases are not levers. Every run walks this order; what varies between a heavy and a light run is the weight each phase carries, never the path.
Starting a run
/droost:workflow:start "Add an Event content type with a listing page"
/droost:workflow:continue # advance one phase, or resume a run found on disk
/droost:workflow:status # where things standRun state lives beside the lever file in .droost-workflow/run.json, on the filesystem so a run started against a live site can be resumed, inspected or described from a plain checkout. Writes go through a temporary file and a rename; a state file that cannot be parsed is never deleted, because it is still evidence.
Plan
The researcher agent finds out what the site actually has before the spec claims anything: modules, existing types, fields, routes, patterns already in the codebase. The spec-writer drafts an EARS-shaped spec from the conversation and those findings. The spec must carry a ## Tooling plan, one droost_decide answer per construct, before the run may enter code. Nothing is measured yet, so plan runs no gates.
Code
The agent builds from the Tooling plan, through the surfaces the router named. Code gates the diff: phpcs, phpstan, and config_clean, which asks the booted site whether a fresh drush config:export would change the tracked tree. Zero diff against a target git can see is the passing state, because a hand-written file that imports fine can still diverge from the canonical serialization and surface later as a noisy re-export.
A green code phase is then held by the seeker checkpoint until an adversarial inspection is recorded clean.
Test
phpunit, and when enabled mutation, playwright and coverage; rendered_check makes a real sub-request to the routes you name (default /) and reads the status; config_clean runs again. The test skill also uses the session's Playwright MCP for interactive verification when a browser is declared.
Complete
Complete opens by capturing what was built (the ## Realized section of the record; the documentation work that used to be its own phase), then re-runs the full enabled set as the terminal safety net, so every gate is met at least twice. wiki_fresh is due only here: it asks the site whether the project's own docs still match the code, and complete is the phase that just wrote them. The seeker holds completion too.
Modes
mode is the lever for how much a run involves you. interactive holds at every phase and converses: plan asks whether the spec is what you want built while changing it is still free, code asks whether you want the diff, test asks what went uncovered, complete asks whether to close the record. agentic runs plan through complete without stopping. You can swap from any hold with swap agentic.
The feedback loop
A blocking gate does not end a run; it starts a bounded loop. Each failing invocation spends one attempt per blocking gate against max_gate_retries, and the one-finding-at-a-time bug-fixer agent fixes the cause between invocations. A missing tool spends budget exactly like a failure. When the budget is spent, the phase is recorded failed, terminal, and nothing further runs.
The bookends
drush droost:workflow:report # the whole workchain: phases, gate verdicts, the seeker ledger, browser tier
drush droost:workflow:reset # archive a finished run to .droost-workflow/history/ and start cleanA phase that failed is never quietly recorded as passed: advancing away from a failed phase is refused, as is advancing backward or past the terminal gate. A report has to be able to describe the run honestly.