Proactive workflows
A proactive workflow starts without anyone asking. An event arrives, an agent decides whether it matters, another agent does the work, and the run pauses until its result is approved before Archon acts on it. Whoever answers reviews finished work instead of starting each run.
Archon builds this from parts that already exist:
| Step | Archon primitive |
|---|---|
| Something happens | A trigger binding starts a run from a GitHub webhook or a schedule |
| Decide whether it matters | A prompt: node on the small model tier with an output_format |
| Stop when it does not | A when: condition on that output |
| Do the work | A prompt: node on the default model |
| Ask for approval | A wait: node with the approval form |
| Stop on reject | A cancel: node with a when: condition on the answer |
| Act | A node that runs only when the answer is approve |
The reference workflow
Section titled “The reference workflow”The Archon repository ships one complete example: .archon/workflows/examples/proactive-issue-investigation.yaml. It is not part of the bundled pack. Copy it into your project’s .archon/workflows/ to use it.
When an issue opens, it runs these nodes:
classifyreads the issue on thesmalltier and returnsroute(investigateorskip) and a one-sentencereason. Most issues need no investigation, so the cheap model reads every issue and the stronger model reads only the ones routed onward.investigateruns only when$classify.output.route == 'investigate'. It reads the issue and the code in the checkout and returns a draftcomment. Its prompt tells it not to change files or write to GitHub.reviewis await:node withform: approval. Itsattention:message shows the classifier’s reason and the full draft. The answer is{decision, text}, wheredecisionisapproveorreject. Rejecting does not end the run by itself; the next two nodes branch on the decision.dropruns only when$review.output.decision == 'reject'. It is acancel:node, so a rejected draft cancels the run and nothing is posted.postruns only when$review.output.decision == 'approve'and sends the draft withgh issue comment. It is abash:node rather than an agent because the review approved that exact text, and an agent could reword it after approval.
The workflow declares repository and issue_number as required inputs, so the trigger binding supplies them. The review node needs no extra declaration: a triggered run that reaches it pauses in the attention queue and continues once someone answers.
Every agent node returns a small typed object. Nodes never parse another node’s prose, and the when: condition compares an enum field, not free text.
Start it when an issue opens
Section titled “Start it when an issue opens”Bind the workflow to GitHub issue openings in your webhook source configuration. The workflow triggers guide also covers signed Standard Webhooks and CLI-driven polling for systems that cannot send webhooks.
This binding maps the issue number from the event and the repository from a literal:
{ "bindingId": "widgets-issue-investigation", "hostId": "build-host-1", "runAsUserId": "2a568f73-e217-40ca-b17d-a784d19fd43a", "resource": "github:acme/widgets:issue-investigation", "capacity": 3, "overlap": "queue", "launch": { "cwd": "/srv/archon/widgets", "workflowName": "proactive-issue-investigation", "inputs": {}, "isolation": { "kind": "default" } }, "selector": { "kind": "issue.lifecycle", "actions": ["opened"], "repository": { "host": "github.com", "path": "acme/widgets" } }, "inputMapping": { "issue_number": { "source": "field", "field": "subject.number" }, "repository": { "source": "literal", "value": "acme/widgets" } }}A run waiting at review is paused, and it gives its resource slot back while it waits, so unreviewed drafts do not block later issues. capacity: 3 bounds how many investigations execute at once. When a draft is approved, its run continues ahead of queued issues once a slot frees. This needs a continuation host: archon serve, or archon workflow wake --watch beside the trigger host. See resource, capacity, and overlap.
The run uses the provider configuration and credentials of runAsUserId. The gh CLI must be authenticated for that user on the execution host, because both agents and the post node call it.
To run the same pattern on a timer instead, use a schedule binding. A schedule has no event to map from, so the workflow’s first node has to find its own work, such as listing issues opened since the last run. Keep that cursor in a durable state document and update it from a bash: or script: node, so the next run starts where this one stopped.
Review the draft
Section titled “Review the draft”A triggered run has no chat conversation, so the attention message is not posted to a chat. The console’s Needs your input banner lists the paused run with its message and approve and reject controls. From the CLI, find paused runs and read the draft with:
archon workflow status --allarchon workflow get <run-id>Then approve or reject it. Approving continues the run to post. Rejecting continues it to drop, which cancels the run. The reason is recorded as the answer’s text:
archon workflow approve <run-id>archon workflow reject <run-id> "Wrong root cause"Check the routes before deploying
Section titled “Check the routes before deploying”The fixtures beside the workflow cover its outcomes. A stub for review stands in for its answer and must match the approval form’s output. posted routes an issue to investigation, answers review with { decision: approve, text: "" }, and checks that the attention message and the post node both carry the draft. rejected answers review with decision: reject and expects the run to end cancelled at drop; post has no stub, so the fixture fails if it runs. skipped routes an issue to skip and fails if any later node runs. Run them without calling a model or GitHub:
archon workflow test proactive-issue-investigationTo see the run stop at review, stub only the two agent nodes. With no stub for review, the simulation pauses there, as a real run would:
archon workflow run proactive-issue-investigation --dry-run \ --stubs stubs.yaml --input repository=acme/widgets --input issue_number=42where stubs.yaml holds:
classify: { route: investigate, reason: "a regression with a stack trace" }investigate: { comment: "The cause is the null check in parser.ts" }Adapting it
Section titled “Adapting it”- Change what counts as worth investigating in the
classifyprompt. Keep its output an enum so thewhen:condition stays an exact comparison. - Change the work in the
investigateprompt. Keep the instruction not to write anywhere;reviewexists so the result is approved before it leaves the run. - Change the action in the
postnode. If the action itself needs judgment, such as applying a fix, use an agent node afterreviewand havereviewapprove the plan rather than exact text.
