Skip to content

feat: script node type for DAG workflows (bun/uv runtimes)#999

Merged
Wirasm merged 8 commits into
devfrom
archon/task-ralph-script-nodes
Apr 9, 2026
Merged

feat: script node type for DAG workflows (bun/uv runtimes)#999
Wirasm merged 8 commits into
devfrom
archon/task-ralph-script-nodes

Conversation

@Wirasm

@Wirasm Wirasm commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Problem: DAG workflows can only run AI nodes (prompt/command) and bash scripts; there's no native TypeScript/Python execution without shell escaping gymnastics.
  • Why it matters: Practitioners need to run TypeScript data-processing scripts or Python ML pipelines as first-class workflow nodes, with proper dependency management and type safety.
  • What changed: Added script: node type to the DAG executor supporting inline and named scripts via bun and uv runtimes, with a deps: field for declaring runtime dependencies.
  • What did not change: All existing node types (command, prompt, bash, loop, approval, cancel) are untouched. No schema breaking changes.

UX Journey

Before

Workflow YAML                Archon Executor
─────────────                ───────────────
nodes:
  - id: fetch-data
    bash: |                  executeBashNode()  ──▶  bash -c '<code>'
      curl ... | python ...  (shell escape hell)

After

Workflow YAML                Archon Executor
─────────────                ───────────────
nodes:
  - id: fetch-data
    script: |                executeScriptNode()  ──▶  bun -e '<code>'
      const r = await fetch(...)                   or  uv run python -c '<code>'
      console.log(r.json())    (no shell escaping)  or  uvx --with httpx python -c '<code>'
    runtime: bun

  - id: scrape
    script: |
      import httpx
      print(httpx.get(...).json())
    runtime: uv
    deps: [httpx]

  - id: named-script
    script: fetch-prices     ──▶  resolves .archon/scripts/fetch-prices.ts
    runtime: bun             ──▶  bun run <path>

Architecture Diagram

After

packages/workflows/src/
  schemas/dag-node.ts    [~] scriptNodeSchema, ScriptNode, isScriptNode, SCRIPT_NODE_AI_FIELDS
  schemas/index.ts       [~] re-exports new types
  script-discovery.ts    [+] discoverScripts(), ScriptDefinition, getDefaultScripts()
  dag-executor.ts        [~] executeScriptNode(), isInlineScript(), SCRIPT_DEFAULT_TIMEOUT
  validator.ts           [~] checkRuntimeAvailable(), script file validation, validateScript()
  loader.ts              [~] warnMissingScriptRuntimes() post-parse async warnings
packages/cli/src/commands/validate.ts  [~] script validation in validate commands

Test files (new):
  script-discovery.test.ts   [+]  14 tests
  runtime-check.test.ts      [+]   6 tests
  script-node-deps.test.ts   [+]   7 tests
From To Status Notes
dag-executor script-discovery new named script lookup
dag-executor @archon/git execFileAsync unchanged reused for script subprocess
validator script-discovery new discoverAvailableScripts()
loader validator new warnMissingScriptRuntimes()
cli/validate validator modified also validates scripts

Label Snapshot

  • Risk: risk: low
  • Size: size: M
  • Scope: workflows
  • Module: workflows:executor, workflows:schemas, workflows:validator

Change Metadata

  • Change type: feature
  • Primary scope: workflows

Stories Implemented

ID Title
US-001 ScriptNode schema and type guards
US-002 Script discovery from .archon/scripts/
US-003 Script execution engine (inline + named)
US-004 Runtime availability validation at load time
US-005 Dependency installation (deps: field)
US-006 Integration tests and validation

Validation Evidence (required)

bun run validate
# type-check: all packages exit 0
# lint: 0 warnings
# format:check: clean
# tests: 0 failures across all packages

All 27 new tests pass. No regressions.

Security Impact (required)

  • New permissions/capabilities? No — script nodes run with same permissions as bash nodes (user's own process)
  • New external network calls? No
  • Secrets/tokens handling changed? No
  • File system access scope changed? No — scripts run in cwd (same as bash nodes)

Compatibility / Migration

  • Backward compatible? Yes — new node type is additive; existing workflows unaffected
  • Config/env changes? No
  • Database migration needed? No

Human Verification (required)

  • Verified scenarios: All 27 tests pass; bun run validate passes
  • Edge cases checked: mutual exclusivity (script+bash, script+prompt, script+command), empty deps, missing runtime binary, missing named script file, timeout, stderr forwarding, $WORKFLOW_ID/$ARTIFACTS_DIR substitution
  • What was not verified: end-to-end with real bun/uv installation on CI (runtime check uses real which)

Side Effects / Blast Radius (required)

  • Affected subsystems: DAG executor, workflow loader, CLI validator
  • Potential unintended effects: None expected — new code paths only activated for script: nodes
  • Guardrails: Runtime availability warnings at load time; missing script file = validation error

Rollback Plan (required)

  • Fast rollback: Revert this PR; no DB changes, no config changes
  • Feature flags: None
  • Observable failure symptoms: script: node type not recognized in YAML (parse error)

Risks and Mitigations

  • Risk: uv/bun not installed on the user's machine
    • Mitigation: Warning at workflow load time via warnMissingScriptRuntimes() with installation instructions

Summary by CodeRabbit

  • New Features
    • Added script node type for DAG workflows: execute TypeScript, Python, or JavaScript via Bun or uv runtimes without AI involvement.
    • Capture script stdout as node output for use in downstream workflow steps.
    • Support named scripts from .archon/scripts/ directory or inline code blocks.
    • Manage script dependencies via deps field and set execution timeouts.

Wirasm added 6 commits April 9, 2026 13:45
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
@coderabbitai

coderabbitai Bot commented Apr 9, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 26c92ca7-9a68-41c8-b5ae-7758047f590b

📥 Commits

Reviewing files that changed from the base of the PR and between ef03cd2 and 0aed6c8.

📒 Files selected for processing (18)
  • .claude/rules/workflows.md
  • CLAUDE.md
  • packages/cli/src/commands/validate.ts
  • packages/workflows/package.json
  • packages/workflows/src/dag-executor.test.ts
  • packages/workflows/src/dag-executor.ts
  • packages/workflows/src/executor-shared.test.ts
  • packages/workflows/src/executor-shared.ts
  • packages/workflows/src/loader.ts
  • packages/workflows/src/runtime-check.test.ts
  • packages/workflows/src/schemas.test.ts
  • packages/workflows/src/schemas/dag-node.ts
  • packages/workflows/src/schemas/index.ts
  • packages/workflows/src/script-discovery.test.ts
  • packages/workflows/src/script-discovery.ts
  • packages/workflows/src/script-node-deps.test.ts
  • packages/workflows/src/validator.test.ts
  • packages/workflows/src/validator.ts

📝 Walkthrough

Walkthrough

A new "script" DAG node type is introduced to the workflow system, enabling direct execution of TypeScript/Python code via bun or uv runtimes without AI involvement. The implementation includes schema validation, runtime discovery, script loading from .archon/scripts/, execution logic with output capture, and comprehensive test coverage across CLI and core workflow modules.

Changes

Cohort / File(s) Summary
Documentation & Configuration
.claude/rules/workflows.md, CLAUDE.md, packages/workflows/package.json
Added documentation and package exports for the new script: node type, including runtime options (bun/uv), dependency installation, and timeouts. Extended test scripts and exports in package.json.
Schema Definitions
packages/workflows/src/schemas/dag-node.ts, packages/workflows/src/schemas/index.ts, packages/workflows/src/schemas.test.ts
Introduced ScriptNode type with script, runtime, optional deps, and timeout fields. Enforced mutual exclusivity between script and other node types. Added type guard isScriptNode and constant SCRIPT_NODE_AI_FIELDS. Updated union type DagNode to include scripts.
Script Discovery & Runtime
packages/workflows/src/script-discovery.ts, packages/workflows/src/script-discovery.test.ts, packages/workflows/src/runtime-check.test.ts, packages/workflows/src/validator.ts
New script discovery module recursively scans .archon/scripts/ for supported file extensions and maps them to runtimes. Added runtime availability checking via which with caching. Extended validator to validate script existence, runtime availability, and emit appropriate warnings and errors.
Execution & Utilities
packages/workflows/src/executor-shared.ts, packages/workflows/src/executor-shared.test.ts, packages/workflows/src/dag-executor.ts, packages/workflows/src/dag-executor.test.ts, packages/workflows/src/script-node-deps.test.ts
Implemented isInlineScript() classifier for distinguishing inline code from named script references. Extended DAG executor with executeScriptNode() to handle inline and named script execution, variable substitution, stdout/stderr capture, error handling, and timeout support. Comprehensive tests cover execution paths, output substitution, error scenarios, and dependency handling.
Validation & CLI
packages/workflows/src/validator.test.ts, packages/cli/src/commands/validate.ts
Extended validation command to discover and validate scripts, producing separate results for commands and scripts with unified error counting. Updated JSON and human-readable output formatting to include script validation results.
Loader
packages/workflows/src/loader.ts
Updated DAG node parser to recognize script nodes and apply conditional AI-field stripping based on node type, treating scripts similarly to bash nodes in field filtering.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested reviewers

  • leex279

Poem

🐰 A script node hops into the DAG,
No AI in sight, just runtime to brag,
Bun and uv run the code so fine,
Stdout captured, variables align!
Workflows now dance with scripts in tow,
Watch them execute, steal the show!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch archon/task-ralph-script-nodes

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- 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
@Wirasm

Wirasm commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator Author

PR Review Summary — Multi-Agent Analysis

Six specialized agents reviewed this PR. Here are the aggregated findings.


Critical Issues (2 found)

Agent Issue Location
silent-failure-hunter discoverScripts() is called outside the try-catch in executeScriptNode() — a duplicate script name (realistic user error) causes an unhandled rejection that bypasses all structured failure handling (no node_failed event, no user message, no log) dag-executor.ts:1541
code-reviewer isNonAiNode check in loader.ts was not updated to include isScriptNode() — users who set model:, provider:, hooks:, etc. on a script node get no warning, and SCRIPT_NODE_AI_FIELDS is exported but never consumed (dead code) loader.ts:58-63

Important Issues (6 found)

Agent Issue Location
silent-failure-hunter Script runtime failure hides stderr from the user-facing error message — user sees only Command failed: bun -e ... instead of the actual traceback/error dag-executor.ts:1611-1655
silent-failure-hunter discoverAvailableScripts() in validator swallows all errors silently — returns [] with no logging, making permission errors indistinguishable from "no scripts found" validator.ts:528-540
code-reviewer, silent-failure-hunter Validator checks uv availability but executor uses uvx when deps are present — workflow passes validation then fails at runtime with ENOENT. Fix: use uv run --with consistently instead of uvx validator.ts:425, dag-executor.ts:1529
code-reviewer ScriptDefinition.content reads every script file into memory during discovery but is never consumed by the executor — YAGNI violation, unnecessary I/O script-discovery.ts:100-111
code-reviewer deps array items have no min-length validation — empty strings pass schema and produce confusing subprocess errors. Should use z.string().min(1) consistent with skills field dag-node.ts:202,354
type-analyzer deps on bun runtime is silently ignored with no validator warning — users expecting dependency installation get no feedback dag-executor.ts:1519-1522

Suggestions (8 found)

Agent Suggestion Location
test-analyzer isInlineScript has no unit tests despite being the single routing decision for all script dispatch (pure function, easy to test) executor-shared.ts:411
test-analyzer Named-script-not-found runtime path is untested in the executor (only tested via static validator) dag-executor.ts:1558-1561
test-analyzer retry field is silently accepted on script nodes but never executed — should either reject in schema or document the no-op dag-executor.ts:2695
code-simplifier BASH_DEFAULT_TIMEOUT and SCRIPT_DEFAULT_TIMEOUT are identical (120_000) — merge into SUBPROCESS_DEFAULT_TIMEOUT dag-executor.ts:1298,1451
code-simplifier Named-script branch re-derives runtime from extname() when scriptDef.runtime already provides this — use the existing field dag-executor.ts (named branch)
code-simplifier formatScriptResult is structurally identical to formatWorkflowResult — extract shared helper validate.ts:188-198
code-simplifier Extension-to-runtime mapping is duplicated in 3 places (EXTENSION_RUNTIME_MAP, validator allExtensions, validator extensions) — use discoverScripts as single source validator.ts, dag-executor.ts
silent-failure-hunter checkRuntimeAvailable uses which — not portable to Windows (low priority, document assumption) validator.ts:222-231

Documentation Issues

File Issue
CLAUDE.md line 674 nodes: DAG format description does not list script: node type
CLAUDE.md lines 539-543 Repo-level .archon/ directory tree missing .archon/scripts/
.claude/rules/workflows.md "DAG Node Types" section missing script: bullet; "DAG node options" missing deps: and runtime:

Strengths

  • Solid mutual exclusivity enforcement via never-branded fields + superRefine — consistent with all existing node types
  • execFileAsync used correctly for subprocess execution — no shell injection risk (code passed as argv, not interpolated)
  • Good test coverage overall: 27+ new tests covering schema validation, script discovery, runtime checks, dependency command construction, and integration execution
  • Test isolation done correctly — new test files get separate bun test invocations to avoid mock.module() pollution
  • Clean separation: script-discovery.ts owns extension-to-runtime mapping, executor handles dispatch, validator handles pre-flight checks
  • SCRIPT_NODE_AI_FIELDS correctly aliases BASH_NODE_AI_FIELDS rather than duplicating

Type Design Scores

Dimension Score
Encapsulation 7/10
Invariant Expression 7/10
Invariant Usefulness 8/10
Invariant Enforcement 6/10

Verdict: NEEDS FIXES

Before merge, fix:

  1. Wrap discoverScripts() call in executeScriptNode inside try-catch for structured failure handling
  2. Add isScriptNode() to isNonAiNode check in loader.ts (activates the already-exported SCRIPT_NODE_AI_FIELDS)
  3. Surface script stderr in the user-facing error message on non-zero exit
  4. Use uv run --with instead of uvx (or validate uvx availability separately)

Should fix:
5. Add z.string().min(1) validation on deps array items
6. Remove unused ScriptDefinition.content field and its readFile call
7. Add logging in discoverAvailableScripts catch block
8. Update CLAUDE.md and .claude/rules/workflows.md with script: node documentation

Consider:
9. Add isInlineScript unit tests
10. Merge duplicate timeout constants
11. Use scriptDef.runtime instead of re-deriving from extname()

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
@Wirasm Wirasm marked this pull request as ready for review April 9, 2026 11:48
@Wirasm Wirasm merged commit 50f96f8 into dev Apr 9, 2026
3 of 4 checks passed
@Wirasm Wirasm deleted the archon/task-ralph-script-nodes branch April 9, 2026 11:51
GQAdonis pushed a commit to GQAdonis/Archon that referenced this pull request Apr 9, 2026
…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
GQAdonis pushed a commit to GQAdonis/Archon that referenced this pull request Apr 9, 2026
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
GQAdonis pushed a commit to GQAdonis/Archon that referenced this pull request Apr 9, 2026
…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.
@Wirasm Wirasm mentioned this pull request Apr 10, 2026
Tyone88 pushed a commit to Tyone88/Archon that referenced this pull request Apr 16, 2026
…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
Tyone88 pushed a commit to Tyone88/Archon that referenced this pull request Apr 16, 2026
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
Tyone88 pushed a commit to Tyone88/Archon that referenced this pull request Apr 16, 2026
…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.
Tyone88 pushed a commit to Tyone88/Archon that referenced this pull request Apr 16, 2026
)

* 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
joaobmonteiro pushed a commit to joaobmonteiro/Archon that referenced this pull request Apr 26, 2026
…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
joaobmonteiro pushed a commit to joaobmonteiro/Archon that referenced this pull request Apr 26, 2026
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
joaobmonteiro pushed a commit to joaobmonteiro/Archon that referenced this pull request Apr 26, 2026
…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.
joaobmonteiro pushed a commit to joaobmonteiro/Archon that referenced this pull request Apr 26, 2026
)

* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant