Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions .archon/workflows/defaults/archon-workflow-builder.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ nodes:
5. Whether this should be a simple DAG or include a loop node

Be specific and concrete. Each proposed node should have a clear type
(bash, prompt, command, or loop) and a one-line description of what it does.
(bash, prompt, command, script, loop, or approval) and a one-line
description of what it does.
model: haiku
allowed_tools: []
output_format:
Expand Down Expand Up @@ -115,7 +116,7 @@ nodes:

nodes:
- id: node-id-kebab-case
# Choose ONE of: prompt, bash, command, loop
# Choose ONE of: prompt, bash, command, script, loop, approval

# --- prompt node (AI-executed) ---
prompt: |
Expand All @@ -131,6 +132,17 @@ nodes:
# --- command node (references a .archon/commands/ file) ---
command: command-name

# --- script node (TypeScript via bun, or Python via uv — no AI, stdout = $<nodeId>.output) ---
# Use for deterministic data transforms the shell would mangle (JSON parsing, etc.)
script: |
const raw = String.raw`$other-node.output`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Avoid literal $*.output placeholders in the bundled workflow prompt.

$other-node.output and $nodeId.output look like real workflow output refs, but this workflow has no other-node or nodeId nodes. Use the angle-bracket placeholder form here so validation/substitution does not treat the documentation text as an actual dependency.

🐛 Proposed fix
-            const raw = String.raw`$other-node.output`;
+            const raw = String.raw`$<upstream-node>.output`;
             const data = JSON.parse(raw);
             console.log(JSON.stringify({ count: data.items.length }));
...
-      6. Use `script` nodes for typed data transforms (TypeScript JSON parsing, Python with deps) — stdout is captured as output, stderr is forwarded as a warning. $nodeId.output is NOT shell-quoted in script bodies — parse with JSON.parse / json.loads, not shell interpolation
+      6. Use `script` nodes for typed data transforms (TypeScript JSON parsing, Python with deps) — stdout is captured as output, stderr is forwarded as a warning. `$<nodeId>.output` is NOT shell-quoted in script bodies — parse with JSON.parse / json.loads, not shell interpolation

Also applies to: 179-179

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/workflows/defaults/archon-workflow-builder.yaml at line 138, The
bundled workflow contains literal placeholder refs inside the raw prompt (e.g.,
the template assigned to const raw = String.raw`$other-node.output` and the
similar occurrence for `$nodeId.output`), which can be misinterpreted as real
workflow outputs; update those raw-string examples to use the angle-bracket
placeholder form (e.g., `<other-node.output>` and `<nodeId.output>`) so
validation/substitution will treat them as documentation text rather than actual
dependencies.

const data = JSON.parse(raw);
console.log(JSON.stringify({ count: data.items.length }));
runtime: bun # required: 'bun' (.ts/.js) or 'uv' (.py)
# deps: [requests] # uv only
# Or reference a named script in .archon/scripts/:
# script: extract-labels # no extension; bun resolves .ts/.js, uv resolves .py

# --- loop node (iterative AI execution) ---
loop:
prompt: |
Expand All @@ -139,17 +151,22 @@ nodes:
max_iterations: 10
fresh_context: true # optional: reset context each iteration

# --- approval node (human gate — pauses workflow) ---
approval:
message: "Review the plan above. Approve to continue."
# capture_response: true # store reviewer comment as $<nodeId>.output

# Common options for all node types:
depends_on: [other-node-id] # DAG edges
when: "$<other-node>.output == 'value'" # conditional execution
trigger_rule: all_success # all_success | one_success | all_done
timeout: 120000 # ms, for bash nodes
timeout: 120000 # ms, for bash and script nodes
```

## Variable Reference
- `$ARGUMENTS` — user's input text
- `$ARTIFACTS_DIR` — pre-created directory for workflow artifacts
- `$<nodeId>.output` — stdout from a bash node or AI response from a prompt node
- `$<nodeId>.output` — stdout from a bash/script node or AI response from a prompt node
- `$<nodeId>.output.field` — JSON field from a node with output_format
- `$BASE_BRANCH` — base git branch

Expand All @@ -158,12 +175,14 @@ nodes:
2. The `description:` MUST follow the "Use when / Triggers / Does / NOT for" pattern
3. Every node MUST have a unique kebab-case `id`
4. Use `depends_on` to define execution order
5. Use `bash` nodes for deterministic operations (file checks, git commands, installs)
6. Use `prompt` nodes for AI reasoning tasks
7. Use `output_format` on prompt nodes when downstream nodes need structured data
8. Use `allowed_tools: []` on classification/analysis nodes that don't need tools
9. Use `denied_tools: [Edit, Bash]` when a node should only use Write (not edit existing files)
10. Prefer `model: haiku` for simple classification tasks to save cost
5. Use `bash` nodes for deterministic shell operations (file checks, git commands, installs)
6. Use `script` nodes for typed data transforms (TypeScript JSON parsing, Python with deps) — stdout is captured as output, stderr is forwarded as a warning. $nodeId.output is NOT shell-quoted in script bodies — parse with JSON.parse / json.loads, not shell interpolation
7. Use `prompt` nodes for AI reasoning tasks
8. Use `approval` nodes to pause for human review at risky gates (plan→execute boundary, destructive actions)
9. Use `output_format` on prompt nodes when downstream nodes need structured data
10. Use `allowed_tools: []` on classification/analysis nodes that don't need tools
11. Use `denied_tools: [Edit, Bash]` when a node should only use Write (not edit existing files)
12. Prefer `model: haiku` for simple classification tasks to save cost

## Output

Expand Down
22 changes: 19 additions & 3 deletions .claude/skills/archon/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ nodes:
depends_on: [first-node]
```

### Four Node Types
### Node Types

Each node has exactly ONE of: `command`, `prompt`, `bash`, or `loop`.
Each node has exactly ONE of: `command`, `prompt`, `bash`, `script`, `loop`, `approval`, or `cancel`.

**Command node** — runs a `.archon/commands/*.md` file:
```yaml
Expand All @@ -177,6 +177,22 @@ Each node has exactly ONE of: `command`, `prompt`, `bash`, or `loop`.
timeout: 15000
```

**Script node** — TypeScript/JavaScript (via `bun`) or Python (via `uv`), no AI, stdout captured as output:
```yaml
- id: transform
script: |
const raw = process.argv.slice(2).join(' ') || '{}';
console.log(JSON.stringify({ parsed: JSON.parse(raw) }));
runtime: bun # 'bun' (.ts/.js) or 'uv' (.py) — REQUIRED
timeout: 30000 # Optional, ms, default 120000

# Or reference a named script from .archon/scripts/ or ~/.archon/scripts/
- id: analyze
script: analyze-metrics # loads .archon/scripts/analyze-metrics.py
runtime: uv
deps: ["pandas>=2.0"] # Optional, uv only — 'uv run --with <dep>'
```
Comment on lines +180 to +194
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm dispatch does not append any positional args after -e or <path>.
rg -nP -C5 "--no-env-file|'-e'|'run'" --type=ts

Repository: coleam00/Archon

Length of output: 514


🏁 Script executed:

#!/bin/bash
# Search for how inline bun scripts are invoked in dispatch/workflow
rg -n "bun.*-e|--no-env-file" --type ts --type yaml -A3 -B3

Repository: coleam00/Archon

Length of output: 50374


🏁 Script executed:

#!/bin/bash
# Read the specific file and lines mentioned in SKILL.md
head -195 .claude/skills/archon/SKILL.md | tail -20

Repository: coleam00/Archon

Length of output: 765


🏁 Script executed:

#!/bin/bash
# Check if script-nodes.md exists and read the relevant sections
fd "script-nodes.md" -x wc -l {}

Repository: coleam00/Archon

Length of output: 122


🏁 Script executed:

#!/bin/bash
# Read SKILL.md lines 180-194 to see the example in question
sed -n '175,200p' .claude/skills/archon/SKILL.md

Repository: coleam00/Archon

Length of output: 942


🏁 Script executed:

#!/bin/bash
# Read script-nodes.md to check lines 184 and 281-287
sed -n '179,190p' packages/docs-web/src/content/docs/guides/script-nodes.md
echo "---"
sed -n '276,295p' packages/docs-web/src/content/docs/guides/script-nodes.md

Repository: coleam00/Archon

Length of output: 952


🏁 Script executed:

#!/bin/bash
# Verify the actual invocation pattern in dag-executor confirms no argv passed
grep -A5 -B2 "'-e'" packages/workflows/src/dag-executor.ts | head -25

Repository: coleam00/Archon

Length of output: 483


Examples use process.argv.slice(2) but inline bun scripts never receive positional arguments.

Per dag-executor.ts:1435, inline bun scripts execute via bun --no-env-file -e '<code>' with no positional args appended. The pattern process.argv.slice(2) in the example at SKILL.md:180-194 (and script-nodes.md:184 and :281-287 for named scripts) will always be empty, silently falling back to '{}'. If demonstrating upstream data passing is the intent, use inline-substituted $nodeId.output references instead (which script bodies do substitute raw, unquoted), or replace with a hard-coded illustrative value. Otherwise readers copy a pattern that never actually receives data.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/archon/SKILL.md around lines 180 - 194, The example uses
process.argv.slice(2) inside an inline bun script but inline scripts are
executed with bun --no-env-file -e '<code>' (per dag-executor.ts:1435) so they
receive no positional args; update the SKILL.md example script body to
demonstrate upstream data passing by using an inline-substituted $nodeId.output
reference (which is substituted raw/unquoted into the script) or replace the
example with a hard-coded illustrative JSON string, and remove or comment out
the process.argv.slice(2) pattern so readers are not misled.


**Loop node** — iterates AI prompt until completion:
```yaml
- id: implement
Expand Down Expand Up @@ -230,7 +246,7 @@ For details: Read `references/dag-advanced.md`

### Example Files

- `examples/dag-workflow.yaml` — workflow with conditions, bash nodes, structured output
- `examples/dag-workflow.yaml` — workflow with conditions, bash + script + loop nodes, structured output
- `examples/command-template.md` — Command file skeleton with all variables

---
Expand Down
28 changes: 25 additions & 3 deletions .claude/skills/archon/examples/dag-workflow.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Example: Workflow with all four node types
# Example: Workflow demonstrating multiple node types
#
# Demonstrates: bash nodes, structured output, when: conditions,
# trigger_rule, per-node model, context: fresh, loop nodes, and output substitution.
# Demonstrates: bash nodes, script nodes (TypeScript via bun), structured output,
# when: conditions, trigger_rule, per-node model, context: fresh, loop nodes,
# and output substitution.
#
# IMPORTANT: This is a reference example. Design your actual workflow
# around the user's specific needs — the number of nodes, their types,
Expand Down Expand Up @@ -42,6 +43,27 @@ nodes:
fi
timeout: 5000

# ── SCRIPT NODE: TypeScript (bun runtime), no AI, stdout captured as output ──
# Deterministic parsing the shell would mangle — extracts labels cleanly as JSON.
#
# NOTE: `$fetch-issue.output` is substituted *raw* into the script body (no shell
# quoting — see reference/variables.md). Wrapping it in a String.raw template
# preserves backslashes and newlines in the JSON payload without needing any
# escaping. Safe here because gh issue view --json emits clean JSON.
- id: extract-labels
script: |
const raw = String.raw`$fetch-issue.output`;
try {
const issue = JSON.parse(raw);
const labels = (issue.labels ?? []).map((l) => l.name);
console.log(JSON.stringify({ labels, count: labels.length }));
Comment on lines +49 to +59
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Demonstrates why raw JSON containing backticks / interpolation markers is unsafe
# inside a JavaScript template literal, but safe as a JSON/JS literal.
python - <<'PY'
payload = '{"body":"contains ` and ${process.exit(1)}","labels":[{"name":"bug"}]}'

print("Current pattern after substitution:")
print(f"const raw = String.raw`{payload}`;")

print("\nSafer literal pattern after substitution:")
print(f"const issue = {payload};")
PY

Repository: coleam00/Archon

Length of output: 319


🏁 Script executed:

cat -n .claude/skills/archon/examples/dag-workflow.yaml | head -80

Repository: coleam00/Archon

Length of output: 3443


🏁 Script executed:

# Check for documentation on variable substitution and timing
find . -name "*.md" -type f | xargs grep -l "variables\|substitution" 2>/dev/null | head -5

Repository: coleam00/Archon

Length of output: 313


🏁 Script executed:

# Look for similar patterns in other YAML files
fd -e yaml -type f | xargs grep -l "String.raw" 2>/dev/null

Repository: coleam00/Archon

Length of output: 231


🏁 Script executed:

# Search for String.raw patterns in YAML files
rg "String.raw" --type yaml -A 2 -B 2

Repository: coleam00/Archon

Length of output: 41


🏁 Script executed:

# Check the reference/variables.md documentation mentioned in the comment
find . -name "variables.md" -o -name "reference.md" | xargs cat 2>/dev/null | head -100

Repository: coleam00/Archon

Length of output: 6435


Remove the unsafe String.raw template literal pattern and treat JSON output as a JavaScript literal.

String.raw does not escape backticks or prevent ${...} interpolation. GitHub issue bodies and comments can contain arbitrary text including backticks and interpolation markers, which will break the template literal or inject code. Since gh --json already emits valid JSON (which is valid JavaScript), assign it directly without parsing overhead or substitution risk.

Fix
-  # NOTE: `$fetch-issue.output` is substituted *raw* into the script body (no shell
-  # quoting — see reference/variables.md). Wrapping it in a String.raw template
-  # preserves backslashes and newlines in the JSON payload without needing any
-  # escaping. Safe here because gh issue view --json emits clean JSON.
+  # NOTE: `$fetch-issue.output` is substituted *raw* into the script body (no shell
+  # quoting — see reference/variables.md). Because gh issue view --json emits JSON,
+  # assign it as a JavaScript literal instead of wrapping in a string.
-      const raw = String.raw`$fetch-issue.output`;
       try {
-        const issue = JSON.parse(raw);
+        const issue = $fetch-issue.output;
         const labels = (issue.labels ?? []).map((l) => l.name);
         console.log(JSON.stringify({ labels, count: labels.length }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/archon/examples/dag-workflow.yaml around lines 49 - 59,
Replace the unsafe template literal in the extract-labels script: remove the use
of String.raw and the backtick template around $fetch-issue.output (the line
declaring const raw), and instead assign the gh JSON output directly to raw as a
JavaScript literal so JSON.parse(raw) can run without risk of backtick or ${...}
injection; update the script under id "extract-labels" to stop using
String.raw`...` while keeping the subsequent JSON.parse(raw) and labels
extraction logic intact.

} catch {
console.log(JSON.stringify({ labels: [], count: 0 }));
}
runtime: bun
depends_on: [fetch-issue]
timeout: 10000
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# ── PROMPT NODE: Inline AI prompt with structured output ──
- id: classify
prompt: |
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/archon/references/dag-advanced.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Advanced Features: Hooks, MCP, Skills, Retry

These features are available on **command and prompt nodes** (hooks, MCP, skills, tool restrictions) and **command, prompt, and bash nodes** (retry, output_format). Loop nodes do not support these features (`retry` on loop nodes is a hard error; others are silently ignored).
These features are available on **command and prompt nodes** (hooks, MCP, skills, tool restrictions, `output_format`, `agents`, Claude SDK options) and **command, prompt, bash, and script nodes** (retry). Loop nodes do not support these features (`retry` on loop nodes is a hard error; others are silently ignored). Bash and script nodes silently ignore AI-specific fields (a loader warning lists the ignored fields).

## Provider Compatibility

Expand Down
1 change: 1 addition & 0 deletions .claude/skills/archon/references/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ All variables are available in all workflows. The only exception is `$nodeId.out
- **Command files** (`.archon/commands/*.md`) — all variables except `$nodeId.output`
- **Inline `prompt:` fields** — in DAG prompt nodes and loop node prompts
- **`bash:` scripts in DAG nodes** — `$nodeId.output` references are automatically shell-quoted (single-quoted with `'` escaped)
- **`script:` bodies in DAG nodes** — same substitution as bash, but `$nodeId.output` values are **NOT** shell-quoted. Parse with `JSON.parse` / `json.loads` rather than interpolating into shell syntax

## Substitution Order

Expand Down
58 changes: 54 additions & 4 deletions .claude/skills/archon/references/workflow-dag.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ nodes:
depends_on: [other-node] # Node IDs that must complete first
```

## Four Node Types (Mutually Exclusive)
## Node Types (Mutually Exclusive)

Each node must have exactly ONE of these fields:
Each node must have exactly ONE of these fields: `command`, `prompt`, `bash`, `script`, `loop`, `approval`, or `cancel`.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### Command Node
Runs a command file from `.archon/commands/`:
Expand Down Expand Up @@ -54,6 +54,54 @@ Runs a shell script without AI:
- **stderr** forwarded as warning, does not fail the node
- No AI invoked — AI-specific fields are ignored
- Use `timeout:` (milliseconds) for execution time limit
- `$nodeId.output` substitutions are **auto shell-quoted** (safe to embed)

### Script Node
Runs TypeScript/JavaScript (via `bun`) or Python (via `uv`) without AI. Same stdout/stderr contract as bash nodes.

**Inline script (TypeScript):**
```yaml
- id: parse
script: |
const raw = process.argv.slice(2).join(' ') || '{}';
const data = JSON.parse(raw);
console.log(JSON.stringify({ items: data.items?.length ?? 0 }));
runtime: bun # REQUIRED: 'bun' or 'uv'
timeout: 30000 # ms, default: 120000
Comment on lines +62 to +70
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Do not show process.argv as the input path for inline Bun scripts.

Inline Bun scripts are executed as bun --no-env-file -e <code> with no extra argv, so this example always falls back to {} and emits items: 0. Show $nodeId.output substitution or make the example explicitly self-contained.

📝 Proposed fix
 **Inline script (TypeScript):**
 ```yaml
 - id: parse
   script: |
-    const raw = process.argv.slice(2).join(' ') || '{}';
-    const data = JSON.parse(raw);
+    const data = $fetch-data.output;
     console.log(JSON.stringify({ items: data.items?.length ?? 0 }));
   runtime: bun                      # REQUIRED: 'bun' or 'uv'
   timeout: 30000                    # ms, default: 120000
+  depends_on: [fetch-data]

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @.claude/skills/archon/references/workflow-dag.md around lines 62 - 70, The
inline Bun example for the step with id "parse" incorrectly uses process.argv
(which is empty when run with bun -e) causing the script to always parse "{}";
replace that input path with a proper workflow substitution (e.g. use
$fetch-data.output or $nodeId.output) or make the script self-contained, and add
the corresponding dependency (e.g. depends_on: [fetch-data]) so the "parse" step
reads the actual JSON payload and logs items correctly; update the script block
under id: parse and ensure runtime: bun and timeout remain unchanged.


</details>

<!-- fingerprinting:phantom:poseidon:ibis -->

<!-- This is an auto-generated comment by CodeRabbit -->

```

**Inline script (Python) with uv dependencies:**
```yaml
- id: fetch
script: |
import httpx, json
r = httpx.get("https://api.github.com/repos/anthropics/anthropic-cookbook")
print(json.dumps({ "stars": r.json()["stargazers_count"] }))
runtime: uv
deps: ["httpx>=0.27"] # Optional — 'uv run --with <dep>'. Ignored for bun.
```

**Named script from `.archon/scripts/`:**
```yaml
- id: analyze
script: analyze-metrics # Resolves .archon/scripts/analyze-metrics.py
runtime: uv # Must match file extension (.ts/.js → bun, .py → uv)
deps: ["pandas>=2.0"]
```

- **Inline vs named**: a `script` value is treated as inline code if it contains a newline or any shell metacharacter (space, or any of: `;` `(` `)` `{` `}` `&` `|` `<` `>` `$` `` ` `` `"` `'`). Otherwise it's a named-script lookup (bare identifier).
- **Named script resolution**: `<cwd>/.archon/scripts/` (wins) → `~/.archon/scripts/`. 1-level subfolder grouping allowed. Extension determines runtime (`.ts`/`.js` → `bun`, `.py` → `uv`) and MUST match the declared `runtime:`
- **Dispatch**:
- `bun` + inline → `bun --no-env-file -e '<code>'`
- `bun` + named → `bun --no-env-file run <path>`
- `uv` + inline → `uv run [--with dep ...] python -c '<code>'`
- `uv` + named → `uv run [--with dep ...] <path>`
- **`deps`** is uv-only. Bun auto-installs on import; `deps` with `runtime: bun` emits a validator warning
- **stdout** captured as `$nodeId.output` (trailing newline trimmed)
- **stderr** forwarded as warning, does NOT fail the node. Non-zero exit DOES fail it.
- **`bun --no-env-file`** prevents target repo `.env` from leaking into the subprocess
- `$nodeId.output` substitutions are **NOT shell-quoted** in script bodies — parse with `JSON.parse` / `json.loads`, don't interpolate into shell syntax
- AI-specific fields (`model`, `provider`, `hooks`, `mcp`, `skills`, `output_format`, `allowed_tools`, `denied_tools`, `agents`, `effort`, `thinking`, `maxBudgetUsd`, `systemPrompt`, `fallbackModel`, `betas`, `sandbox`) emit a loader warning and are ignored

### Loop Node
Iterates an AI prompt until a completion signal or max iterations:
Expand Down Expand Up @@ -83,7 +131,7 @@ All node types share these fields:
| `depends_on` | string[] | `[]` | Node IDs that must settle before this node runs |
| `when` | string | — | Condition expression. Node **skipped** when false |
| `trigger_rule` | string | `all_success` | Join semantics for multiple dependencies |
| `idle_timeout` | number (ms) | 300000 | Per-node idle timeout. On loop nodes, applies per-iteration |
| `idle_timeout` | number (ms) | 300000 | Idle timeout for AI streaming (`command`, `prompt`) and per-iteration idle for `loop`. Accepted but ignored on `bash` and `script` — use `timeout` there |

**Command, prompt, and bash nodes** (silently ignored on loop nodes, except `retry` which is a hard error):

Expand Down Expand Up @@ -302,7 +350,9 @@ Use `--json` for machine-readable output. Use `archon validate commands <name>`
- All `depends_on` reference existing IDs
- No cycles
- `$nodeId.output` refs in `when:`, `prompt:`, `loop.prompt:` must point to known IDs
- Exactly one of `command`, `prompt`, `bash`, `loop` per node
- Exactly one of `command`, `prompt`, `bash`, `script`, `loop`, `approval`, `cancel` per node
- Script nodes require `runtime: bun` or `runtime: uv`
- Named scripts must exist in `.archon/scripts/` or `~/.archon/scripts/` with extension matching declared runtime
- `retry` on loop node = hard error
- `steps:` format rejected (deprecated — use `nodes:` only)

Expand Down
2 changes: 1 addition & 1 deletion packages/docs-web/src/content/docs/adapters/web.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ Click on a workflow run (from the dashboard or progress card) to open the execut
The Workflow Builder at `/workflows/builder` provides a visual editor for creating and modifying workflow YAML files. Features include:

- **DAG canvas** -- Drag-and-drop nodes to build your workflow graph visually
- **Node palette** -- Add command, prompt, bash, and loop nodes from a sidebar library
- **Node palette** -- Drag command, prompt, and bash nodes from a sidebar library. Additional node types (`script`, `loop`, `approval`, `cancel`) are editable via the Code / Split view
- **Node inspector** -- Click a node to configure its properties (command, prompt text, dependencies, model overrides, hooks, MCP servers, etc.) in a tabbed panel
- **View modes** -- Toggle between Visual, Split, and Code views. Split mode shows the canvas and YAML side by side.
- **Command picker** -- Browse available commands when configuring command nodes
Expand Down
5 changes: 4 additions & 1 deletion packages/docs-web/src/content/docs/book/dag-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,14 +230,17 @@ The classify-and-route example uses `none_failed_min_one_success` on `implement`

## Node Types

Archon supports four node types:
Archon supports seven node types:

| Type | Syntax | When to use |
|------|--------|-------------|
| **Command** | `command: my-command` | Load a command from `.archon/commands/my-command.md`. The standard choice. |
| **Prompt** | `prompt: "inline instructions..."` | Quick, one-off instructions that don't need a reusable command file. |
| **Bash** | `bash: "shell command"` | Run a shell script without AI. Stdout is captured as `$nodeId.output`. Deterministic operations only. |
| **Script** | `script: "..." runtime: bun\|uv` | TypeScript (via bun) or Python (via uv) — deterministic typed transforms where bash would need fragile quoting. Stdout is captured as `$nodeId.output`. See [Script Nodes](/guides/script-nodes/). |
| **Loop** | `loop: { prompt: "...", until: SIGNAL }` | Repeat an AI prompt until a completion signal appears in the output. See [Loop Nodes](/guides/loop-nodes/). |
| **Approval** | `approval: { message: "..." }` | Pause the run for human review before continuing. See [Approval Nodes](/guides/approval-nodes/). |
| **Cancel** | `cancel: "reason string"` | Terminate the run with a reason (useful as a `when:`-gated branch for safety checks). |

**Command** is the most common. Use it for anything you'll reuse across workflows.

Expand Down
3 changes: 3 additions & 0 deletions packages/docs-web/src/content/docs/book/quick-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ All nodes share these base fields:
| `command` | One of | string | Name of a command file in `.archon/commands/` |
| `prompt` | One of | string | Inline AI instructions |
| `bash` | One of | string | Shell script (runs without AI; stdout captured as `$nodeId.output`) |
| `script` | One of | string | TypeScript/JS (via bun) or Python (via uv); requires `runtime:` (`bun` or `uv`); optional `deps:` (uv only) and `timeout:` (ms). Stdout captured as `$nodeId.output`. See [Script Nodes](/guides/script-nodes/) |
| `loop` | One of | object | Loop configuration (see Loop Options below) |
| `approval` | One of | object | Human-review gate; pauses the run until approved or rejected. See [Approval Nodes](/guides/approval-nodes/) |
| `cancel` | One of | string | Terminates the run with the given reason string |
| `depends_on` | No | string[] | Node IDs that must complete before this node runs |
| `when` | No | string | Condition expression; node is skipped if false |
| `trigger_rule` | No | string | Join semantics when multiple upstreams exist (see Trigger Rules) |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ nodes:
| `command` | string | Command name to load from `.archon/commands/` |
| `prompt` | string | Inline prompt string |
| `bash` | string | Shell script (no AI). Stdout captured as `$nodeId.output`. Optional `timeout` (ms, default 120000) |
| `script` | string | TypeScript/JavaScript (via `bun`) or Python (via `uv`) — inline code or named reference to `.archon/scripts/`. Stdout captured as `$nodeId.output`. Requires `runtime: bun` or `runtime: uv`. Optional `deps` (uv only) and `timeout` (ms, default 120000). See [Script Nodes](/guides/script-nodes/) |
| `loop` | object | Iterative AI prompt until completion signal. See [Loop Nodes](/guides/loop-nodes/) |
| `approval` | object | Pauses workflow for human review. See [Approval Nodes](/guides/approval-nodes/) |
| `cancel` | string | Terminates the workflow run with a reason string. Uses existing cancellation plumbing — in-flight parallel nodes are stopped |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ area: workflows
audience: [user]
status: current
sidebar:
order: 8
order: 9
---

Workflows placed in `~/.archon/workflows/`, commands in `~/.archon/commands/`, and scripts in `~/.archon/scripts/` are loaded globally -- they appear in every project and can be invoked from any repository. Workflows and commands carry the `source: 'global'` label in the Web UI node palette; scripts resolve under the same repo-wins-over-home precedence.
Expand Down
2 changes: 1 addition & 1 deletion packages/docs-web/src/content/docs/guides/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ area: workflows
audience: [user]
status: current
sidebar:
order: 5
order: 6
---

DAG workflow nodes support a `hooks` field that attaches Claude Agent SDK hooks
Expand Down
1 change: 1 addition & 0 deletions packages/docs-web/src/content/docs/guides/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ How-to guides for building and running AI coding workflows with Archon.

- [Loop Nodes](/guides/loop-nodes/) — Iterative AI execution with completion conditions and deterministic exit checks
- [Approval Nodes](/guides/approval-nodes/) — Human review gates with optional AI rework on rejection
- [Script Nodes](/guides/script-nodes/) — TypeScript/JavaScript (bun) or Python (uv) as a deterministic DAG node, without AI

## Node Features (Claude only)

Expand Down
2 changes: 1 addition & 1 deletion packages/docs-web/src/content/docs/guides/mcp-servers.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ area: workflows
audience: [user]
status: current
sidebar:
order: 6
order: 7
---

DAG workflow nodes support a `mcp` field that attaches MCP (Model Context Protocol)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ area: workflows
audience: [user]
status: current
sidebar:
order: 9
order: 10
---

The `archon-remotion-generate` workflow uses AI to create Remotion video compositions.
Expand Down
Loading
Loading