docs(script-nodes): dedicated guide + teach the archon skill#1362
docs(script-nodes): dedicated guide + teach the archon skill#1362
Conversation
📝 WalkthroughWalkthroughThis PR introduces comprehensive documentation and configuration support for a new Script Node type in DAG workflows. The Script Node enables deterministic execution of TypeScript/JavaScript code (via Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.claude/skills/archon/references/dag-advanced.md (1)
294-295:⚠️ Potential issue | 🟡 MinorInconsistent with the updated retry matrix at Line 3.
Line 3 now states retry is supported on command, prompt, bash, and script nodes, but this section header still says "Available on command, prompt, and bash nodes." Script nodes are missing here, which contradicts the new support matrix at the top of the file.
📝 Proposed fix
-Available on command, prompt, and bash nodes. **Not supported on loop nodes** (hard error at load time). +Available on command, prompt, bash, and script nodes. **Not supported on loop nodes** (hard error at load time).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.claude/skills/archon/references/dag-advanced.md around lines 294 - 295, The header string "Available on command, prompt, and bash nodes. **Not supported on loop nodes**" is inconsistent with the retry matrix; update that header to include "script" nodes so it reads "Available on command, prompt, bash, and script nodes. **Not supported on loop nodes**" (leave the loop-node limitation unchanged) so the section matches the retry matrix referenced at Line 3 and any mentions of retry support in this file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/skills/archon/examples/dag-workflow.yaml:
- Around line 46-60: The extract-labels script currently reads
process.env.ISSUE_JSON which is never set; instead inject the upstream node
output ($fetch-issue.output) directly into the inline script for the
extract-labels node so the Parse logic consumes the real issue JSON. Replace the
ENV-based read in the extract-labels node's script with a raw-substitution
approach that assigns the literal $fetch-issue.output string to a local variable
(taking care to wrap/escape it correctly) and then JSON.parse that variable;
update the script block in the extract-labels node (referenced as
"extract-labels") and remove reliance on ISSUE_JSON and the fallback branch that
always returns an empty array.
In @.claude/skills/archon/SKILL.md:
- Around line 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.
In `@packages/docs-web/src/content/docs/guides/script-nodes.md`:
- Around line 274-296: The named-script example triage-fmt.ts incorrectly reads
arguments via process.argv.slice(2) (and the Python example uses sys.argv[1]),
which contradicts the docs saying named scripts receive no argv; update these
examples to read input from environment variables or stdin instead (e.g.,
replace process.argv usage with reading process.env variables such as
USER_MESSAGE or reading from stdin, and replace sys.argv usage with os.environ
or stdin reads) so the helper (triage-fmt.ts) and the Python example follow the
documented named-script behavior.
---
Outside diff comments:
In @.claude/skills/archon/references/dag-advanced.md:
- Around line 294-295: The header string "Available on command, prompt, and bash
nodes. **Not supported on loop nodes**" is inconsistent with the retry matrix;
update that header to include "script" nodes so it reads "Available on command,
prompt, bash, and script nodes. **Not supported on loop nodes**" (leave the
loop-node limitation unchanged) so the section matches the retry matrix
referenced at Line 3 and any mentions of retry support in this file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 20a7dd80-4e0c-4c8f-a112-7c9de3b74baf
📒 Files selected for processing (14)
.claude/skills/archon/SKILL.md.claude/skills/archon/examples/dag-workflow.yaml.claude/skills/archon/references/dag-advanced.md.claude/skills/archon/references/variables.md.claude/skills/archon/references/workflow-dag.mdpackages/docs-web/src/content/docs/guides/authoring-workflows.mdpackages/docs-web/src/content/docs/guides/global-workflows.mdpackages/docs-web/src/content/docs/guides/hooks.mdpackages/docs-web/src/content/docs/guides/index.mdpackages/docs-web/src/content/docs/guides/mcp-servers.mdpackages/docs-web/src/content/docs/guides/remotion-workflow.mdpackages/docs-web/src/content/docs/guides/script-nodes.mdpackages/docs-web/src/content/docs/guides/skills.mdpackages/docs-web/src/content/docs/reference/variables.md
| **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>' | ||
| ``` |
There was a problem hiding this comment.
🧩 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=tsRepository: 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 -B3Repository: 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 -20Repository: 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.mdRepository: 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.mdRepository: 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 -25Repository: 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.
| ### Reusable helper in `~/.archon/scripts/` | ||
|
|
||
| A helper you want available in every repo — say, a triage summary formatter — | ||
| lives at `~/.archon/scripts/triage-fmt.ts`: | ||
|
|
||
| ```typescript | ||
| // ~/.archon/scripts/triage-fmt.ts | ||
| const raw = process.argv.slice(2).join(' ') || '{}'; | ||
| const data = JSON.parse(raw); | ||
| const lines = data.issues?.map((i: { id: string; title: string }) => | ||
| `- [${i.id}] ${i.title}` | ||
| ).join('\n') ?? ''; | ||
| console.log(lines || 'no issues'); | ||
| ``` | ||
|
|
||
| Then reference it by name from any repo's workflow: | ||
|
|
||
| ```yaml | ||
| - id: format | ||
| script: triage-fmt | ||
| runtime: bun | ||
| depends_on: [gather] | ||
| ``` |
There was a problem hiding this comment.
Named-script example contradicts the "named scripts don't get argv" rule stated earlier.
Lines 202–205 state: "For named scripts, variables are not passed automatically. Read them from the environment (process.env.USER_MESSAGE, os.environ['USER_MESSAGE']) or accept them via stdin." But the triage-fmt.ts named-script example reads process.argv.slice(2).join(' '), which won't be populated — the dispatch is bun --no-env-file run <path> with no trailing args. A reader copying this will get '{}' every time.
Rewrite the helper to read from process.env (matching the doc's own guidance) or from stdin:
📝 Proposed fix (env-based)
// ~/.archon/scripts/triage-fmt.ts
-const raw = process.argv.slice(2).join(' ') || '{}';
+const raw = process.env.TRIAGE_INPUT ?? '{}';
const data = JSON.parse(raw);Same issue applies to the inline example at Line 184 and the Python example at Line 305 (sys.argv[1]).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/docs-web/src/content/docs/guides/script-nodes.md` around lines 274 -
296, The named-script example triage-fmt.ts incorrectly reads arguments via
process.argv.slice(2) (and the Python example uses sys.argv[1]), which
contradicts the docs saying named scripts receive no argv; update these examples
to read input from environment variables or stdin instead (e.g., replace
process.argv usage with reading process.env variables such as USER_MESSAGE or
reading from stdin, and replace sys.argv usage with os.environ or stdin reads)
so the helper (triage-fmt.ts) and the Python example follow the documented
named-script behavior.
PR Review Summary — multi-agent reviewReviewed with Critical Issues (1)
Important Issues (2)
Suggestions (1)
Documentation Follow-ups (confirmed stale — author knows)These are the
One additional stale minor hit the author didn't call out:
Strengths
VerdictNEEDS FIXES — one critical (the broken script example teaches the wrong pattern and will propagate), one important doc inaccuracy ( Recommended Actions
|
Review follow-ups applied in 5f1e588Addressed all findings from the multi-agent review and the Windows CI failure. Critical
Important
Docs-only follow-ups (author had declared out-of-scope, fixed anyway)
Windows CI failure (unrelated to docs)
Validation
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/docs-web/src/content/docs/book/quick-reference.md (1)
140-146:⚠️ Potential issue | 🟡 MinorClarify
idle_timeoutvstimeoutfor bash/script nodes.Line 140 lists
idle_timeoutas a common field without mentioning that bash and script nodes ignore it in favor of their owntimeoutfield. Line 146's note addresses bashtimeoutbut doesn't mention script nodes or explain the relationship toidle_timeout.Per the commit message, this clarification was intended but appears incomplete. Consider updating line 140 or expanding the note at line 146 to explicitly state that bash and script nodes use
timeout(in milliseconds) and ignoreidle_timeout.📝 Suggested clarification
Option 1 - Update line 140:
-| `idle_timeout` | No | number | Per-node idle timeout in milliseconds (default: 5 minutes) | +| `idle_timeout` | No | number | Per-node idle timeout in milliseconds (default: 5 minutes). Ignored for bash and script nodes; use `timeout` instead |Option 2 - Expand the note at line 146:
-> **bash node timeout**: The `timeout` field on bash nodes is in **milliseconds** (default: 120000). This differs from hook `timeout`, which is in seconds. +> **bash and script node timeout**: The `timeout` field on bash and script nodes is in **milliseconds** (default: 120000 for bash). Bash and script nodes ignore the common `idle_timeout` field and use `timeout` instead. Hook `timeout` is in seconds.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/docs-web/src/content/docs/book/quick-reference.md` around lines 140 - 146, The docs currently list the `idle_timeout` common field but don't say that bash and script nodes ignore it; update the documentation so it explicitly states that bash and script nodes use their own `timeout` field (not `idle_timeout`) and that this `timeout` is in milliseconds (default: 120000), either by amending the `idle_timeout` description to note the exception for bash/script nodes or by expanding the existing bash `timeout` note to mention script nodes and the `idle_timeout` override behavior.
🧹 Nitpick comments (1)
.archon/workflows/defaults/archon-workflow-builder.yaml (1)
64-65: Includecancelin the builder’s node-type choices or call out the exclusion.The workflow reference now lists
cancelalongsidescriptandapproval, but this builder still asks the model to choose only six node types. Ifcancelis supported for generated workflows, add it here too; otherwise state that the builder intentionally avoids generating cancel nodes.Based on learnings, workflow nodes support types:
command,prompt,bash,script,loop,approval,cancel.Also applies to: 119-119
🤖 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 around lines 64 - 65, The builder's node-type choice list is inconsistent with the workflow reference: either add "cancel" to the allowed node types in the builder's node-type choices (so the set includes command, prompt, bash, script, loop, approval, cancel) or explicitly document in the builder text that "cancel" is intentionally excluded; update the node-type choices definition used by the builder (the list/array or inline choices for node types) to include "cancel" if supported, or add a sentence calling out the intentional exclusion next to the existing choices so the reference and builder stay in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.archon/workflows/defaults/archon-workflow-builder.yaml:
- 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.
In @.claude/skills/archon/examples/dag-workflow.yaml:
- Around line 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.
In @.claude/skills/archon/references/workflow-dag.md:
- Around line 23-25: The quick schema example at the top of the "Node Types
(Mutually Exclusive)" section is out of sync: update the opening schema snippet
that currently lists only `prompt`, `command`, `bash`, and `loop` so it matches
the expanded list (`command`, `prompt`, `bash`, `script`, `loop`, `approval`,
`cancel`) used in the "Node Types (Mutually Exclusive)" text; locate the "Node
Types (Mutually Exclusive)" header and the opening schema example referenced as
"nodes" and edit that snippet to include `script`, `approval`, and `cancel` so
the doc is consistent.
- Around line 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.
---
Outside diff comments:
In `@packages/docs-web/src/content/docs/book/quick-reference.md`:
- Around line 140-146: The docs currently list the `idle_timeout` common field
but don't say that bash and script nodes ignore it; update the documentation so
it explicitly states that bash and script nodes use their own `timeout` field
(not `idle_timeout`) and that this `timeout` is in milliseconds (default:
120000), either by amending the `idle_timeout` description to note the exception
for bash/script nodes or by expanding the existing bash `timeout` note to
mention script nodes and the `idle_timeout` override behavior.
---
Nitpick comments:
In @.archon/workflows/defaults/archon-workflow-builder.yaml:
- Around line 64-65: The builder's node-type choice list is inconsistent with
the workflow reference: either add "cancel" to the allowed node types in the
builder's node-type choices (so the set includes command, prompt, bash, script,
loop, approval, cancel) or explicitly document in the builder text that "cancel"
is intentionally excluded; update the node-type choices definition used by the
builder (the list/array or inline choices for node types) to include "cancel" if
supported, or add a sentence calling out the intentional exclusion next to the
existing choices so the reference and builder stay in sync.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3fbdbb0f-9897-4459-8f3f-c4851826f16a
📒 Files selected for processing (9)
.archon/workflows/defaults/archon-workflow-builder.yaml.claude/skills/archon/examples/dag-workflow.yaml.claude/skills/archon/references/workflow-dag.mdpackages/docs-web/src/content/docs/adapters/web.mdpackages/docs-web/src/content/docs/book/dag-workflows.mdpackages/docs-web/src/content/docs/book/quick-reference.mdpackages/docs-web/src/content/docs/guides/script-nodes.mdpackages/providers/src/claude/binary-resolver.test.tspackages/workflows/src/defaults/bundled-defaults.generated.ts
✅ Files skipped from review due to trivial changes (3)
- packages/docs-web/src/content/docs/adapters/web.md
- packages/docs-web/src/content/docs/book/dag-workflows.md
- packages/docs-web/src/content/docs/guides/script-nodes.md
| # --- 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`; |
There was a problem hiding this comment.
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 interpolationAlso 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.
| # 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 })); |
There was a problem hiding this comment.
🧩 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};")
PYRepository: coleam00/Archon
Length of output: 319
🏁 Script executed:
cat -n .claude/skills/archon/examples/dag-workflow.yaml | head -80Repository: 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 -5Repository: 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/nullRepository: coleam00/Archon
Length of output: 231
🏁 Script executed:
# Search for String.raw patterns in YAML files
rg "String.raw" --type yaml -A 2 -B 2Repository: 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 -100Repository: 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.
| **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 |
There was a problem hiding this comment.
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 -->
…w to write them Script nodes (script:) have been a first-class DAG node type since v0.3.3 but were documented only as one-liners in CLAUDE.md and a CI smoke test. Claude Code reading the archon skill would see "Four Node Types: command, prompt, bash, loop" and reach for bash+node/python one-liners instead of a proper script node — losing bun's --no-env-file isolation, uv's --with dependency pins, and the .archon/scripts/ reuse story. - New packages/docs-web/src/content/docs/guides/script-nodes.md mirroring the structure of loop-nodes.md / approval-nodes.md: schema, inline vs named dispatch, runtime/deps semantics, scripts directory precedence (repo > home), extension-runtime mapping, env isolation, stdout/stderr contract, patterns, and the explicit list of ignored AI fields. - guides/authoring-workflows.md and guides/index.md updated so the new guide is discoverable from both the node-types table and the guides landing page. - reference/variables.md calls out the no-shell-quote difference between bash: and script: substitution — a subtle correctness trap when adapting a bash pattern into a script node. - Sidebar order bumped +1 on hooks/mcp-servers/skills/global-workflows/ remotion-workflow to slot script-nodes at order 5 next to the other node-type guides. - .claude/skills/archon/SKILL.md: replaces stale "Four Node Types" (which also silently omitted approval and cancel) with the accurate seven, with a script-node code block showing both inline and named patterns. - references/workflow-dag.md: full Script Node section covering dispatch, resolution, deps, stdout contract, and the list of AI-only fields that are ignored; validation-rules list updated. - references/dag-advanced.md and references/variables.md: retry-support line corrected; no-shell-quote note added. - examples/dag-workflow.yaml: added an extract-labels TypeScript script node and updated the header comment.
- skills example: extract-labels was reading process.env.ISSUE_JSON which is never set; use String.raw`$fetch-issue.output` so the upstream bash node's JSON is actually consumed - guides/script-nodes.md + skills/workflow-dag.md: idle_timeout is accepted but ignored on script (and bash) nodes — executeScriptNode only reads node.timeout. Clarify that script/bash use `timeout`, not idle_timeout - archon-workflow-builder.yaml: prompt enumerated only bash/prompt/command/loop, so the AI builder could never propose script or approval nodes. Add both (plus examples + rule about script output not being shell-quoted) and regenerate bundled defaults - book/dag-workflows.md + book/quick-reference.md + adapters/web.md: fill in the node-type references that were missing script, approval, and cancel. adapters/web.md also overclaimed "loop" in the palette — NodePalette.tsx only drags command/prompt/bash, so note that the other kinds are YAML-only
5f1e588 to
0d65696
Compare
|
Caught a dupe: Cole already landed the Windows CI fix on Amended |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
.claude/skills/archon/references/workflow-dag.md (2)
62-71:⚠️ Potential issue | 🟡 MinorReplace
process.argvin the inline script example.Inline
bunscripts are invoked with-eand no extra args, so this parses{}and emitsitems: 0. Use$nodeId.outputsubstitution or make the example self-contained.Proposed fix using upstream JSON output
**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 - 71, The
inline TypeScript example for the task with id "parse" incorrectly uses
process.argv (which doesn't receive args when bun runs inline with -e), so
replace that parsing logic with a proper upstream substitution or self-contained
data: reference the upstream node output via the substitution token (e.g. use
$fetch-data.output / $nodeId.output) and add a dependency (depends_on:
[fetch-data]) to ensure the source exists; update the script body in the "parse"
example to read from that substitution (and keep the console.log of items
length) and remove the process.argv-based parsing.</details> --- `19-25`: _⚠️ Potential issue_ | _🟡 Minor_ **Update the quick schema snippet to include the new node types.** The schema example still lists only `prompt`, `command`, `bash`, and `loop`, contradicting the expanded seven-node list below. <details> <summary>Proposed fix</summary> ```diff - prompt: "Inline AI prompt" # OR command: name OR bash: "script" OR loop: {...} + prompt: "Inline AI prompt" # OR command / bash / script / loop / approval / cancel🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.claude/skills/archon/references/workflow-dag.md around lines 19 - 25, The quick schema example is out of sync: update the snippet that currently shows only prompt/command/bash/loop to include the new node types `script`, `approval`, and `cancel` so it matches the "Mutually Exclusive" list; locate the example node (fields like `prompt: "Inline AI prompt"`, `depends_on: [...]`) and add the missing keys (`script`, `approval`, `cancel`) or a comment listing them so the sample demonstrates all seven mutually exclusive node fields (`command`, `prompt`, `bash`, `script`, `loop`, `approval`, `cancel`)..claude/skills/archon/examples/dag-workflow.yaml (1)
49-57:⚠️ Potential issue | 🟠 MajorAvoid wrapping raw node output in a template literal.
String.rawstill allows backticks and${...}in issue bodies to break or interpolate inside the generated script. Sincegh --jsonoutput is already valid JSON/JavaScript literal syntax, assign it directly instead.Proposed 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 it in a string. - id: extract-labels script: | - 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);🤖 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 - 57, The script under the extract-labels node currently assigns the raw GH output using String.raw which can be broken by backticks or ${...}; change the assignment so the node output is used directly (remove the String.raw template wrapper around $fetch-issue.output) and parse it with JSON.parse as before (refer to the extract-labels node, the script block, and the const raw / JSON.parse usage to locate the lines to modify)..claude/skills/archon/SKILL.md (1)
180-188:⚠️ Potential issue | 🟡 MinorDo not use
process.argvfor inline script-node input.Inline
bunscript nodes do not receive positional args, so this example silently falls back to{}. Show raw$nodeId.outputsubstitution or make the snippet explicitly self-contained.Proposed fix using the preceding `fetch-data` example
- id: transform script: | - const raw = process.argv.slice(2).join(' ') || '{}'; - console.log(JSON.stringify({ parsed: JSON.parse(raw) })); + const data = $fetch-data.output; + console.log(JSON.stringify({ parsed: data })); runtime: bun # 'bun' (.ts/.js) or 'uv' (.py) — REQUIRED timeout: 30000 # Optional, ms, default 120000 + depends_on: [fetch-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 - 188, The inline script node named "transform" incorrectly uses process.argv (which is empty for inline bun scripts), causing silent fallback to '{}'; update the script example to either read the upstream node output via the substitution placeholder (e.g., use the raw $nodeId.output substitution for the input) or make the snippet self-contained by reading from stdin or embedding a hardcoded JSON string, and update the YAML snippet around the "script" and "runtime: bun" entries so the example shows the correct input mechanism instead of process.argv.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/docs-web/src/content/docs/reference/variables.md`:
- Line 15: Update the sentence about variable substitution to match the
seven-node model: either expand the list to include `approval:` and `cancel:`
along with `command:`, `prompt:`, `bash:`, `script:`, and `loop:`, or reword the
sentence to say variables are substituted in "text-bearing node fields" (or
equivalent) if `approval:` and `cancel:` are intentionally excluded; ensure the
chosen wording accurately reflects where substitution actually occurs and uses
the node type tokens (`command:`, `prompt:`, `bash:`, `script:`, `loop:`,
`approval:`, `cancel:`) so readers can easily find the relevant nodes.
---
Duplicate comments:
In @.claude/skills/archon/examples/dag-workflow.yaml:
- Around line 49-57: The script under the extract-labels node currently assigns
the raw GH output using String.raw which can be broken by backticks or ${...};
change the assignment so the node output is used directly (remove the String.raw
template wrapper around $fetch-issue.output) and parse it with JSON.parse as
before (refer to the extract-labels node, the script block, and the const raw /
JSON.parse usage to locate the lines to modify).
In @.claude/skills/archon/references/workflow-dag.md:
- Around line 62-71: The inline TypeScript example for the task with id "parse"
incorrectly uses process.argv (which doesn't receive args when bun runs inline
with -e), so replace that parsing logic with a proper upstream substitution or
self-contained data: reference the upstream node output via the substitution
token (e.g. use $fetch-data.output / $nodeId.output) and add a dependency
(depends_on: [fetch-data]) to ensure the source exists; update the script body
in the "parse" example to read from that substitution (and keep the console.log
of items length) and remove the process.argv-based parsing.
- Around line 19-25: The quick schema example is out of sync: update the snippet
that currently shows only prompt/command/bash/loop to include the new node types
`script`, `approval`, and `cancel` so it matches the "Mutually Exclusive" list;
locate the example node (fields like `prompt: "Inline AI prompt"`, `depends_on:
[...]`) and add the missing keys (`script`, `approval`, `cancel`) or a comment
listing them so the sample demonstrates all seven mutually exclusive node fields
(`command`, `prompt`, `bash`, `script`, `loop`, `approval`, `cancel`).
In @.claude/skills/archon/SKILL.md:
- Around line 180-188: The inline script node named "transform" incorrectly uses
process.argv (which is empty for inline bun scripts), causing silent fallback to
'{}'; update the script example to either read the upstream node output via the
substitution placeholder (e.g., use the raw $nodeId.output substitution for the
input) or make the snippet self-contained by reading from stdin or embedding a
hardcoded JSON string, and update the YAML snippet around the "script" and
"runtime: bun" entries so the example shows the correct input mechanism instead
of process.argv.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e8e07b95-57fa-442d-8c0e-d625093f42ff
📒 Files selected for processing (19)
.archon/workflows/defaults/archon-workflow-builder.yaml.claude/skills/archon/SKILL.md.claude/skills/archon/examples/dag-workflow.yaml.claude/skills/archon/references/dag-advanced.md.claude/skills/archon/references/variables.md.claude/skills/archon/references/workflow-dag.mdpackages/docs-web/src/content/docs/adapters/web.mdpackages/docs-web/src/content/docs/book/dag-workflows.mdpackages/docs-web/src/content/docs/book/quick-reference.mdpackages/docs-web/src/content/docs/guides/authoring-workflows.mdpackages/docs-web/src/content/docs/guides/global-workflows.mdpackages/docs-web/src/content/docs/guides/hooks.mdpackages/docs-web/src/content/docs/guides/index.mdpackages/docs-web/src/content/docs/guides/mcp-servers.mdpackages/docs-web/src/content/docs/guides/remotion-workflow.mdpackages/docs-web/src/content/docs/guides/script-nodes.mdpackages/docs-web/src/content/docs/guides/skills.mdpackages/docs-web/src/content/docs/reference/variables.mdpackages/workflows/src/defaults/bundled-defaults.generated.ts
✅ Files skipped from review due to trivial changes (11)
- packages/docs-web/src/content/docs/guides/skills.md
- packages/docs-web/src/content/docs/guides/hooks.md
- packages/docs-web/src/content/docs/guides/remotion-workflow.md
- packages/docs-web/src/content/docs/guides/global-workflows.md
- packages/docs-web/src/content/docs/guides/index.md
- packages/docs-web/src/content/docs/guides/mcp-servers.md
- .claude/skills/archon/references/variables.md
- packages/docs-web/src/content/docs/adapters/web.md
- packages/docs-web/src/content/docs/book/dag-workflows.md
- packages/docs-web/src/content/docs/guides/authoring-workflows.md
- packages/docs-web/src/content/docs/guides/script-nodes.md
🚧 Files skipped from review as they are similar to previous changes (3)
- .claude/skills/archon/references/dag-advanced.md
- packages/docs-web/src/content/docs/book/quick-reference.md
- .archon/workflows/defaults/archon-workflow-builder.yaml
| ## Workflow Variables | ||
|
|
||
| These variables are substituted by the workflow executor in all node types (`command:`, `prompt:`, `bash:`, `loop:`). | ||
| These variables are substituted by the workflow executor in all node types (`command:`, `prompt:`, `bash:`, `script:`, `loop:`). |
There was a problem hiding this comment.
Make the “all node types” list match the seven-node model.
This says “all node types” but omits approval: and cancel:. Either include them if variables are substituted there, or change the wording to “text-bearing node fields” to avoid overpromising.
Proposed wording if approval/cancel are intentionally excluded
-These variables are substituted by the workflow executor in all node types (`command:`, `prompt:`, `bash:`, `script:`, `loop:`).
+These variables are substituted by the workflow executor in text-bearing node fields (`command:`, `prompt:`, `bash:`, `script:`, `loop.prompt:`, etc.).Based on learnings, workflow nodes support command, prompt, bash, script, loop, approval, and cancel.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| These variables are substituted by the workflow executor in all node types (`command:`, `prompt:`, `bash:`, `script:`, `loop:`). | |
| These variables are substituted by the workflow executor in text-bearing node fields (`command:`, `prompt:`, `bash:`, `script:`, `loop.prompt:`, etc.). |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/docs-web/src/content/docs/reference/variables.md` at line 15, Update
the sentence about variable substitution to match the seven-node model: either
expand the list to include `approval:` and `cancel:` along with `command:`,
`prompt:`, `bash:`, `script:`, and `loop:`, or reword the sentence to say
variables are substituted in "text-bearing node fields" (or equivalent) if
`approval:` and `cancel:` are intentionally excluded; ensure the chosen wording
accurately reflects where substitution actually occurs and uses the node type
tokens (`command:`, `prompt:`, `bash:`, `script:`, `loop:`, `approval:`,
`cancel:`) so readers can easily find the relevant nodes.
Approval and cancel nodes are first-class DAG node types (approval since the workflow lifecycle work in #871, cancel as a guarded-exit primitive) but the skill never described either one. An agent reading the skill and asked to "add a review gate before implementation" or "stop the workflow if the input is unsafe" would fall back to bash + exit 1, losing the proper semantics (cancelled vs. failed, on_reject AI rework, web UI auto-resume). Approval node coverage (references/workflow-dag.md, SKILL.md): - Full configuration block with message, capture_response, on_reject - The interactive: true workflow-level requirement for web UI delivery - Approve/reject commands across all platforms (CLI, slash, natural language) and the capture_response → $node-id.output flow - Ignored-fields list + the on_reject.prompt AI sub-node exception Cancel node coverage (references/workflow-dag.md, SKILL.md): - Single-field schema (cancel: "<reason>") - Lifecycle: cancelled (not failed); in-flight parallel nodes stopped; no DAG auto-resume path - The "cancel: vs bash-exit-1" decision rule (expected precondition miss vs. check itself failing) - Two canonical patterns — upstream-classification gate, pre-expensive-step gate Validation-rules list updated to enumerate approval/cancel constraints (message non-empty, on_reject.max_attempts range 1-10, cancel reason non-empty), plus a forward note that script: joins the mutually-exclusive set once PR #1362 lands. Placement in both files is after the Loop section and before the validation section, so this commit stays additive with respect to PR #1362's Script node insertion between Bash and Loop — rebase is clean.
The book is the curated first-contact reading path (landing page → "Get Started" → /book/). Both dag-workflows.md and quick-reference.md were stuck on "four node types" — missing script, approval, and cancel. A user reading the book as their first introduction would form an incomplete mental model, then find three more node types in the reference section later with no explanation of when they arrived. book/dag-workflows.md: - "four node types" → "seven node types. Exactly one mode field is required per node" - Table now lists Command, Prompt, Bash, Script, Loop, Approval, Cancel with one-line "when to use" for each, and cross-links to the dedicated guide pages for Script / Loop / Approval - New sections below the table for Script (inline + named examples with runtime and deps), Approval (with the interactive: true workflow-level note that's easy to miss), and Cancel (guarded-exit pattern) — keeping the existing narrative shape for Bash and Loop book/quick-reference.md: - Node Options table now includes script, approval, cancel rows - agents row added (inline sub-agents, Claude-only) - New "Script-specific fields" and "Approval-specific fields" subsections so the cheat-sheet is actually complete rather than pointing users elsewhere for the required constraints - Retry row callout that loop nodes hard-error on retry — previously omitted - bash timeout note widened to cover script timeout (same semantics) Both files are docs-web content; the CI build on the docs-script-nodes PR (#1362) previously validated the Starlight build path with a similar table addition, so this should render clean.
…nv gaps, add good-practices + troubleshooting (#1363) * fix(skill/when): document the full `when:` operator set and compound expressions The skill reference previously stated "operators: ==, != only" which is materially wrong — the condition evaluator supports ==, !=, <, >, <=, >= plus && / || compound expressions with && binding tighter than ||, plus dot-notation JSON field access. An agent authoring a workflow from the skill would think half the operators don't exist. Replaces the single-sentence section with a structured reference covering: - All six comparison operators (string and numeric modes) - Compound expressions with precedence rules and short-circuit eval - JSON dot notation semantics and failure modes - The fail-closed rules in full (invalid expression, non-numeric side, missing field, skipped upstream) Grounded in packages/workflows/src/condition-evaluator.ts. * feat(skill): document Approval and Cancel node types Approval and cancel nodes are first-class DAG node types (approval since the workflow lifecycle work in #871, cancel as a guarded-exit primitive) but the skill never described either one. An agent reading the skill and asked to "add a review gate before implementation" or "stop the workflow if the input is unsafe" would fall back to bash + exit 1, losing the proper semantics (cancelled vs. failed, on_reject AI rework, web UI auto-resume). Approval node coverage (references/workflow-dag.md, SKILL.md): - Full configuration block with message, capture_response, on_reject - The interactive: true workflow-level requirement for web UI delivery - Approve/reject commands across all platforms (CLI, slash, natural language) and the capture_response → $node-id.output flow - Ignored-fields list + the on_reject.prompt AI sub-node exception Cancel node coverage (references/workflow-dag.md, SKILL.md): - Single-field schema (cancel: "<reason>") - Lifecycle: cancelled (not failed); in-flight parallel nodes stopped; no DAG auto-resume path - The "cancel: vs bash-exit-1" decision rule (expected precondition miss vs. check itself failing) - Two canonical patterns — upstream-classification gate, pre-expensive-step gate Validation-rules list updated to enumerate approval/cancel constraints (message non-empty, on_reject.max_attempts range 1-10, cancel reason non-empty), plus a forward note that script: joins the mutually-exclusive set once PR #1362 lands. Placement in both files is after the Loop section and before the validation section, so this commit stays additive with respect to PR #1362's Script node insertion between Bash and Loop — rebase is clean. * feat(skill): document workflow-level fields beyond name/provider/model The skill's Schema section previously showed only name, description, provider, and model at the workflow level — which is most of a stub. Agents asked to "use the 1M-context Claude beta" or "run this under a network sandbox" or "add a fallback model in case Opus rate-limits" had no way to discover that any of these fields existed at the workflow level. Adds a comprehensive Workflow-Level Fields section covering: - Core: name, description, provider, model, interactive (with explicit callout that interactive: true is REQUIRED for approval/loop gates on web UI — a common footgun) - Isolation: worktree.enabled for pin-on/pin-off (the only worktree field at workflow level; baseBranch/copyFiles/path/initSubmodules are config.yaml only, so a cross-reference points there) - Claude SDK advanced: effort, thinking, fallbackModel, betas, sandbox, with explicit per-node-only exceptions (maxBudgetUsd, systemPrompt) - Codex-specific: modelReasoningEffort (with note that it's NOT the same as Claude's effort — this has confused users), webSearchMode, additionalDirectories - A complete worked example combining sandbox + approval + interactive All fields cross-referenced against packages/workflows/src/schemas/workflow.ts and packages/workflows/src/schemas/dag-node.ts. * feat(skill/loop): document interactive loops and gate_message Interactive loop nodes pause between iterations for human feedback via /workflow approve — used by archon-piv-loop and archon-interactive-prd. The skill's Loop Nodes section previously omitted both interactive: true and gate_message entirely, so an agent writing a guided-refinement workflow wouldn't know the feature exists or that gate_message is required at parse time. Adds: - interactive and gate_message rows to the config table (marking gate_message as required when interactive: true — enforced by the loader's superRefine) - A dedicated "Interactive Loops" subsection explaining the 6-step iterate-pause-approve-resume flow - Explicit call-out that $LOOP_USER_INPUT populates ONLY on the first iteration of a resumed session — easy to miss and a common surprise - Workflow-level interactive: true requirement for web UI delivery (loader warning otherwise) so the full-flow example is complete - Note that until_bash substitution DOES shell-quote $nodeId.output (unlike script bodies) — called out since the audit surfaced this inconsistency * fix(skill/cli): complete the CLI command reference with missing lifecycle commands The CLI reference previously documented only list, run, cleanup, validate, complete, version, setup, and chat — missing nearly every workflow lifecycle command an agent needs to operate a paused, failed, or stuck run. The interactive-workflows reference assumed these commands existed without actually documenting them. Adds full documentation for: - archon workflow status — show running workflow(s) - archon workflow approve <run-id> [comment] — resume approval gate (also populates $LOOP_USER_INPUT on interactive loops and the gate node's output when capture_response: true) - archon workflow reject <run-id> [reason] — reject gate; cancels or triggers on_reject rework depending on node config - archon workflow cancel <run-id> — terminate running/paused with in-flight subprocess kill - archon workflow abandon <run-id> — mark stuck row cancelled without subprocess kill (for orphan-cleanup after server crashes — matches the #1216 precedent) - archon workflow resume <run-id> [message] — force-resume specific run (auto-resume is default; this is for explicit override) - archon workflow cleanup [days] — disk hygiene for old terminal runs (with explicit callout that it does NOT transition 'running' rows, a common confusion) - archon workflow event emit — used inside loop prompts for state signalling; documented so agents don't invent their own mechanism - archon continue <branch> [flags] [msg] — iterative-session entry point with --workflow and --no-context flags Also: - Adds --allow-env-keys flag to the `workflow run` flag table with audit-log context and the env-leak-gate remediation use case - Adds an "Auto-resume without --resume" note disambiguating when --resume is needed vs. when auto-resume handles it - Adds --include-closed flag to `isolation cleanup`, which was previously missing; converts the flag list to a structured table - Explains the cancel/abandon distinction (live subprocess vs. orphan) All grounded in packages/cli/src/commands/workflow.ts, continue.ts, and isolation.ts. * feat(skill/repo-init): add scripts/ and state/, three-path env model, per-project env injection The repo-init reference was missing two first-class .archon/ directories (scripts/ since v0.3.3, state/ since the workflow-state feature) and had nothing to say about env — the #1 thing a user hits on first-run when their repo has a .env file with API keys. Directory tree updates: - Adds .archon/scripts/ with the extension->runtime rule (.ts/.js -> bun, .py -> uv) so agents know where to put named scripts referenced by script: nodes. - Adds .archon/state/ with explicit "always gitignore" callout — these are runtime artifacts, not source. Previously undocumented in the skill. - Adds .archon/.env (repo-scoped Archon env) and distinguishes it from the target repo's top-level .env. - Adds a "What each directory is for" list so the structure isn't just a tree with no narrative. .gitignore guidance: - state/ and .env added as must-gitignore (state/ matches CLAUDE.md and reference/archon-directories.md — skill was lagging). - mcp/ demoted to conditional — gitignore only if you hardcode secrets. New "Three-Path Env Model" section: - ~/.archon/.env (trusted, user), <cwd>/.archon/.env (trusted, repo), <cwd>/.env (UNTRUSTED, target project — stripped from subprocess env). - Precedence (override: true across archon-owned paths) and the observable [archon] loaded N keys / stripped K keys log lines so operators can verify what actually happened. - Decision tree for where to put API keys vs. target-project env vs. things Archon shouldn't touch. - Links to archon setup --scope home|project with --force for writing to the right file with timestamped backups. New "Per-Project Env Injection" section: - Documents both managed surfaces: .archon/config.yaml env: block (git-committed, $REF expansion) and Web UI Settings → Projects → Env Vars (DB-stored, never returned over API). - Names every execution surface that receives the injected vars: Claude/Codex/Pi subprocess, bash: nodes, script: nodes, and direct codebase-scoped chat. - Documents the env-leak gate with all 5 remediation paths so an agent hitting "Cannot register: env has sensitive keys" knows the options. Grounded in CHANGELOG v0.3.7 (three-path env + setup flags), v0.3.0 (env-leak gate), and reference/security.md on the docs site. * fix(skill/authoring-commands): correct override paths and add home-scoped commands The file-location and discovery sections described an override layout that does not match the actual resolver. It showed: .archon/commands/defaults/archon-assist.md # Overrides the bundled and claimed `.archon/commands/defaults/` was where repo-level overrides lived. In fact the resolver (executor-shared.ts:152-200 + command- validation.ts) walks `.archon/commands/` 1 level deep and uses basename matching — putting `archon-assist.md` at the top of `.archon/commands/` is the canonical way to override the bundled version. The `defaults/` subfolder is a Archon-internal convention for shipping bundled defaults, not a user-facing override pattern. Also, home-scoped commands (`~/.archon/commands/`, shipped in v0.3.7) were completely absent — agents authoring personal helpers wouldn't know they could live at the user level and be shared across every repo. Changes: - File Location section now shows all three discovery scopes (repo, home, bundled) with precedence ordering and 1-level subfolder rules - Duplicate-basename rule documented as a user error surface - Discovery and Priority section rewritten with accurate 3-step lookup order — no more references to the nonexistent defaults/ override path - Adds the Web UI "Global (~/.archon/commands/)" palette label note so users authoring helpers for the builder know what to expect No code changes — this is a pure fix of stale/incorrect skill reference material. * feat(skill): add workflow good-practices and troubleshooting reference pages Closes two gaps from the audit. The skill previously had zero guidance on designing multi-node workflows (what to avoid, what to reach for first, how to structure artifact chains) and zero guidance on where to look when things go wrong (log paths, env-leak gate remediations, orphan-row cleanup, resume semantics). New references/good-practices.md (9 Good Practices + 7 Anti-Patterns): - Use deterministic nodes (bash:/script:) for deterministic work, AI for reasoning — the single biggest quality lever - output_format required whenever downstream when: reads a field — the most common source of "workflow silently routes wrong" - trigger_rule: none_failed_min_one_success after conditional branches — the classic bug where all_success fails because a skipped when:-gated branch doesn't count as a success - context: fresh requires artifacts for state passing — commands must explicitly "read $ARTIFACTS_DIR/..." when downstream of fresh - Cheap models (haiku) for glue, strong for substance - Workflow descriptions as routing affordances - Validate (archon validate workflows) + smoke-run before shipping - Artifact-chain-first design - worktree.enabled: true for code-changing workflows (reversibility) - Anti-patterns with before/after YAML examples for each (AI-for-tests, free-form when: matching, context: fresh without artifacts, long flat AI-node layers, secrets in YAML, retry on loop nodes, tiny max_iterations, missing workflow-level interactive:, tool-restricted MCP nodes) New references/troubleshooting.md: - Log location (~/.archon/workspaces/<owner>/<repo>/logs/<run-id>.jsonl) with jq recipes for common queries (last assistant message, failed events, full stream) - Artifact location for cross-node handoff debugging - 9 Common Failure Modes, each with root cause + concrete fix: - $BASE_BRANCH unresolvable - Env-leak gate (5 remediations) - Claude/Codex binary not found (compiled-binary-only) - "running" forever (AI working / orphan / idle_timeout) - Mid-workflow failure and auto-resume semantics - Approval gate missing on web UI (workflow-level interactive:) - MCP plugin connection noise (filtered by design) - Empty $nodeId.output / field access (4 causes) - Diagnostic command cheat sheet (list, status, isolation list, validate, tail-log, --verbose, LOG_LEVEL=debug) - Escalation protocol (version + validate + log tail + CHANGELOG + issue) SKILL.md routing table now dispatches "Workflow good practices / anti-patterns" and "Troubleshoot a failing / stuck workflow" to the new references so an agent can find them without having to know they exist. * docs(book): update node-types coverage from four to all seven The book is the curated first-contact reading path (landing page → "Get Started" → /book/). Both dag-workflows.md and quick-reference.md were stuck on "four node types" — missing script, approval, and cancel. A user reading the book as their first introduction would form an incomplete mental model, then find three more node types in the reference section later with no explanation of when they arrived. book/dag-workflows.md: - "four node types" → "seven node types. Exactly one mode field is required per node" - Table now lists Command, Prompt, Bash, Script, Loop, Approval, Cancel with one-line "when to use" for each, and cross-links to the dedicated guide pages for Script / Loop / Approval - New sections below the table for Script (inline + named examples with runtime and deps), Approval (with the interactive: true workflow-level note that's easy to miss), and Cancel (guarded-exit pattern) — keeping the existing narrative shape for Bash and Loop book/quick-reference.md: - Node Options table now includes script, approval, cancel rows - agents row added (inline sub-agents, Claude-only) - New "Script-specific fields" and "Approval-specific fields" subsections so the cheat-sheet is actually complete rather than pointing users elsewhere for the required constraints - Retry row callout that loop nodes hard-error on retry — previously omitted - bash timeout note widened to cover script timeout (same semantics) Both files are docs-web content; the CI build on the docs-script-nodes PR (#1362) previously validated the Starlight build path with a similar table addition, so this should render clean. * fix(skill/cli): remove nonexistent \`archon workflow cancel\`, fix workflow status jq recipe Two accuracy issues from the PR code-reviewer (comment 4311243858). C1: \`archon workflow cancel <run-id>\` does NOT exist as a CLI subcommand. The switch at packages/cli/src/cli.ts:318-485 dispatches on list / run / status / resume / abandon / approve / reject / cleanup / event — running \`archon workflow cancel\` hits the default case and exits with "Unknown workflow subcommand: cancel" (cli.ts:478-484). Active cancellation is only available via: - /workflow cancel <run-id> chat slash command (all platforms) - Cancel button on the Web UI dashboard - POST /api/workflows/runs/{runId}/cancel REST endpoint cli-commands.md: removed the \`### archon workflow cancel <run-id>\` subsection; kept the \`abandon\` subsection but made it explicit that abandon does NOT kill a subprocess. Added a call-out box at the bottom of the abandon section explaining where to go for actual cancellation. troubleshooting.md "running forever" section: split the original cancel-vs-abandon advice into three bullets — Web UI / CLI abandon (for orphans, no subprocess kill) / chat \`/workflow cancel\` (for live runs that need interruption). Added an explicit "there is no archon workflow cancel CLI subcommand" parenthetical since the wrong command was being suggested in flow. I1: the \`archon workflow list --json\` diagnostic used an incorrect jq filter. workflow list's --json output (workflow.ts:185-219) has shape { workflows: [{ name, description, provider?, model?, ... }], errors: [...] } with no \`runs\` field — \`jq '.workflows[] | select(.runs)'\` returns empty unconditionally. Replaced with \`archon workflow status --json | jq '.runs[]'\`, which matches the actual shape of workflowStatusCommand at workflow.ts:852+ ({ runs: WorkflowRun[] }). Also tightened the narration to distinguish JSON from human-readable status output. No change to the commit history in this PR — these are follow-up fixes to claims I introduced in earlier commits of this branch (f10b989 for C1, 66d2b86 for I1). * fix(skill): remove env-leak gate references (feature was removed in provider extraction) C2 from the PR code-reviewer (comment 4311243858). The pre-spawn env-leak gate was removed from the codebase during the provider-extraction refactor — see TODO(#1135) at packages/providers/src/claude/provider.ts:908. Zero hits for --allow-env-keys / allowEnvKeys / allow_env_keys / allow_target_repo_keys across packages/. The CLI's parseArgs (cli.ts:182-208) has no --allow-env-keys option, and because parseArgs uses strict: false, an unknown --allow-env-keys would be silently ignored rather than error. What remains accurate and is NOT touched: - Three-Path Env Model section (user/repo archon-owned envs are loaded; target repo <cwd>/.env keys are stripped from process.env at boot) still correctly describes current behavior, grounded in packages/paths/src/strip-cwd-env.ts + env-integration.test.ts - Per-Project Env Injection section (Option 1: .archon/config.yaml env: block; Option 2: Web UI Settings → Projects → Env Vars) is unchanged — both remain the sanctioned way to get env vars into subprocesses Removed claims (all three files): - cli-commands.md: --allow-env-keys flag row in the workflow run flags table - repo-init.md: the "Env-leak gate" subsection at the end of Per-Project Env Injection listing 5 remediations (all of which reference UI/CLI/ config surfaces that don't exist). Replaced with a succinct callout that explains the actual current behavior — target repo .env keys are stripped, workflows that need those values should use managed injection — so the reader still gets the "where to put my env vars" answer - troubleshooting.md: the "Cannot register: codebase has sensitive env keys" section (error message that can no longer be emitted) If the env-leak gate is ever resurrected per TODO(#1135), the docs can be re-added then. The CHANGELOG v0.3.0 entry describing the gate is a historical record of past behavior and does not need to be rewritten. * fix(skill/troubleshooting): correct JSONL event type names and field name C3 from the PR code-reviewer (comment 4311243858). The troubleshooting reference's event-types table used _started / _completed / _failed suffixes, but packages/workflows/src/logger.ts:19-30 shows the actual WorkflowEvent.type enum is: workflow_start | workflow_complete | workflow_error | assistant | tool | validation | node_start | node_complete | node_skipped | node_error The second jq recipe also queried `.event` but the discriminator is `.type`. Fixes: - Event table: renamed columns (_started → _start, _completed → _complete, _failed → _error). Explicitly called out the field name as `type` so the reader knows what jq selector to use - Replaced the "tool_use / tool_result" row with a single `tool` row and listed its actual payload fields (tool_name, tool_input, duration_ms, tokens) — tool_use/tool_result are SDK message kinds that appear within the AI stream, not top-level log event types - Added a `validation` row (was missing; it's emitted by workflow-level validation calls with `check` and `result` fields) - Removed `retry_attempt` row — this event type is not emitted to the JSONL file. Retry bookkeeping goes through pino logs, not the workflow log file - Added an explicit callout that loop_iteration_started / loop_iteration_completed (and other emitter-only events) go through the workflow event emitter + DB workflow_events table, NOT the JSONL file. Pointed readers to the DB or Web UI for loop-level detail. This distinguishes the two parallel event systems — easy to conflate (store.ts:11-17 uses _started/_completed/_failed for the DB side, logger.ts uses _start/_complete/_error for JSONL) - Fixed the "all failed events" jq recipe: .event → .type and _failed → _error - Minor cleanup: the inline "tool_use events" mention in the "running forever" section said the wrong event name — updated to "tool or assistant events in the tail" Grounded in packages/workflows/src/logger.ts (canonical JSONL event shape) and packages/workflows/src/store.ts (the parallel DB event naming, which the reviewer correctly flagged as different and worth keeping distinct). * fix(skill): two stragglers from the code-reviewer audit Cleanup of two references that slipped through the earlier C1 and C3 fixes: - references/troubleshooting.md:126: \`node_failed\` → \`node_error\` (the "Node output is empty" diagnostics section references the JSONL log, which uses the logger.ts enum — not the DB workflow_events table which does use \`node_failed\`). The C3 fix corrected the event table and one jq recipe but missed this inline mention. - references/interactive-workflows.md:106: removed \`archon workflow cancel <run-id>\` (nonexistent CLI subcommand) from the troubleshooting bullet. This was pre-existing before the hardening PR but fell within the C1 remediation scope. Replaced with the correct triage: reject (approval gate only) vs abandon (orphan cleanup, no subprocess kill) vs chat /workflow cancel (actual subprocess termination). Grounded in the same sources as the earlier C1/C3 commits: packages/cli/src/cli.ts:318-485 (no cancel case) and packages/workflows/src/logger.ts:19-30 (JSONL type enum). * feat(skill): point to archon.diy as the canonical docs source The skill had no reference to archon.diy (the live docs site built from packages/docs-web/). Several reference files said "see the docs site" without naming the URL, leaving the agent to guess or grep the repo for the hostname. An agent with the skill loaded should know that when the distilled reference pages don't cover a case, the full canonical docs are one WebFetch away. SKILL.md: new "Richer Context: archon.diy" section between Routing and Running Workflows. Covers: - When to reach for the live docs (longer examples, tutorial framing, features the skill only mentions in passing, "where's that documented?" user questions) - URL map — 13 starting points covering getting-started, book (tutorial series), guides/ (authoring + per-node-type + per-node-feature), reference/ (variables, CLI, security, architecture, configuration, troubleshooting), adapters/, deployment/ - Precedence: skill refs first (context-cheap, tuned for agents), docs site as escalation. Prevents agents defaulting to WebFetch when a local skill ref already covers the answer Also upgrades the 5 existing generic "docs site" mentions across reference files to concrete archon.diy URLs with anchor fragments where helpful: - good-practices.md: Inline sub-agents pattern → archon.diy/guides/ authoring-workflows/#inline-sub-agents - troubleshooting.md: "Install page on the docs site" → archon.diy/ getting-started/installation/ - workflow-dag.md: "Workflow Description Best Practices" → anchor link; sandbox schema reference → archon.diy/guides/authoring-workflows/ #claude-sdk-advanced-options - repo-init.md: Security Model reference → archon.diy/reference/ security/#target-repo-env-isolation (deep-link into the section that covers the <cwd>/.env strip behavior) URL source of truth: astro.config.mjs:5 (site: 'https://archon.diy'). URL structure mirrors packages/docs-web/src/content/docs/<section>/ <page>.md — verified by the 62 pages the docs build produces.
* chore: update Homebrew formula for v0.3.9
* chore(release-skill): use --help (not version) for Step 1.5 smoke probe (#1359)
The pre-flight binary smoke does a bare `bun build --compile` — it
deliberately skips `scripts/build-binaries.sh` to stay fast. That means
packages/paths/src/bundled-build.ts retains its dev defaults, including
BUNDLED_IS_BINARY = false.
version.ts branches on BUNDLED_IS_BINARY: when true it returns the
embedded string; when false it calls getDevVersion(), which reads
package.json at `SCRIPT_DIR/../../../../package.json`. Inside a compiled
binary SCRIPT_DIR resolves under `$bunfs/root/`, the walk produces a CWD-
relative path that doesn't exist, and the smoke aborts with "Failed to
read version: package.json not found" — a false positive.
Hit during the 0.3.8 release attempt: the real Pi lazy-load fix was
working end-to-end; the smoke test was the only thing failing.
Use --help instead. It exercises the same module-init graph (so it still
catches the real failure modes the skill lists — Pi package.json init
crash, Bun --bytecode bugs, CJS wrapper issues, circular imports under
minify) but has no dev/binary branch, so no false positive.
Also add a longer comment block explaining why --help is preferred, so
this doesn't get "normalized" back to `version` by a future drive-by.
* chore(test-release-skill): preserve archon-stable across test cycles
The brew path of /test-release runs `brew uninstall` in Phase 5 to leave the
system in its pre-test state. For operators using the dual-homebrew pattern
(renamed brew binary at `/opt/homebrew/bin/archon-stable` so it coexists with
a `bun link` dev `archon`), that uninstall wipes the Cellar dir the
`archon-stable` symlink points into → `archon-stable` becomes dangling →
`brew cleanup` sweeps it away on the next brew op. Next time the operator
wants stable, they have to manually re-run `brew-upgrade-archon`.
Fix: make the skill aware of `archon-stable` and restore it transparently.
- Phase 2 item 4: detect the `archon-stable` symlink before any brew op;
export `ARCHON_STABLE_WAS_INSTALLED=yes` so Phase 5 knows to restore it.
Only triggers for the brew path (curl-mac/curl-vps don't touch brew so
they leave `archon-stable` alone).
- Phase 5 brew path: after `brew uninstall + untap`, if the flag was set,
re-tap + re-install + rename. Verifies the restored `archon-stable`
reports a version and warns (non-fatal) if the rename target is missing.
Documents the tradeoff: the restored version is "whatever the tap ships
today", not necessarily the pre-test version — usually that's what the
operator wants (the release they just tested becomes stable) but the
back-version-QA case requires a manual `brew-upgrade-archon` after.
- Phase 1 confirmation banner now mentions that `archon-stable` will be
preserved so the operator isn't surprised by the reinstall during Phase 5.
No changes to curl-mac/curl-vps paths. No changes to Phase 4 test suite.
* fix(providers/pi): install PI_PACKAGE_DIR shim so Pi workflows run in a compiled binary (#1360)
v0.3.9 made Pi boot-safe: lazy-loading its imports meant `archon version`
no longer crashed on `@mariozechner/pi-coding-agent/dist/config.js`'s
module-init `readFileSync(getPackageJsonPath())`. That's what the
`provider-lazy-load.test.ts` regression test guards.
The fix was only half the problem though. When a Pi workflow actually
runs, sendQuery() triggers the dynamic import — and Pi's config.js
module-init fires then, hitting the exact same ENOENT on
`dirname(process.execPath)/package.json`. Discovered by running
`archon workflow run test-pi` against a locally-compiled 0.3.9 binary:
[main] Failed: ENOENT: no such file or directory,
open '/private/tmp/package.json'
at readFileSync (unknown)
at <anonymous> (/$bunfs/root/archon-providertest:184:7889)
at init_config
Boot-safe ≠ runtime-safe. The `/test-release` run for 0.3.9 passed
because it only exercised `archon-assist` (Claude); Pi was never
actually invoked on the released binary.
Fix: before the dynamic `import('@mariozechner/pi-coding-agent')` in
sendQuery, install a PI_PACKAGE_DIR shim. Pi's config.js checks
`process.env.PI_PACKAGE_DIR` first in its `getPackageDir()` and
short-circuits the `dirname(process.execPath)` walk. We write a
minimal `{name, version, piConfig:{}}` stub to
`tmpdir()/archon-pi-shim/package.json` (idempotent — existsSync check)
and set the env var. Pi only reads `piConfig.name`, `piConfig.configDir`,
and `version` from that file, all optional, so the stub surface is
genuinely minimal.
Localized to PiProvider: no global state, no mutation of any shared
config, no upstream fork. Claude and Codex providers are unaffected
(their SDKs don't have this class of module-init side effect).
Verified end-to-end: built a compiled archon binary with this patch,
ran `archon workflow run test-pi --no-worktree` (Pi workflow with
model `anthropic/claude-haiku-4-5`), got a clean response. Before the
patch, same binary crashed at `dag_node_started` with the ENOENT above.
Regression test added: asserts `PI_PACKAGE_DIR` is set after sendQuery
hits even its fast-fail "no model" path. Together with the existing
`provider-lazy-load.test.ts` (boot-safe) this covers both halves.
* feat(providers): autodetect canonical binary install paths for Claude and Codex (#1361)
Both binary resolvers previously stopped at env-var + explicit config and
threw a "not found" error when neither was set. Users who followed the
upstream-recommended install flow (Anthropic's `curl install.sh` for
Claude, `npm install -g @openai/codex`) still had to manually set either
`CLAUDE_BIN_PATH` / `CODEX_BIN_PATH` or the corresponding config field
before any workflow could run.
Add a tier-N autodetect step between the explicit config tier and the
install-instructions throw. Purely additive: env and config still win
when set (precedence covered by new tests). On autodetect miss, the same
install-instructions error fires as before.
Claude probe list (verified against docs.claude.com "Uninstall Claude
Code → Native installation" section):
- $HOME/.local/bin/claude (mac/linux native installer)
- $USERPROFILE\.local\bin\claude.exe (Windows native installer)
Codex probe list (verified against openai/codex README; npm global-
install puts the binary at `{npm_prefix}/bin/<name>` on POSIX,
`{npm_prefix}\<name>.cmd` on Windows):
- $HOME/.npm-global/bin/codex (user-set `npm config set prefix`)
- /opt/homebrew/bin/codex (mac arm64 with homebrew-node)
- /usr/local/bin/codex (mac intel / linux system node)
- %APPDATA%\npm\codex.cmd (Windows npm global default)
- $HOME\.npm-global\codex.cmd (Windows user-set prefix)
Not probed (explicit override still required):
- Custom npm prefixes — `npm root -g` would need a subprocess per
resolve, too much surface for a probe helper
- `brew install --cask codex` — cask layout isn't a PATH binary
- Manual GitHub Releases extracts — placement is user-determined
- `~/.bun/bin/codex` — not documented in openai/codex README
Pi provider intentionally has no equivalent change: the Pi SDK is
bundled into the archon binary (no subprocess), so there's no "binary"
to resolve. Pi auth lives at `~/.pi/agent/auth.json` which the SDK
already finds by default, and the PR A shim (`PI_PACKAGE_DIR`) handles
the package-dir case via Pi's own documented escape hatch.
E2E verified: removed both config entries from ~/.archon/config.yaml,
rebuilt compiled binary, ran `archon workflow run archon-assist` and a
Codex workflow. Logs showed `source: 'autodetect'` for both, responses
returned cleanly.
* fix(providers/test): use os.homedir() instead of $HOME in claude binary autodetect test
The native-installer autodetect test computed its expected path from
process.env.HOME, but the implementation uses node:os homedir(). On
Windows, HOME is typically unset (Windows uses USERPROFILE), so the
test fell back to '/Users/test' while the resolver returned the real
home dir — making the spy's path-equality check fail and breaking CI
on windows-latest.
Mirror the implementation by importing homedir() from node:os and
joining with node:path so the expected path matches the actual
platform-resolved home and separator.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(server): contain Discord login failure so it doesn't kill the server (#1365)
Reported in #1365: a user running `archon serve` with DISCORD_BOT_TOKEN
set but the "Message Content Intent" toggle disabled in the Discord
Developer Portal saw the entire server crash with `Used disallowed
intents`. Discord rejects the gateway connection (close code 4014) when
a privileged intent is requested without being enabled, and the
unguarded `await discord.start()` propagated the error all the way up,
taking the web UI down with it.
Wrap discord.start() in try/catch — log the failure with an actionable
hint (special-cased for the disallowed-intent error) and continue
running. Other adapters and the web UI come up regardless. The shutdown
handler already uses optional chaining (`discord?.stop()`) so nulling
discord after a failed start is safe.
Other adapters (Telegram, Slack, GitHub, Gitea, GitLab) have the same
unguarded-start pattern but are out of scope for this fix — addressing
them is tracked separately.
Also expanded the Discord setup docs with a caution callout that names
the exact error string and the new log event so users can grep for
both.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs(script-nodes): dedicated guide + teach the archon skill (#1362)
* docs(script-nodes): add dedicated guide and teach the archon skill how to write them
Script nodes (script:) have been a first-class DAG node type since v0.3.3 but
were documented only as one-liners in CLAUDE.md and a CI smoke test. Claude
Code reading the archon skill would see "Four Node Types: command, prompt,
bash, loop" and reach for bash+node/python one-liners instead of a proper
script node — losing bun's --no-env-file isolation, uv's --with dependency
pins, and the .archon/scripts/ reuse story.
- New packages/docs-web/src/content/docs/guides/script-nodes.md mirroring the
structure of loop-nodes.md / approval-nodes.md: schema, inline vs named
dispatch, runtime/deps semantics, scripts directory precedence (repo > home),
extension-runtime mapping, env isolation, stdout/stderr contract, patterns,
and the explicit list of ignored AI fields.
- guides/authoring-workflows.md and guides/index.md updated so the new guide is
discoverable from both the node-types table and the guides landing page.
- reference/variables.md calls out the no-shell-quote difference between
bash: and script: substitution — a subtle correctness trap when adapting a
bash pattern into a script node.
- Sidebar order bumped +1 on hooks/mcp-servers/skills/global-workflows/
remotion-workflow to slot script-nodes at order 5 next to the other
node-type guides.
- .claude/skills/archon/SKILL.md: replaces stale "Four Node Types" (which
also silently omitted approval and cancel) with the accurate seven, with a
script-node code block showing both inline and named patterns.
- references/workflow-dag.md: full Script Node section covering dispatch,
resolution, deps, stdout contract, and the list of AI-only fields that are
ignored; validation-rules list updated.
- references/dag-advanced.md and references/variables.md: retry-support line
corrected; no-shell-quote note added.
- examples/dag-workflow.yaml: added an extract-labels TypeScript script node
and updated the header comment.
* fix(docs): review follow-ups for script-node guide
- skills example: extract-labels was reading process.env.ISSUE_JSON which is
never set; use String.raw`$fetch-issue.output` so the upstream bash node's
JSON is actually consumed
- guides/script-nodes.md + skills/workflow-dag.md: idle_timeout is accepted
but ignored on script (and bash) nodes — executeScriptNode only reads
node.timeout. Clarify that script/bash use `timeout`, not idle_timeout
- archon-workflow-builder.yaml: prompt enumerated only bash/prompt/command/loop,
so the AI builder could never propose script or approval nodes. Add both
(plus examples + rule about script output not being shell-quoted) and
regenerate bundled defaults
- book/dag-workflows.md + book/quick-reference.md + adapters/web.md: fill in
the node-type references that were missing script, approval, and cancel.
adapters/web.md also overclaimed "loop" in the palette — NodePalette.tsx
only drags command/prompt/bash, so note that the other kinds are YAML-only
* docs/skill: general hardening — fix inaccuracies, fill workflow/CLI/env gaps, add good-practices + troubleshooting (#1363)
* fix(skill/when): document the full `when:` operator set and compound expressions
The skill reference previously stated "operators: ==, != only" which is
materially wrong — the condition evaluator supports ==, !=, <, >, <=, >=
plus && / || compound expressions with && binding tighter than ||, plus
dot-notation JSON field access. An agent authoring a workflow from the
skill would think half the operators don't exist.
Replaces the single-sentence section with a structured reference covering:
- All six comparison operators (string and numeric modes)
- Compound expressions with precedence rules and short-circuit eval
- JSON dot notation semantics and failure modes
- The fail-closed rules in full (invalid expression, non-numeric side,
missing field, skipped upstream)
Grounded in packages/workflows/src/condition-evaluator.ts.
* feat(skill): document Approval and Cancel node types
Approval and cancel nodes are first-class DAG node types (approval since the
workflow lifecycle work in #871, cancel as a guarded-exit primitive) but the
skill never described either one. An agent reading the skill and asked to
"add a review gate before implementation" or "stop the workflow if the input
is unsafe" would fall back to bash + exit 1, losing the proper semantics
(cancelled vs. failed, on_reject AI rework, web UI auto-resume).
Approval node coverage (references/workflow-dag.md, SKILL.md):
- Full configuration block with message, capture_response, on_reject
- The interactive: true workflow-level requirement for web UI delivery
- Approve/reject commands across all platforms (CLI, slash, natural
language) and the capture_response → $node-id.output flow
- Ignored-fields list + the on_reject.prompt AI sub-node exception
Cancel node coverage (references/workflow-dag.md, SKILL.md):
- Single-field schema (cancel: "<reason>")
- Lifecycle: cancelled (not failed); in-flight parallel nodes stopped;
no DAG auto-resume path
- The "cancel: vs bash-exit-1" decision rule (expected precondition miss
vs. check itself failing)
- Two canonical patterns — upstream-classification gate, pre-expensive-step
gate
Validation-rules list updated to enumerate approval/cancel constraints
(message non-empty, on_reject.max_attempts range 1-10, cancel reason
non-empty), plus a forward note that script: joins the mutually-exclusive
set once PR #1362 lands.
Placement in both files is after the Loop section and before the validation
section, so this commit stays additive with respect to PR #1362's Script
node insertion between Bash and Loop — rebase is clean.
* feat(skill): document workflow-level fields beyond name/provider/model
The skill's Schema section previously showed only name, description, provider,
and model at the workflow level — which is most of a stub. Agents asked to
"use the 1M-context Claude beta" or "run this under a network sandbox" or
"add a fallback model in case Opus rate-limits" had no way to discover
that any of these fields existed at the workflow level.
Adds a comprehensive Workflow-Level Fields section covering:
- Core: name, description, provider, model, interactive (with explicit
callout that interactive: true is REQUIRED for approval/loop gates on
web UI — a common footgun)
- Isolation: worktree.enabled for pin-on/pin-off (the only worktree field
at workflow level; baseBranch/copyFiles/path/initSubmodules are
config.yaml only, so a cross-reference points there)
- Claude SDK advanced: effort, thinking, fallbackModel, betas, sandbox,
with explicit per-node-only exceptions (maxBudgetUsd, systemPrompt)
- Codex-specific: modelReasoningEffort (with note that it's NOT the same
as Claude's effort — this has confused users), webSearchMode,
additionalDirectories
- A complete worked example combining sandbox + approval + interactive
All fields cross-referenced against packages/workflows/src/schemas/workflow.ts
and packages/workflows/src/schemas/dag-node.ts.
* feat(skill/loop): document interactive loops and gate_message
Interactive loop nodes pause between iterations for human feedback via
/workflow approve — used by archon-piv-loop and archon-interactive-prd.
The skill's Loop Nodes section previously omitted both interactive: true
and gate_message entirely, so an agent writing a guided-refinement
workflow wouldn't know the feature exists or that gate_message is
required at parse time.
Adds:
- interactive and gate_message rows to the config table (marking
gate_message as required when interactive: true — enforced by the
loader's superRefine)
- A dedicated "Interactive Loops" subsection explaining the 6-step
iterate-pause-approve-resume flow
- Explicit call-out that $LOOP_USER_INPUT populates ONLY on the first
iteration of a resumed session — easy to miss and a common surprise
- Workflow-level interactive: true requirement for web UI delivery
(loader warning otherwise) so the full-flow example is complete
- Note that until_bash substitution DOES shell-quote $nodeId.output
(unlike script bodies) — called out since the audit surfaced this
inconsistency
* fix(skill/cli): complete the CLI command reference with missing lifecycle commands
The CLI reference previously documented only list, run, cleanup, validate,
complete, version, setup, and chat — missing nearly every workflow
lifecycle command an agent needs to operate a paused, failed, or stuck
run. The interactive-workflows reference assumed these commands existed
without actually documenting them.
Adds full documentation for:
- archon workflow status — show running workflow(s)
- archon workflow approve <run-id> [comment] — resume approval gate
(also populates $LOOP_USER_INPUT on interactive loops and the gate
node's output when capture_response: true)
- archon workflow reject <run-id> [reason] — reject gate; cancels or
triggers on_reject rework depending on node config
- archon workflow cancel <run-id> — terminate running/paused with
in-flight subprocess kill
- archon workflow abandon <run-id> — mark stuck row cancelled without
subprocess kill (for orphan-cleanup after server crashes — matches
the #1216 precedent)
- archon workflow resume <run-id> [message] — force-resume specific
run (auto-resume is default; this is for explicit override)
- archon workflow cleanup [days] — disk hygiene for old terminal runs
(with explicit callout that it does NOT transition 'running' rows,
a common confusion)
- archon workflow event emit — used inside loop prompts for state
signalling; documented so agents don't invent their own mechanism
- archon continue <branch> [flags] [msg] — iterative-session entry
point with --workflow and --no-context flags
Also:
- Adds --allow-env-keys flag to the `workflow run` flag table with
audit-log context and the env-leak-gate remediation use case
- Adds an "Auto-resume without --resume" note disambiguating when
--resume is needed vs. when auto-resume handles it
- Adds --include-closed flag to `isolation cleanup`, which was
previously missing; converts the flag list to a structured table
- Explains the cancel/abandon distinction (live subprocess vs. orphan)
All grounded in packages/cli/src/commands/workflow.ts, continue.ts,
and isolation.ts.
* feat(skill/repo-init): add scripts/ and state/, three-path env model, per-project env injection
The repo-init reference was missing two first-class .archon/ directories
(scripts/ since v0.3.3, state/ since the workflow-state feature) and had
nothing to say about env — the #1 thing a user hits on first-run when
their repo has a .env file with API keys.
Directory tree updates:
- Adds .archon/scripts/ with the extension->runtime rule (.ts/.js -> bun,
.py -> uv) so agents know where to put named scripts referenced by
script: nodes.
- Adds .archon/state/ with explicit "always gitignore" callout — these
are runtime artifacts, not source. Previously undocumented in the skill.
- Adds .archon/.env (repo-scoped Archon env) and distinguishes it from
the target repo's top-level .env.
- Adds a "What each directory is for" list so the structure isn't just
a tree with no narrative.
.gitignore guidance:
- state/ and .env added as must-gitignore (state/ matches CLAUDE.md and
reference/archon-directories.md — skill was lagging).
- mcp/ demoted to conditional — gitignore only if you hardcode secrets.
New "Three-Path Env Model" section:
- ~/.archon/.env (trusted, user), <cwd>/.archon/.env (trusted, repo),
<cwd>/.env (UNTRUSTED, target project — stripped from subprocess env).
- Precedence (override: true across archon-owned paths) and the
observable [archon] loaded N keys / stripped K keys log lines so
operators can verify what actually happened.
- Decision tree for where to put API keys vs. target-project env vs.
things Archon shouldn't touch.
- Links to archon setup --scope home|project with --force for writing
to the right file with timestamped backups.
New "Per-Project Env Injection" section:
- Documents both managed surfaces: .archon/config.yaml env: block
(git-committed, $REF expansion) and Web UI Settings → Projects →
Env Vars (DB-stored, never returned over API).
- Names every execution surface that receives the injected vars:
Claude/Codex/Pi subprocess, bash: nodes, script: nodes, and direct
codebase-scoped chat.
- Documents the env-leak gate with all 5 remediation paths so an agent
hitting "Cannot register: env has sensitive keys" knows the options.
Grounded in CHANGELOG v0.3.7 (three-path env + setup flags), v0.3.0
(env-leak gate), and reference/security.md on the docs site.
* fix(skill/authoring-commands): correct override paths and add home-scoped commands
The file-location and discovery sections described an override layout that
does not match the actual resolver. It showed:
.archon/commands/defaults/archon-assist.md # Overrides the bundled
and claimed `.archon/commands/defaults/` was where repo-level overrides
lived. In fact the resolver (executor-shared.ts:152-200 + command-
validation.ts) walks `.archon/commands/` 1 level deep and uses basename
matching — putting `archon-assist.md` at the top of `.archon/commands/`
is the canonical way to override the bundled version. The `defaults/`
subfolder is a Archon-internal convention for shipping bundled defaults,
not a user-facing override pattern.
Also, home-scoped commands (`~/.archon/commands/`, shipped in v0.3.7)
were completely absent — agents authoring personal helpers wouldn't
know they could live at the user level and be shared across every repo.
Changes:
- File Location section now shows all three discovery scopes (repo,
home, bundled) with precedence ordering and 1-level subfolder rules
- Duplicate-basename rule documented as a user error surface
- Discovery and Priority section rewritten with accurate 3-step lookup
order — no more references to the nonexistent defaults/ override path
- Adds the Web UI "Global (~/.archon/commands/)" palette label note so
users authoring helpers for the builder know what to expect
No code changes — this is a pure fix of stale/incorrect skill reference
material.
* feat(skill): add workflow good-practices and troubleshooting reference pages
Closes two gaps from the audit. The skill previously had zero guidance on
designing multi-node workflows (what to avoid, what to reach for first,
how to structure artifact chains) and zero guidance on where to look
when things go wrong (log paths, env-leak gate remediations, orphan-row
cleanup, resume semantics).
New references/good-practices.md (9 Good Practices + 7 Anti-Patterns):
- Use deterministic nodes (bash:/script:) for deterministic work, AI for
reasoning — the single biggest quality lever
- output_format required whenever downstream when: reads a field — the
most common source of "workflow silently routes wrong"
- trigger_rule: none_failed_min_one_success after conditional branches —
the classic bug where all_success fails because a skipped when:-gated
branch doesn't count as a success
- context: fresh requires artifacts for state passing — commands must
explicitly "read $ARTIFACTS_DIR/..." when downstream of fresh
- Cheap models (haiku) for glue, strong for substance
- Workflow descriptions as routing affordances
- Validate (archon validate workflows) + smoke-run before shipping
- Artifact-chain-first design
- worktree.enabled: true for code-changing workflows (reversibility)
- Anti-patterns with before/after YAML examples for each (AI-for-tests,
free-form when: matching, context: fresh without artifacts, long flat
AI-node layers, secrets in YAML, retry on loop nodes, tiny
max_iterations, missing workflow-level interactive:, tool-restricted
MCP nodes)
New references/troubleshooting.md:
- Log location (~/.archon/workspaces/<owner>/<repo>/logs/<run-id>.jsonl)
with jq recipes for common queries (last assistant message, failed
events, full stream)
- Artifact location for cross-node handoff debugging
- 9 Common Failure Modes, each with root cause + concrete fix:
- $BASE_BRANCH unresolvable
- Env-leak gate (5 remediations)
- Claude/Codex binary not found (compiled-binary-only)
- "running" forever (AI working / orphan / idle_timeout)
- Mid-workflow failure and auto-resume semantics
- Approval gate missing on web UI (workflow-level interactive:)
- MCP plugin connection noise (filtered by design)
- Empty $nodeId.output / field access (4 causes)
- Diagnostic command cheat sheet (list, status, isolation list, validate,
tail-log, --verbose, LOG_LEVEL=debug)
- Escalation protocol (version + validate + log tail + CHANGELOG + issue)
SKILL.md routing table now dispatches "Workflow good practices /
anti-patterns" and "Troubleshoot a failing / stuck workflow" to the new
references so an agent can find them without having to know they exist.
* docs(book): update node-types coverage from four to all seven
The book is the curated first-contact reading path (landing page → "Get
Started" → /book/). Both dag-workflows.md and quick-reference.md were
stuck on "four node types" — missing script, approval, and cancel. A user
reading the book as their first introduction would form an incomplete
mental model, then find three more node types in the reference section
later with no explanation of when they arrived.
book/dag-workflows.md:
- "four node types" → "seven node types. Exactly one mode field is
required per node"
- Table now lists Command, Prompt, Bash, Script, Loop, Approval, Cancel
with one-line "when to use" for each, and cross-links to the dedicated
guide pages for Script / Loop / Approval
- New sections below the table for Script (inline + named examples with
runtime and deps), Approval (with the interactive: true workflow-level
note that's easy to miss), and Cancel (guarded-exit pattern) — keeping
the existing narrative shape for Bash and Loop
book/quick-reference.md:
- Node Options table now includes script, approval, cancel rows
- agents row added (inline sub-agents, Claude-only)
- New "Script-specific fields" and "Approval-specific fields" subsections
so the cheat-sheet is actually complete rather than pointing users
elsewhere for the required constraints
- Retry row callout that loop nodes hard-error on retry — previously
omitted
- bash timeout note widened to cover script timeout (same semantics)
Both files are docs-web content; the CI build on the docs-script-nodes
PR (#1362) previously validated the Starlight build path with a similar
table addition, so this should render clean.
* fix(skill/cli): remove nonexistent \`archon workflow cancel\`, fix workflow status jq recipe
Two accuracy issues from the PR code-reviewer (comment 4311243858).
C1: \`archon workflow cancel <run-id>\` does NOT exist as a CLI subcommand.
The switch at packages/cli/src/cli.ts:318-485 dispatches on list / run /
status / resume / abandon / approve / reject / cleanup / event — running
\`archon workflow cancel\` hits the default case and exits with "Unknown
workflow subcommand: cancel" (cli.ts:478-484). Active cancellation is
only available via:
- /workflow cancel <run-id> chat slash command (all platforms)
- Cancel button on the Web UI dashboard
- POST /api/workflows/runs/{runId}/cancel REST endpoint
cli-commands.md: removed the \`### archon workflow cancel <run-id>\`
subsection; kept the \`abandon\` subsection but made it explicit that
abandon does NOT kill a subprocess. Added a call-out box at the bottom
of the abandon section explaining where to go for actual cancellation.
troubleshooting.md "running forever" section: split the original
cancel-vs-abandon advice into three bullets — Web UI / CLI abandon (for
orphans, no subprocess kill) / chat \`/workflow cancel\` (for live runs
that need interruption). Added an explicit "there is no archon workflow
cancel CLI subcommand" parenthetical since the wrong command was being
suggested in flow.
I1: the \`archon workflow list --json\` diagnostic used an incorrect jq
filter. workflow list's --json output (workflow.ts:185-219) has shape
{ workflows: [{ name, description, provider?, model?, ... }], errors: [...] }
with no \`runs\` field — \`jq '.workflows[] | select(.runs)'\` returns empty
unconditionally. Replaced with \`archon workflow status --json | jq '.runs[]'\`,
which matches the actual shape of workflowStatusCommand at
workflow.ts:852+ ({ runs: WorkflowRun[] }). Also tightened the narration
to distinguish JSON from human-readable status output.
No change to the commit history in this PR — these are follow-up fixes
to claims I introduced in earlier commits of this branch (f10b989e for
C1, 66d2b86e for I1).
* fix(skill): remove env-leak gate references (feature was removed in provider extraction)
C2 from the PR code-reviewer (comment 4311243858). The pre-spawn env-leak
gate was removed from the codebase during the provider-extraction refactor
— see TODO(#1135) at packages/providers/src/claude/provider.ts:908. Zero
hits for --allow-env-keys / allowEnvKeys / allow_env_keys / allow_target_repo_keys
across packages/. The CLI's parseArgs (cli.ts:182-208) has no
--allow-env-keys option, and because parseArgs uses strict: false, an
unknown --allow-env-keys would be silently ignored rather than error.
What remains accurate and is NOT touched:
- Three-Path Env Model section (user/repo archon-owned envs are loaded;
target repo <cwd>/.env keys are stripped from process.env at boot)
still correctly describes current behavior, grounded in
packages/paths/src/strip-cwd-env.ts + env-integration.test.ts
- Per-Project Env Injection section (Option 1: .archon/config.yaml env:
block; Option 2: Web UI Settings → Projects → Env Vars) is unchanged —
both remain the sanctioned way to get env vars into subprocesses
Removed claims (all three files):
- cli-commands.md: --allow-env-keys flag row in the workflow run flags
table
- repo-init.md: the "Env-leak gate" subsection at the end of Per-Project
Env Injection listing 5 remediations (all of which reference UI/CLI/
config surfaces that don't exist). Replaced with a succinct callout
that explains the actual current behavior — target repo .env keys are
stripped, workflows that need those values should use managed
injection — so the reader still gets the "where to put my env vars"
answer
- troubleshooting.md: the "Cannot register: codebase has sensitive env
keys" section (error message that can no longer be emitted)
If the env-leak gate is ever resurrected per TODO(#1135), the docs can be
re-added then. The CHANGELOG v0.3.0 entry describing the gate is a
historical record of past behavior and does not need to be rewritten.
* fix(skill/troubleshooting): correct JSONL event type names and field name
C3 from the PR code-reviewer (comment 4311243858). The troubleshooting
reference's event-types table used _started / _completed / _failed
suffixes, but packages/workflows/src/logger.ts:19-30 shows the actual
WorkflowEvent.type enum is:
workflow_start | workflow_complete | workflow_error |
assistant | tool | validation |
node_start | node_complete | node_skipped | node_error
The second jq recipe also queried `.event` but the discriminator is `.type`.
Fixes:
- Event table: renamed columns (_started → _start, _completed → _complete,
_failed → _error). Explicitly called out the field name as `type` so the
reader knows what jq selector to use
- Replaced the "tool_use / tool_result" row with a single `tool` row and
listed its actual payload fields (tool_name, tool_input, duration_ms,
tokens) — tool_use/tool_result are SDK message kinds that appear within
the AI stream, not top-level log event types
- Added a `validation` row (was missing; it's emitted by workflow-level
validation calls with `check` and `result` fields)
- Removed `retry_attempt` row — this event type is not emitted to the
JSONL file. Retry bookkeeping goes through pino logs, not the workflow
log file
- Added an explicit callout that loop_iteration_started /
loop_iteration_completed (and other emitter-only events) go through
the workflow event emitter + DB workflow_events table, NOT the JSONL
file. Pointed readers to the DB or Web UI for loop-level detail. This
distinguishes the two parallel event systems — easy to conflate
(store.ts:11-17 uses _started/_completed/_failed for the DB side,
logger.ts uses _start/_complete/_error for JSONL)
- Fixed the "all failed events" jq recipe: .event → .type and _failed → _error
- Minor cleanup: the inline "tool_use events" mention in the "running
forever" section said the wrong event name — updated to "tool or
assistant events in the tail"
Grounded in packages/workflows/src/logger.ts (canonical JSONL event
shape) and packages/workflows/src/store.ts (the parallel DB event
naming, which the reviewer correctly flagged as different and worth
keeping distinct).
* fix(skill): two stragglers from the code-reviewer audit
Cleanup of two references that slipped through the earlier C1 and C3 fixes:
- references/troubleshooting.md:126: \`node_failed\` → \`node_error\`
(the "Node output is empty" diagnostics section references the JSONL
log, which uses the logger.ts enum — not the DB workflow_events table
which does use \`node_failed\`). The C3 fix corrected the event table
and one jq recipe but missed this inline mention.
- references/interactive-workflows.md:106: removed \`archon workflow
cancel <run-id>\` (nonexistent CLI subcommand) from the
troubleshooting bullet. This was pre-existing before the hardening
PR but fell within the C1 remediation scope. Replaced with the
correct triage: reject (approval gate only) vs abandon (orphan
cleanup, no subprocess kill) vs chat /workflow cancel (actual
subprocess termination).
Grounded in the same sources as the earlier C1/C3 commits:
packages/cli/src/cli.ts:318-485 (no cancel case) and
packages/workflows/src/logger.ts:19-30 (JSONL type enum).
* feat(skill): point to archon.diy as the canonical docs source
The skill had no reference to archon.diy (the live docs site built from
packages/docs-web/). Several reference files said "see the docs site"
without naming the URL, leaving the agent to guess or grep the repo for
the hostname. An agent with the skill loaded should know that when the
distilled reference pages don't cover a case, the full canonical docs
are one WebFetch away.
SKILL.md: new "Richer Context: archon.diy" section between Routing and
Running Workflows. Covers:
- When to reach for the live docs (longer examples, tutorial framing,
features the skill only mentions in passing, "where's that
documented?" user questions)
- URL map — 13 starting points covering getting-started, book (tutorial
series), guides/ (authoring + per-node-type + per-node-feature),
reference/ (variables, CLI, security, architecture, configuration,
troubleshooting), adapters/, deployment/
- Precedence: skill refs first (context-cheap, tuned for agents), docs
site as escalation. Prevents agents defaulting to WebFetch when a
local skill ref already covers the answer
Also upgrades the 5 existing generic "docs site" mentions across
reference files to concrete archon.diy URLs with anchor fragments where
helpful:
- good-practices.md: Inline sub-agents pattern → archon.diy/guides/
authoring-workflows/#inline-sub-agents
- troubleshooting.md: "Install page on the docs site" → archon.diy/
getting-started/installation/
- workflow-dag.md: "Workflow Description Best Practices" → anchor link;
sandbox schema reference → archon.diy/guides/authoring-workflows/
#claude-sdk-advanced-options
- repo-init.md: Security Model reference → archon.diy/reference/
security/#target-repo-env-isolation (deep-link into the section that
covers the <cwd>/.env strip behavior)
URL source of truth: astro.config.mjs:5 (site: 'https://archon.diy').
URL structure mirrors packages/docs-web/src/content/docs/<section>/
<page>.md — verified by the 62 pages the docs build produces.
* chore(workflows): switch default Opus pin to opus[1m] alias (#1395)
Anthropic's Opus 4.7 landed 2026-04-16; on the Anthropic API, opus /
opus[1m] now resolve to 4.7 with a 1M context window at standard
pricing. Using the alias instead of the hard-pinned claude-opus-4-6[1m]
lets bundled default workflows auto-track the recommended Opus version.
No explicit effort is set, so nodes inherit the per-model default
(xhigh on 4.7, high on 4.6).
* fix(workflow): migrate piv-loop plan handoff to $ARTIFACTS_DIR (#1398)
* fix(workflow): migrate piv-loop plan handoff to $ARTIFACTS_DIR (#1380)
The create-plan node used a relative path (.claude/archon/plans/{slug}.plan.md)
that the AI agent would sometimes write to a different location, breaking all
downstream nodes that glob for the plan file. Migrated all plan/progress file
references to $ARTIFACTS_DIR/plan.md and $ARTIFACTS_DIR/progress.txt, matching
the pattern used by archon-fix-github-issue and other workflows.
Changes:
- Replace slug-based plan path with $ARTIFACTS_DIR/plan.md in create-plan node
- Replace ls -t glob discovery with direct $ARTIFACTS_DIR/plan.md reads in
refine-plan, code-review, and fix-feedback nodes
- Replace empty-string guard with file-existence check in implement-setup bash
- Migrate progress.txt references in implement loop to $ARTIFACTS_DIR/
- Add explicit plan/progress paths in finalize node
- Regenerated bundled-defaults.generated.ts
Fixes #1380
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(workflow): address review findings in archon-piv-loop
- Rename 'Step 2: Write the Plan' to 'Step 2: Plan File Location' to
eliminate the duplicate heading that collided with Step 3's identical
title in the create-plan node
- Guard implement-setup against a 0-task plan file: exit 1 with a
clear error when no '### Task N:' sections are found, preventing a
silent no-op implement loop
- Remove 2>/dev/null from code-review commit so pre-commit hook failures
and other stderr are visible to the agent instead of silently swallowed
- Replace '|| true' on git push in finalize with an explicit WARNING echo
so push failures (auth, upstream conflict, no remote) surface to the
agent rather than being silently ignored
- Regenerate bundled-defaults.generated.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(workflows): regenerate bundled defaults to match opus[1m] alias
The bundle was stale relative to the YAML sources after #1395 merged —
check:bundled was failing CI. Regenerated; no YAML edits.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(workflows): add anyFailed status derivation coverage for DAG executor (#1403)
PIV Task 1: Adds three new tests in a dedicated describe block
'executeDagWorkflow -- final status derivation' covering the anyFailed
branch (dag-executor.ts ~line 2956) that previously had no direct test:
- one success + one independent failure calls failWorkflowRun (not completeWorkflowRun)
- multiple successes + one failure calls failWorkflowRun (not completeWorkflowRun)
- trigger_rule: none_failed skips dependent node but anyFailed still marks run failed
Fixes #1381.
* docs/skill: add parameter-matrix.md quick-lookup reference
New reference for the archon skill: a single-glance lookup of which
parameter works on which node type, an intent-based "how do I..." table,
a consolidated silent-failure catalog, and an inline agents: section
(previously only referenced via archon.diy).
Purpose is complementary, not duplicative:
- workflow-dag.md remains the authoring guide
- dag-advanced.md remains the hooks/MCP/skills/retry deep-dive
- good-practices.md remains the patterns and anti-patterns
- parameter-matrix.md is the grep-this-first lookup when you know the
outcome you want but not which field gets you there
Also registers the new reference in SKILL.md routing table.
* docs: point contributors at PR template and Closes #N convention
Add explicit references to .github/PULL_REQUEST_TEMPLATE.md in both
CONTRIBUTING.md and CLAUDE.md, plus a reminder to link issues with
Closes/Fixes/Resolves so they auto-close on merge. Repo-triage runs
were flagging dozens of partially-filled or unlinked PRs each cycle.
* feat(workflows): add maintainer-standup workflow for daily PR/issue triage (#1428)
* feat(workflows): add maintainer-standup workflow for daily PR/issue triage
Daily morning briefing that pulls origin/dev, triages all open PRs and assigned
issues against direction.md, and surfaces progress vs. the previous run. Designed
for live-checkout use (worktree.enabled: false) so it can read its own state.
Layout under .archon/maintainer-standup/:
- direction.md (committed) — project north-star: what Archon IS / IS NOT.
Drives PR P4 polite-decline classification with cited clauses.
- README.md / profile.md.example — setup docs and template for new maintainers.
- profile.md, state.json, briefs/YYYY-MM-DD.md — gitignored, per-maintainer.
Engine:
- 3 parallel gather scripts in .archon/scripts/maintainer-standup-*.ts
(git-status, gh-data, read-context) — bun runtime, JSON stdout.
- Synthesis node: command file with output_format schema for
{ brief_markdown, next_state }.
- Persist node: tiny inline bun script writes both to disk.
Run-to-run continuity: state.json carries observed_prs/issues snapshots, so the
next run can detect what merged, what closed, what the maintainer shipped, and
which carry-over items aged past N days.
Also adds .archon/** to the ESLint global ignore list (matches the existing
.claude/skills/** pattern) since .archon/ is user content and not part of any
tsconfig project.
* fix(maintainer-standup): address CodeRabbit review on #1428
- gh-data: bump --limit 100 → 1000 on all_open_prs and warn loudly when
the cap is hit; preserves the observed_prs invariant the next-run
"resolved since last run" diff depends on. (CodeRabbit critical)
- maintainer-standup.md: clarify P1 CI signal — the gathered payload only
carries mergeStateStatus, not statusCheckRollup; for borderline P1s,
drill in via `gh pr checks <n>`. (CodeRabbit minor)
- workflow.yaml persist: write briefs under local YYYY-MM-DD (sv-SE
locale) instead of UTC ISO date, so an evening run doesn't file
tomorrow's brief and break recent_briefs lookups. (CodeRabbit minor)
- workflow.yaml persist: wrap state/brief writes in try/catch; on
failure dump brief_markdown and next_state to stderr so a 5-minute
Sonnet synthesis isn't lost to a transient disk error. (CodeRabbit minor)
- gh-data + git-status: switch from execSync (shell-string) to
execFileSync (argv array) for git/gh invocations. Defense-in-depth
against shell metacharacters in values that pass through (esp. the
gh_handle from profile.md). (CodeRabbit nitpick)
* feat(workflows): support explicit tags in workflow YAML (#1190)
Add optional `tags: string[]` to `workflowBaseSchema`. Explicit values take precedence over keyword inference; `tags: []` suppresses inference end-to-end; omitting the field falls back to inference (backwards compatible). Non-array values warn-and-ignore matching the sibling `worktree`/`additionalDirectories` patterns.
* feat(workflows): add maintainer-review-pr and group maintainer workflows under maintainer/ (#1430)
* feat(workflows): add maintainer-review-pr and group maintainer workflows under .archon/workflows/maintainer/
Adds the maintainer-review-pr workflow — a Pi/Minimax-based PR triage
flow that gates on direction alignment, scope focus, and PR-template
quality before doing any deep review. If the gate clears, runs the
five review aspects (code/error-handling/test-coverage/comment-quality/
docs-impact) as parallel Archon nodes and auto-posts a synthesized
review comment. If the gate fails (direction conflict, multiple
concerns, sprawling scope), drafts a polite-decline comment and pauses
for the maintainer's approval before posting.
Reorganizes the existing maintainer-standup workflow into the same
subfolder so all maintainer-facing workflows live together. Subfolder
grouping is supported by the workflow loader (1 level deep, resolution
by filename).
What lands:
- .archon/workflows/maintainer/maintainer-standup.yaml (moved from
.archon/workflows/maintainer-standup.yaml)
- .archon/workflows/maintainer/maintainer-review-pr.yaml (new)
- .archon/commands/maintainer-review-{gate,code-review,error-handling,
test-coverage,comment-quality,docs-impact,synthesize,report}.md (new,
Pi-tuned variants of the existing review-agent commands so they avoid
Claude-only Task / sub-agent patterns)
Pi/Minimax integration:
- Uses provider: pi, model: minimax/MiniMax-M2.7 — verified via the
e2e-minimax-smoke test that Pi correctly routes to Minimax (session
jsonl confirms provider=minimax) and that Pi's best-effort
output_format parser handles the gate's nested schema.
- Two test runs landed real comments: a direction-decline on PR #1335
and a deep-review on PR #1369. Both were posted to GitHub via the
workflow's gh pr comment node.
* chore(workflows): also group repo-triage under .archon/workflows/maintainer/
repo-triage is the third maintainer-facing workflow alongside maintainer-standup and maintainer-review-pr; group it in the same subfolder for consistency. Subfolder resolution is by filename so the workflow name is unchanged.
* feat(pi): use ModelRegistry to support custom models and skip auth for unmapped providers (#1284)
Closes #1096.
- Switch Pi provider model lookup from pi-ai's getModel() (static catalog
only) to ModelRegistry.create(authStorage).find() so user-configured
custom models in ~/.pi/agent/models.json (LM Studio, ollama, llamacpp,
custom OpenAI-compatible endpoints) are discoverable.
- Remove the local lookupPiModel helper.
- For env-var-mapped providers (anthropic, openai, etc.) still throw
with a pi /login hint when credentials are missing. For unmapped
providers, log pi.auth_missing at info and continue so local models
that don't need credentials work without ceremony.
- Surface modelRegistry.getError() in the not-found message and emit
pi.model_not_found so users debugging custom-provider configs see the
real cause (e.g. missing baseUrl in models.json).
- Guard AuthStorage.create() and ModelRegistry.create() with try/catch
so a malformed ~/.pi/agent/auth.json surfaces with Pi-framed context
instead of a raw SDK stack trace.
- Document the credential-free path for local providers in ai-assistants.md.
Co-authored-by: Matt Chapman <Matt@NinjitsuWeb.com>
* chore(workflows): group smoke-test workflows under test-workflows/ + add e2e-minimax-smoke (#1431)
* chore(workflows): group all smoke-test workflows under .archon/workflows/test-workflows/
Move the 7 existing e2e-*.yaml smoke tests plus the new e2e-minimax-smoke
test into a dedicated subfolder. Subfolder grouping is supported by the
workflow loader (1 level deep, resolution by filename) so workflow names
are unchanged. Mirrors the .archon/workflows/maintainer/ split landing
in #1430.
Also adds e2e-minimax-smoke.yaml — a sanity check that Pi correctly
routes to Minimax M2.7 via the user's local pi auth, and that Pi's
best-effort output_format parser handles a small nested schema. Asserts
routing by reading the most recent Pi session jsonl rather than asking
the model to self-identify (LLMs are unreliable narrators about their
own identity, especially when Pi's system prompt mentions other
providers as defaults).
* fix(e2e-minimax-smoke): address CodeRabbit review on #1431
- Widen find window from -mmin -3 to -mmin -10. The smoke's three Pi
nodes plus the assert can collectively run several minutes on slow
networks; 3 minutes was tight enough to false-FAIL on a healthy run.
(CodeRabbit minor)
- Drop non-deterministic `head -1` over `find` output. find doesn't
guarantee any order; on a tie, the wrong file would be picked. Now
iterates all matching sessions and breaks on first one carrying the
routing signal — any match is sufficient evidence. (CodeRabbit minor)
- Replace single-regex `'"provider":"minimax".*"modelId":"MiniMax-M2.7"'`
with two separate greps joined by `&&`. JSON field order isn't part of
Pi's contract; a future Pi release reordering `provider` and `modelId`
in the model_change event would silently false-FAIL the original
pattern. The new check is order-independent. (CodeRabbit major)
* fix(maintainer-review): address CodeRabbit findings on #1430 (#1432)
Six findings, two majors and four minors/nitpicks:
- gate.md L17 vs L77: resolved conflicting input-source instructions.
Body claimed "all inline, no extra fetch" while a later phase
permitted reading PULL_REQUEST_TEMPLATE.md. Now: explicit "one
allowed extra read" callout in Phase 1 + matching wording in Gate C.
(CodeRabbit major)
- gate.md fenced blocks: added missing language identifiers (text/json/
markdown) to satisfy markdownlint MD040. (CodeRabbit minor)
- gate.md L155 + read-context.ts: deterministic clock. The 3-day deadline
was anchored to prior_state.last_run_at, which can be stale and produce
past-dated deadlines. Moved both today and deadline_3d into the
read-context.ts output (computed via sv-SE locale → ISO date in local
time) and instructed the gate to use $read-context.output.deadline_3d
directly. LLMs are unreliable at calendar arithmetic; this avoids it
entirely. (CodeRabbit major)
- maintainer-review-pr.yaml fetch-diff: dropped 2>/dev/null on gh pr diff
so auth / network / deleted-PR failures fail the node instead of
feeding an empty diff to the gate. Empty-but-successful diff (PR has
no changes) is now an explicit marker the gate can detect. (CodeRabbit
minor)
- maintainer-review-pr.yaml approve-unclear: added capture_response: true
so the maintainer's approve comment flows to the report node. Reject
reasoning is already captured by Archon's run record. (CodeRabbit
minor)
- maintainer-review-pr.yaml post-decline + report.md: the gh pr edit
--add-label call previously swallowed all errors with || true and the
report still claimed the label was applied. Now writes applied/skipped
to $ARTIFACTS_DIR/.label-applied + the gh stderr to .label-error so
the report can describe the actual outcome. (CodeRabbit nitpick)
* fix(workflows): approval gate bypass after reject-with-redraft on resume (#1435)
* fix(workflows): approval gate bypass after reject-with-redraft on resume
When an approval node was rejected with on_reject.prompt, the synthetic
PromptNode built to run the on_reject prompt reused the approval gate's
own node ID. executeNodeInternal then wrote a node_completed event with
that ID, causing getCompletedDagNodeOutputs to treat the gate as already
completed on the next resume — bypassing the human gate entirely.
Fix: give the synthetic node the ID `${node.id}:on_reject` so its
node_completed event has a distinct step_name that won't match the
approval gate slot in priorCompletedNodes.
Adds a regression test asserting no node_completed event with the
approval gate's ID is written during on_reject execution.
Fixes #1429
* test(workflows): add positive assertion and SSE side-effect comment for on_reject synthetic node
Add complementary positive assertion to the regression test to verify that
node_completed is written exactly once with step_name 'review:on_reject',
ensuring future refactors that suppress the event entirely would be caught.
Add inline comment in executeApprovalNode documenting the known SSE side-effect:
node_started/node_completed events with nodeId='review:on_reject' flow through
the SSE pipeline into the web UI, resulting in a transient phantom node in the
execution view. This is cosmetic-only — the human gate contract is preserved.
* simplify: reduce duplicate cast pattern in on_reject test assertions
* feat(workflows): add mutates_checkout to allow concurrent runs on live checkout (#1438)
* feat(workflows): add mutates_checkout field to skip path-lock for concurrent runs
Add `mutates_checkout: boolean` (optional, default true) to the workflow
schema. When set to false, the executor skips the path-exclusive lock
that serializes all runs on the same working path, allowing N concurrent
runs on the same live checkout.
The primary use case is `maintainer-review-pr`, which reads shared state
but writes only to per-run artifact paths and GitHub PR comments — two
parallel reviews of different PRs should not fail with "Workflow already
active on this path".
Changes:
- `schemas/workflow.ts`: add optional `mutates_checkout` field
- `loader.ts`: parse and propagate the field (warn-and-ignore on invalid values)
- `executor.ts`: wrap path-lock guard in `if (workflow.mutates_checkout !== false)`
- `executor.test.ts`: two new tests in the concurrent-run guard suite
- `maintainer-review-pr.yaml`: opt in with `mutates_checkout: false`
* test(workflows): add loader tests for mutates_checkout parsing
- Add 5 tests covering false, true, omitted, and invalid (string "yes") values
- Invalid non-boolean values are silently dropped with warn — now explicitly tested
- Remove the // end mutates_checkout guard trailing comment (no precedent in file)
- Clarify loader comment: "parse/warn pattern" not "warn-and-ignore pattern" to avoid implying the return style matches interactive
* simplify: collapse nodeType/aiFields pair into single nonAiNode object in parseDagNode
* docs: replace String.raw with direct assignment in script node examples (#1434)
* docs: replace String.raw with direct assignment in script node examples
String.raw`$nodeId.output` fails silently when substituted output contains
a backtick, terminating the template literal early and producing cryptic parse
errors. JSON is valid JS expression syntax, so direct assignment is safe for
all valid JSON values including those with backticks.
- Replace String.raw pattern in dag-workflow.yaml example
- Replace String.raw pattern in archon-workflow-builder.yaml template
- Add CAUTION bullet in workflow-dag.md Script Node section
- Add Silent Failures item #14 in parameter-matrix.md
- Add Starlight caution aside in script-nodes.md
- Extend script bodies bullet in variables.md
- Regenerate bundled-defaults.generated.ts
Fixes #1427
* docs: fix Rule 6 in generate-yaml prompt to distinguish bun vs uv patterns
Rule 6 still referenced JSON.parse after the example was updated to direct
assignment, creating a contradiction for the AI code generator. Update the
prose to explicitly distinguish TypeScript/bun (direct assignment) from
Python/uv (json.loads), matching the updated embedded example.
* chore(workflows): group experimental workflows under .archon/workflows/experimental/
Move two repo-scoped workflows that were sitting untracked at the workflow
root into a dedicated subfolder. Subfolder grouping is supported by the
loader (1 level deep, resolution by filename), so workflow names are
unchanged and the /release skill still resolves archon-release correctly.
Files moved:
- archon-fix-github-issue-experimental.yaml — Path-A variant of the
issue-fix workflow used today to land #1434, #1435, #1438.
- archon-release.yaml — the live release workflow used by the /release
skill end-to-end (validate -> binary smoke -> version bump -> changelog
-> approval -> commit -> PR -> tag -> Homebrew formula update).
* fix(workflows): export ARTIFACTS_DIR, LOG_DIR, BASE_BRANCH to bash nodes (#1387)
executeBashNode previously only merged explicit envVars on top of
process.env. The three well-known workflow directories (artifactsDir,
logDir, baseBranch) were passed as function parameters and used for
compile-time substitution of $ARTIFACTS_DIR / $LOG_DIR / $BASE_BRANCH
in the script body, but were never added to the subprocess environment.
As a result, any script that relied on shell-runtime expansion — e.g.
JSON_FILE="${ARTIFACTS_DIR}/foo.output.json" inside a heredoc, an
inherited helper script, or a `bash -c` subshell — saw the variable
unset and silently fell back to its default (typically an empty string
or "."), writing artifacts to the workflow cwd instead of the nominal
artifacts directory.
Always build subprocessEnv from process.env plus the three well-known
directories, then allow explicit envVars to override. Compile-time
substitution behavior is unchanged; existing scripts that do not
reference these variables are unaffected; user-supplied envVars still
win on conflict.
* fix(workflow): substitute $nodeId.output refs in approval messages (#1426)
* fix(workflow): substitute \$nodeId.output refs in approval messages
Approval node messages were emitted as raw strings, bypassing the
substituteNodeOutputRefs() pass that prompt/bash/loop/cancel nodes
all run. This made interactive workflows like atlas-onboard show
literal "\$gather-context.output.repo_name" placeholders to humans
at HITL gates, leaving them unable to know what they were approving.
Fix: rendered the approval.message through substituteNodeOutputRefs
once at the top of the standard approval gate path, then used the
resolved string in all 4 emission sites (safeSendMessage,
createWorkflowEvent, pauseWorkflowRun, event-emitter).
Test: new dag-executor.test case wires a structured-output upstream
node into an approval node and asserts pauseWorkflowRun receives the
substituted message ("Repo: hcr-els | App: CCELS | Port: 3012")
rather than the literal placeholders.
Repro: any workflow with an approval node whose message references
\$nodeId.output[.field]. Observed in the wild on atlas-onboard's
confirm-context HITL gate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(workflow): extend approval-substitution test to cover all 4 emission sites
Per CodeRabbit review: the original test only verified pauseWorkflowRun
received the substituted message, but the fix touches 4 emission sites.
A future regression at safeSendMessage / createWorkflowEvent / event-emitter
would silently leave the test passing while users still saw raw $node.output
placeholders.
Adds two additional assertions:
- platform.sendMessage prompt contains substituted message + does NOT
contain literal $gather-context.output placeholders
- The persisted approval_requested workflow event's data.message is
substituted
Event-emitter assertion deferred (no existing pattern for spying on the
global emitter in this test file). Two of three secondary surfaces
covered closes the practical regression risk — both are user-visible
(chat prompt + audit-log event); the emitter is internal only.
Test count: 7 pass / 22 expect() (was 18). Full suite 193 pass / 353
expect() — no regressions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(workflows): expose $LOOP_PREV_OUTPUT in loop node prompts (#1286) (#1367)
* feat(workflows): expose $LOOP_PREV_OUTPUT in loop node prompts (#1286)
Adds a new substitution variable that carries the previous loop iteration's
cleaned output into the next iteration's prompt. Empty on iteration 1; the
prior iteration's output (after stripCompletionTags) on iteration 2+.
Why: fresh_context: true loops have no way to reference what the previous
pass produced or why it failed without dragging the full session forward.
$LOOP_PREV_OUTPUT closes that gap with zero session-cost — same trust
boundary as $nodeId.output, no new external surface.
Changes:
- packages/workflows/src/executor-shared.ts: substituteWorkflowVariables
accepts a 10th positional loopPrevOutput arg and substitutes
$LOOP_PREV_OUTPUT (defaults to '').
- packages/workflows/src/dag-executor.ts: executeLoopNode passes
lastIterationOutput on iteration 2+ (and explicit '' on iteration 1 /
the first iteration of an interactive resume, since lastIterationOutput
is a per-call variable that does not survive resume metadata).
- Unit tests: 3 new cases in executor-shared.test.ts.
- Integration tests: 2 new cases in dag-executor.test.ts verifying the
prompt sent to the AI on iter 1 vs iter 2, and that the value reflects
cleaned output (no <promise> tags).
- Docs: variables.md, loop-nodes.md (new "Retry-on-failure" pattern),
CLAUDE.md variable reference.
Backward compatibility: prompts that don't reference $LOOP_PREV_OUTPUT are
unaffected. All 843 workflow tests + type-check + lint + format:check +
bun run validate pass locally.
* docs: address coderabbit review on variables/loop-nodes
- variables.md: include $LOOP_PREV_OUTPUT in substitution-order list and
availability table to match the new variable row at line 30
- loop-nodes.md: document the interactive-resume exception where the first
iteration after an approval-gate resume still receives an empty
$LOOP_PREV_OUTPUT regardless of iteration number (per dag-executor.ts
L1781-1783 where i === startIteration always clears prev output)
* docs(changelog): add Unreleased entry for $LOOP_PREV_OUTPUT (#1367 review)
* test(loop): add resume-from-approval integration test for $LOOP_PREV_OUTPUT (#1367 review)
Per maintainer-review-pr suggestion (Wirasm): two-call integration test
covering the resume-from-approval scenario.
- Call 1: fresh interactive loop pauses at the gate after iteration 1 and
asserts $LOOP_PREV_OUTPUT substitutes to empty on iter 1 (no prior
output) plus the gate pause is recorded.
- Call 2: resumed run with metadata.approval populated. The first
resumed iteration must substitute $LOOP_PREV_OUTPUT to '', NOT to the
paused run's iter-1 output (which lived in a different process and is
not persisted). $LOOP_USER_INPUT still flows through as normal.
Locks the documented invariant at dag-executor.ts:1769-1772.
---------
Co-authored-by: voidborne-d <DottyEstradalco@allergist.com>
* feat(maintainer-standup): surface contributor replies since last run (#1457)
The brief was missing a key signal — when contributors reply on PRs or
issues, the maintainer wouldn't see it explicitly. Empirically reviewed
PR replies were buried under aggregate updatedAt timestamps with no
indication of WHO replied or WHAT they said.
This adds a new "Replies waiting on you" section to the daily brief,
sourced from two paginated GitHub API calls scoped by since=last_run_at:
- /repos/{o}/{r}/issues/comments PR + issue conversation comments
- /repos/{o}/{r}/pulls/comments inline code-review comments
Filters applied:
- Skip the maintainer's own comments (gh_handle from profile.md)
- Skip GitHub bot accounts (login ending in [bot]) — coderabbitai,
chatgpt-codex-connector, dependabot, etc. They post a constant
churn of automated review tooling that drowns out human replies;
the maintainer wants the latter.
Output is grouped by PR/issue number with kind classification:
- issue comment on a non-PR issue
- pr_conversation PR conversation-level comment
- pr_review inline code-review comment (most actionable —
usually needs a code-level response, so kind
upgrades to pr_review whenever review comments
arrive on a PR that also has conversation ones)
Sorted by recency (newest reply first). Synthesizer reads
gh-data.output.replies_since_last_run and renders a section.
Verified on a backdated state.json (last_run_at = yesterday morning):
22 human replies on 22 PRs/issues, bot noise filtered (32 → 22 after
the [bot] filter). Surfaces…
Summary
script:nodes (TypeScript/JS via bun, Python via uv) have been a first-class DAG node type since v0.3.3 but were effectively undocumented. The only mentions on the docs site were two one-liners inreference/archon-directories.md/reference/security.md; theguides/authoring-workflows.mdnode-types table andbook/dag-workflows.mdstill said "four node types" and omittedscriptentirely. The archon skill was worse —SKILL.mdandreferences/workflow-dag.mdboth stated "Each node has exactly ONE of:command,prompt,bash, orloop", so an agent using the skill literally could not author a script node.bash: "bun -e …"/bash: "python3 -c …". That losesbun --no-env-fileenv isolation,uv run --with <dep>dependency pinning, and the.archon/scripts/reuse story (repo > home precedence). The feature is real and validated; it just wasn't visible to anyone using the skill.guides/script-nodes.md(parallel structure toloop-nodes.md/approval-nodes.md), plus minimal surgical updates to the two node-type tables that omittedscript, and the variables reference. Skill updated to include a Script node block, full reference section, and a script node in the example workflow.book/dag-workflows.mdandbook/quick-reference.mdalso still say "four node types" but fixing those properly needs addingapproval,cancel, ANDscript— out of scope for this PR.Changes
Docs site
packages/docs-web/src/content/docs/guides/script-nodes.md(sidebar order 5, next to loop/approval) — schema, inline vs named dispatch, runtime/deps semantics,.archon/scripts/→~/.archon/scripts/precedence, extension↔runtime mapping, env isolation (bun --no-env-file), stdout/stderr contract, validator behavior, 3 patterns (adapter / reusable helper / Python-with-deps), explicit list of ignored AI fieldsguides/authoring-workflows.md—scriptrow added to the node-types tableguides/index.md— Script Nodes link under "Node Types"reference/variables.md— broadened from "bash scripts" to includescript:; added a "Shell Quoting inbash:vsscript:" subsection calling out that$nodeId.outputvalues are NOT auto shell-quoted in script bodies (subtle correctness trap when porting bash patterns)guides/hooks.md,mcp-servers.md,skills.md,global-workflows.md,remotion-workflow.md— sidebarorderbumped +1 so script-nodes fits at 5 without renumbering the existing node-type guidesArchon skill
SKILL.md— replaced "Four Node Types" (silently omitted script + approval + cancel) with the accurate seven; added a Script node block showing both inline and named patterns; updated the examples pointerreferences/workflow-dag.md— full Script Node section: schema, inline-vs-named rules, dispatch table, deps semantics, output handling, explicit list of AI-only fields that are ignored; validation-rules list now lists all seven node typesreferences/dag-advanced.md— one-line fix: retry support now correctly lists "command, prompt, bash, and script" nodesreferences/variables.md— added bullet forscript:bodies with the no-shell-quote warningexamples/dag-workflow.yaml— added anextract-labelsTypeScript script node demonstrating the typical adapter pattern; header comment updatedLabel Snapshot
Change Metadata
Linked Issue
None — surfaced during a user request to audit the skill/docs for missing node-type coverage.
Validation Evidence
```bash
bun --filter @archon/docs-web build
→ 62 pages built, /guides/script-nodes/ rendered, no warnings
bun --filter '@archon/workflows' test -t "script"
→ all script-related tests pass
bun -e 'import {parseWorkflow} from …loader; parseWorkflow(yaml)'
→ .claude/skills/archon/examples/dag-workflow.yaml parses clean,
extract-labels recognized as kind: 'script'
bunx prettier --check
→ All matched files use Prettier code style!
```
Full test/type/lint suite (`bun run validate`) was NOT run for this PR — changes are docs-only (14 files, zero TS/YAML-schema impact). CI will run it.
Security Impact
Pure documentation PR.
Compatibility / Migration
Sidebar-order bumps on 5 existing guides could theoretically re-order Starlight's rendered sidebar if anything else pinned those orders — verified none do.
Human Verification
/guides/script-nodes/renders (tables, code fences, internal links all work)parseWorkflow()and the new node classifies as `script`What was NOT verified:
Side Effects / Blast Radius
.claude/skills/archon/skillRollback Plan
Risks and Mitigations
guides/script-nodes.mdandreference/variables.md, and surfaced in the skill's reference.Summary by CodeRabbit
New Features
bun) or Python (viauv) code in workflows with stdout captured as node output.Documentation