feat: script node type for DAG workflows (bun/uv runtimes)#999
Conversation
Implements US-001 from the script-nodes PRD. Changes: - Add scriptNodeSchema with script, runtime (bun|uv), deps, and timeout fields - Add ScriptNode type with never fields for mutual exclusivity - Add isScriptNode type guard - Add SCRIPT_NODE_AI_FIELDS constant (same as BASH_NODE_AI_FIELDS) - Update dagNodeSchema superRefine and transform to handle script: nodes - Update DagNode union type to include ScriptNode - Add script node dispatch stub in dag-executor.ts (fails fast until US-003) - Export all new types and values from schemas/index.ts - Add comprehensive schema tests for ScriptNode parsing and validation
Implements US-002 from PRD. Changes: - Add ScriptDefinition type and discoverScripts() in script-discovery.ts - Auto-detect runtime from file extension (.ts/.js->bun, .py->uv) - Handle duplicate script name conflicts across extensions - Add bundled defaults infrastructure (empty) for scripts - Add tests for discovery, naming, and runtime detection
Implements US-003 from PRD. Changes: - Add executeScriptNode() in dag-executor.ts following executeBashNode pattern - Support inline bun (-e) and uv (run python -c) execution - Support named scripts via bun run / uv run - Wire ScriptNode dispatch replacing 'not yet implemented' stub - Capture stdout as node output, stderr as warning - Handle timeout and non-zero exit - Pass env vars for variable substitution - Add tests for inline/named/timeout/failure cases
Implements US-004 from PRD. Changes: - Add checkRuntimeAvailable() utility for bun/uv binary detection - Extend validator.ts with script file and runtime validation - Integrate script validation into parseWorkflow flow in loader.ts - Add tests for runtime availability detection
Implements US-005 from PRD. Changes: - Support deps field for uv nodes: uvx --with dep1... for inline - Support uv run --with dep1... for named uv scripts - Bun deps are auto-installed at runtime via bun's native mechanism - Empty/omitted deps field produces no extra flags - Add tests for dep injection into both runtimes
Implements US-006 from PRD. Changes: - Fill test coverage gaps for script node feature - Add script + command mutual exclusivity schema test - Add env var substitution tests ($WORKFLOW_ID, $ARTIFACTS_DIR in scripts) - Add stderr handling test (stderr sent to user as platform message) - Add missing named script file validation tests to validator.test.ts - Full bun run validate passes
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (18)
📝 WalkthroughWalkthroughA new "script" DAG node type is introduced to the workflow system, enabling direct execution of TypeScript/Python code via Changes
Sequence Diagram(s)sequenceDiagram
participant WF as Workflow Executor
participant SE as Script Executor
participant RT as Runtime (bun/uv)
participant SF as Script File System
participant LOG as Logger/Platform
WF->>SE: executeScriptNode(script, runtime, deps)
alt Inline Script
SE->>SE: Classify as inline code
SE->>SE: Substitute $WORKFLOW_ID and $ARTIFACTS_DIR
alt runtime === 'bun'
SE->>RT: bun -e <code>
else runtime === 'uv'
SE->>RT: uv run --with <deps> python -c <code>
end
else Named Script
SE->>SF: Discover scripts in .archon/scripts/
alt Script found
SE->>SE: Load ScriptDefinition (path, runtime)
SE->>RT: uv run <path> or bun run <path>
else Script not found
SE->>LOG: Emit error + message
SE-->>WF: return { state: 'failed' }
end
end
RT->>RT: Execute code/script
alt Execution successful
RT-->>SE: stdout (trimmed)
SE->>SE: Capture as $nodeId.output
SE->>LOG: Log any stderr as warning
SE-->>WF: return { state: 'completed', output }
else Timeout exceeded
SE->>LOG: Timeout error + message
SE-->>WF: return { state: 'failed', error }
else Missing runtime
SE->>LOG: ENOENT error + message
SE-->>WF: return { state: 'failed', error }
else Non-zero exit
SE->>LOG: Stderr warning + message
SE-->>WF: return { state: 'failed', error }
end
WF->>WF: Continue downstream nodes with $nodeId.output substitution
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Suggested reviewers
Poem
✨ 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 |
- Extract isInlineScript to executor-shared.ts (was duplicated in dag-executor.ts and validator.ts) - Remove dead warnMissingScriptRuntimes from loader.ts (validator already covers runtime checks) - Remove path traversal fallback in executeScriptNode — error when named script not found instead of executing arbitrary file paths - Memoize checkRuntimeAvailable to avoid repeated subprocess spawns - Add min(1) to scriptNodeSchema.script field for consistency - Replace dynamic import with static import in validator.ts
PR Review Summary — Multi-Agent AnalysisSix specialized agents reviewed this PR. Here are the aggregated findings. Critical Issues (2 found)
Important Issues (6 found)
Suggestions (8 found)
Documentation Issues
Strengths
Type Design Scores
Verdict: NEEDS FIXESBefore merge, fix:
Should fix: Consider: |
Critical fixes: - Wrap discoverScripts() in try-catch inside executeScriptNode to prevent unhandled rejections when script discovery fails (e.g. duplicate names) - Add isScriptNode to isNonAiNode check in loader.ts so AI-specific fields on script nodes emit warnings (activates SCRIPT_NODE_AI_FIELDS) Important fixes: - Surface script stderr in user-facing error messages on non-zero exit - Replace uvx with uv run --with for inline uv scripts with deps - Add z.string().min(1) validation on deps array items - Remove unused ScriptDefinition.content field and readFile I/O - Add logging in discoverAvailableScripts catch block - Warn when deps is specified with bun runtime (silently ignored) Simplifications: - Merge BASH_DEFAULT_TIMEOUT and SCRIPT_DEFAULT_TIMEOUT into single SUBPROCESS_DEFAULT_TIMEOUT constant - Use scriptDef.runtime instead of re-deriving from extname() - Extract shared formatValidationResult helper, deduplicate section comments Tests: - Add isInlineScript unit tests to executor-shared.test.ts - Add named-script-not-found executor test to dag-executor.test.ts - Update deps tests to expect uv instead of uvx Docs: - Add script: node type to CLAUDE.md node types and directory structure - Add script: to .claude/rules/workflows.md DAG Node Types section
…aram conversion The SQLite adapter's convertPlaceholders() regex matches all $N patterns including those in SQL comments. The comment "-- $3 = 1 day" was being counted as a 4th placeholder, causing "expected 3 values, received 4". This blocked all workflow runs (findResumableRun is called on every start). Closes coleam00#999
convertPlaceholders replaces all $N occurrences including inside SQL comments, which caused the param mismatch in findResumableRun. Add tests documenting this known limitation so future SQL additions avoid putting $N in comments. Fixes coleam00#999
…n test Add expect(query).not.toMatch(/--.*\$\d/) to the findResumableRun mocked test to guard the specific query that caused coleam00#999. This prevents a future developer from re-adding a -- \$3 comment to that query and silently breaking SQLite in production.
…aram conversion The SQLite adapter's convertPlaceholders() regex matches all $N patterns including those in SQL comments. The comment "-- $3 = 1 day" was being counted as a 4th placeholder, causing "expected 3 values, received 4". This blocked all workflow runs (findResumableRun is called on every start). Closes coleam00#999
convertPlaceholders replaces all $N occurrences including inside SQL comments, which caused the param mismatch in findResumableRun. Add tests documenting this known limitation so future SQL additions avoid putting $N in comments. Fixes coleam00#999
…n test Add expect(query).not.toMatch(/--.*\$\d/) to the findResumableRun mocked test to guard the specific query that caused coleam00#999. This prevents a future developer from re-adding a -- \$3 comment to that query and silently breaking SQLite in production.
) * feat: add ScriptNode schema and type guards (US-001) Implements US-001 from the script-nodes PRD. Changes: - Add scriptNodeSchema with script, runtime (bun|uv), deps, and timeout fields - Add ScriptNode type with never fields for mutual exclusivity - Add isScriptNode type guard - Add SCRIPT_NODE_AI_FIELDS constant (same as BASH_NODE_AI_FIELDS) - Update dagNodeSchema superRefine and transform to handle script: nodes - Update DagNode union type to include ScriptNode - Add script node dispatch stub in dag-executor.ts (fails fast until US-003) - Export all new types and values from schemas/index.ts - Add comprehensive schema tests for ScriptNode parsing and validation * feat: script discovery from .archon/scripts/ Implements US-002 from PRD. Changes: - Add ScriptDefinition type and discoverScripts() in script-discovery.ts - Auto-detect runtime from file extension (.ts/.js->bun, .py->uv) - Handle duplicate script name conflicts across extensions - Add bundled defaults infrastructure (empty) for scripts - Add tests for discovery, naming, and runtime detection * feat: script execution engine (inline + named) Implements US-003 from PRD. Changes: - Add executeScriptNode() in dag-executor.ts following executeBashNode pattern - Support inline bun (-e) and uv (run python -c) execution - Support named scripts via bun run / uv run - Wire ScriptNode dispatch replacing 'not yet implemented' stub - Capture stdout as node output, stderr as warning - Handle timeout and non-zero exit - Pass env vars for variable substitution - Add tests for inline/named/timeout/failure cases * feat: runtime availability validation at load time Implements US-004 from PRD. Changes: - Add checkRuntimeAvailable() utility for bun/uv binary detection - Extend validator.ts with script file and runtime validation - Integrate script validation into parseWorkflow flow in loader.ts - Add tests for runtime availability detection * feat: dependency installation for script nodes Implements US-005 from PRD. Changes: - Support deps field for uv nodes: uvx --with dep1... for inline - Support uv run --with dep1... for named uv scripts - Bun deps are auto-installed at runtime via bun's native mechanism - Empty/omitted deps field produces no extra flags - Add tests for dep injection into both runtimes * test: integration tests and validation for script nodes Implements US-006 from PRD. Changes: - Fill test coverage gaps for script node feature - Add script + command mutual exclusivity schema test - Add env var substitution tests ($WORKFLOW_ID, $ARTIFACTS_DIR in scripts) - Add stderr handling test (stderr sent to user as platform message) - Add missing named script file validation tests to validator.test.ts - Full bun run validate passes * fix: address review findings in script nodes - Extract isInlineScript to executor-shared.ts (was duplicated in dag-executor.ts and validator.ts) - Remove dead warnMissingScriptRuntimes from loader.ts (validator already covers runtime checks) - Remove path traversal fallback in executeScriptNode — error when named script not found instead of executing arbitrary file paths - Memoize checkRuntimeAvailable to avoid repeated subprocess spawns - Add min(1) to scriptNodeSchema.script field for consistency - Replace dynamic import with static import in validator.ts * fix(workflows): address review findings for script node implementation Critical fixes: - Wrap discoverScripts() in try-catch inside executeScriptNode to prevent unhandled rejections when script discovery fails (e.g. duplicate names) - Add isScriptNode to isNonAiNode check in loader.ts so AI-specific fields on script nodes emit warnings (activates SCRIPT_NODE_AI_FIELDS) Important fixes: - Surface script stderr in user-facing error messages on non-zero exit - Replace uvx with uv run --with for inline uv scripts with deps - Add z.string().min(1) validation on deps array items - Remove unused ScriptDefinition.content field and readFile I/O - Add logging in discoverAvailableScripts catch block - Warn when deps is specified with bun runtime (silently ignored) Simplifications: - Merge BASH_DEFAULT_TIMEOUT and SCRIPT_DEFAULT_TIMEOUT into single SUBPROCESS_DEFAULT_TIMEOUT constant - Use scriptDef.runtime instead of re-deriving from extname() - Extract shared formatValidationResult helper, deduplicate section comments Tests: - Add isInlineScript unit tests to executor-shared.test.ts - Add named-script-not-found executor test to dag-executor.test.ts - Update deps tests to expect uv instead of uvx Docs: - Add script: node type to CLAUDE.md node types and directory structure - Add script: to .claude/rules/workflows.md DAG Node Types section
…aram conversion The SQLite adapter's convertPlaceholders() regex matches all $N patterns including those in SQL comments. The comment "-- $3 = 1 day" was being counted as a 4th placeholder, causing "expected 3 values, received 4". This blocked all workflow runs (findResumableRun is called on every start). Closes coleam00#999
convertPlaceholders replaces all $N occurrences including inside SQL comments, which caused the param mismatch in findResumableRun. Add tests documenting this known limitation so future SQL additions avoid putting $N in comments. Fixes coleam00#999
…n test Add expect(query).not.toMatch(/--.*\$\d/) to the findResumableRun mocked test to guard the specific query that caused coleam00#999. This prevents a future developer from re-adding a -- \$3 comment to that query and silently breaking SQLite in production.
) * feat: add ScriptNode schema and type guards (US-001) Implements US-001 from the script-nodes PRD. Changes: - Add scriptNodeSchema with script, runtime (bun|uv), deps, and timeout fields - Add ScriptNode type with never fields for mutual exclusivity - Add isScriptNode type guard - Add SCRIPT_NODE_AI_FIELDS constant (same as BASH_NODE_AI_FIELDS) - Update dagNodeSchema superRefine and transform to handle script: nodes - Update DagNode union type to include ScriptNode - Add script node dispatch stub in dag-executor.ts (fails fast until US-003) - Export all new types and values from schemas/index.ts - Add comprehensive schema tests for ScriptNode parsing and validation * feat: script discovery from .archon/scripts/ Implements US-002 from PRD. Changes: - Add ScriptDefinition type and discoverScripts() in script-discovery.ts - Auto-detect runtime from file extension (.ts/.js->bun, .py->uv) - Handle duplicate script name conflicts across extensions - Add bundled defaults infrastructure (empty) for scripts - Add tests for discovery, naming, and runtime detection * feat: script execution engine (inline + named) Implements US-003 from PRD. Changes: - Add executeScriptNode() in dag-executor.ts following executeBashNode pattern - Support inline bun (-e) and uv (run python -c) execution - Support named scripts via bun run / uv run - Wire ScriptNode dispatch replacing 'not yet implemented' stub - Capture stdout as node output, stderr as warning - Handle timeout and non-zero exit - Pass env vars for variable substitution - Add tests for inline/named/timeout/failure cases * feat: runtime availability validation at load time Implements US-004 from PRD. Changes: - Add checkRuntimeAvailable() utility for bun/uv binary detection - Extend validator.ts with script file and runtime validation - Integrate script validation into parseWorkflow flow in loader.ts - Add tests for runtime availability detection * feat: dependency installation for script nodes Implements US-005 from PRD. Changes: - Support deps field for uv nodes: uvx --with dep1... for inline - Support uv run --with dep1... for named uv scripts - Bun deps are auto-installed at runtime via bun's native mechanism - Empty/omitted deps field produces no extra flags - Add tests for dep injection into both runtimes * test: integration tests and validation for script nodes Implements US-006 from PRD. Changes: - Fill test coverage gaps for script node feature - Add script + command mutual exclusivity schema test - Add env var substitution tests ($WORKFLOW_ID, $ARTIFACTS_DIR in scripts) - Add stderr handling test (stderr sent to user as platform message) - Add missing named script file validation tests to validator.test.ts - Full bun run validate passes * fix: address review findings in script nodes - Extract isInlineScript to executor-shared.ts (was duplicated in dag-executor.ts and validator.ts) - Remove dead warnMissingScriptRuntimes from loader.ts (validator already covers runtime checks) - Remove path traversal fallback in executeScriptNode — error when named script not found instead of executing arbitrary file paths - Memoize checkRuntimeAvailable to avoid repeated subprocess spawns - Add min(1) to scriptNodeSchema.script field for consistency - Replace dynamic import with static import in validator.ts * fix(workflows): address review findings for script node implementation Critical fixes: - Wrap discoverScripts() in try-catch inside executeScriptNode to prevent unhandled rejections when script discovery fails (e.g. duplicate names) - Add isScriptNode to isNonAiNode check in loader.ts so AI-specific fields on script nodes emit warnings (activates SCRIPT_NODE_AI_FIELDS) Important fixes: - Surface script stderr in user-facing error messages on non-zero exit - Replace uvx with uv run --with for inline uv scripts with deps - Add z.string().min(1) validation on deps array items - Remove unused ScriptDefinition.content field and readFile I/O - Add logging in discoverAvailableScripts catch block - Warn when deps is specified with bun runtime (silently ignored) Simplifications: - Merge BASH_DEFAULT_TIMEOUT and SCRIPT_DEFAULT_TIMEOUT into single SUBPROCESS_DEFAULT_TIMEOUT constant - Use scriptDef.runtime instead of re-deriving from extname() - Extract shared formatValidationResult helper, deduplicate section comments Tests: - Add isInlineScript unit tests to executor-shared.test.ts - Add named-script-not-found executor test to dag-executor.test.ts - Update deps tests to expect uv instead of uvx Docs: - Add script: node type to CLAUDE.md node types and directory structure - Add script: to .claude/rules/workflows.md DAG Node Types section
Summary
script:node type to the DAG executor supporting inline and named scripts via bun and uv runtimes, with adeps:field for declaring runtime dependencies.UX Journey
Before
After
Architecture Diagram
After
Label Snapshot
risk: lowsize: Mworkflowsworkflows:executor,workflows:schemas,workflows:validatorChange Metadata
featureworkflowsStories Implemented
.archon/scripts/deps:field)Validation Evidence (required)
All 27 new tests pass. No regressions.
Security Impact (required)
Compatibility / Migration
Human Verification (required)
which)Side Effects / Blast Radius (required)
script:nodesRollback Plan (required)
script:node type not recognized in YAML (parse error)Risks and Mitigations
warnMissingScriptRuntimes()with installation instructionsSummary by CodeRabbit
.archon/scripts/directory or inline code blocks.depsfield and set execution timeouts.