* fix(workflows): add word boundary to context variable substitution regex (#1112)
Variable substitution for $CONTEXT, $EXTERNAL_CONTEXT, and $ISSUE_CONTEXT
was matching as a prefix of longer identifiers like $CONTEXT_FILE, silently
corrupting bash node scripts. Added negative lookahead (?![A-Za-z0-9_]) to
CONTEXT_VAR_PATTERN_STR so only exact variable names are substituted.
Changes:
- Add negative lookahead to CONTEXT_VAR_PATTERN_STR regex in executor-shared.ts
- Add regression test for prefix-match boundary case
Fixes#1112
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(workflows): add missing boundary cases for context variable substitution
Add three new test cases that complete coverage of the word-boundary fix
from #1112: $ISSUE_CONTEXT with suffix variants, $ISSUE_CONTEXT with multiple
suffixes, and contextSubstituted=false for suffix-only prompts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 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
- Fix YAML injection in setup.ts: quote docs path value to prevent
malformed config from special characters
- Add try/catch around file I/O in setup.ts (matches skill-install pattern)
- Add user feedback when docs: key already exists in config
- Add defensive fallback in substituteWorkflowVariables (docsDir || 'docs/')
- Add warn log for whitespace-only docs.path in config-loader
- Add $DOCS_DIR to CLAUDE.md variable list and config example
- Add $DOCS_DIR to .claude/rules/workflows.md variable table
- Add $DOCS_DIR to all docs-web variable tables (6 pages)
- Add config-loader tests for docsPath (propagation, trim, undefined)
- Add executor tests for docsPath default resolution
- Add executor-shared test for empty-string docsDir fallback
Projects with docs outside `docs/` (e.g., `packages/docs-web/src/content/docs/`)
get broken bundled commands because the path is hardcoded. Add `docs.path` to
`.archon/config.yaml` and thread it through the workflow engine as `$DOCS_DIR`
(default: `docs/`), following the same pipeline as `$BASE_BRANCH`.
Changes:
- Add `docs.path` to RepoConfig and `docsPath` to MergedConfig/WorkflowConfig
- Thread `docsDir` through executor-shared, executor, and dag-executor
- Update bundled commands to use `$DOCS_DIR` instead of hardcoded `docs/`
- Add optional docs path prompt to `archon setup`
- Add variable reference and configuration documentation
- Resolve pre-existing merge conflicts in server/api.ts
Fixes#982
Remove @returns tag from detectCreditExhaustion — the return type and
behavior are already clear from the signature and body. Fix the
"credit balance" test case which used an input that matched both
"credit balance" and "insufficient credit" patterns; use an input that
matches only the intended pattern.
The `startsWith('error_')` check would misclassify any SDK error whose
subtype begins with `error_` (e.g. `error_max_turns`, `error_max_tokens`)
as credit exhaustion, giving users misleading "wait for credits" guidance.
The SDK returns credit exhaustion as assistant text anyway, so
`detectCreditExhaustion(nodeOutputText)` is the correct and complete
detection path. Remove the speculative SDK flag path and its associated
local variables.
Also adds an independent test for the 'insufficient credit' pattern in
CREDIT_EXHAUSTION_OUTPUT_PATTERNS, which was previously only incidentally
covered by the 'credit balance' test string.
When Claude credits run out mid-workflow, the SDK returns the error as
normal assistant text rather than throwing. The DAG executor was marking
these nodes as completed with garbage output, preventing resume from
re-running them.
Changes:
- Add isError/errorSubtype to MessageChunk and WorkflowMessageChunk result types
- Propagate is_error/subtype from Claude SDK result message
- Add detectCreditExhaustion() to executor-shared for text pattern matching
- Gate node_completed in dag-executor with credit exhaustion check
- Mark credit-exhausted nodes as failed so resume re-runs them
Fixes#940
* refactor(workflows)!: remove sequential execution mode, DAG becomes sole format
Remove the steps-based (sequential) workflow execution mode entirely.
All workflows now use the nodes-based (DAG) format exclusively.
- Convert 8 sequential default workflows to DAG format
- Delete archon-fix-github-issue sequential (DAG version absorbs triggers)
- Remove SingleStep, ParallelBlock, StepWorkflow types and guards
- Gut executor.ts from ~2200 to ~730 lines (remove sequential loop)
- Remove step_started/completed/failed and parallel_agent_* events
- Remove logStepStart/Complete and logParallelBlockStart/Complete
- Delete SequentialEditor, StepProgress, ParallelBlockView components
- Remove sequential mode from workflow builder and execution views
- Delete executor.test.ts (4395 lines), update ~45 test fixtures
- Update CLAUDE.md and docs to reflect DAG-only format
BREAKING CHANGE: Workflows using `steps:` format are no longer supported.
Convert to `nodes:` (DAG) format. The loader provides a clear error message
directing users to the migration guide.
* fix: address review findings — guard errors, remove dead code, add tests
- Guard logNodeSkip/logWorkflowError against filesystem errors in dag-executor
- Move mkdir(artifactsDir) inside try-catch with user-friendly error
- Remove startFromStep dead parameter from executeWorkflow signature
- Remove isDagWorkflow() tautology and all callers (20+ sites)
- Remove dead BuilderMode/mode state from frontend components
- Remove vestigial isLoop, selectedStep, stepIndex, step_index fields
- Remove "DAG" prefix from user-facing resume/error messages
- Fix 5 stale docs (README, getting-started, authoring-commands, web adapter)
- Update event-emitter tests to use node events instead of removed step events
- Add executor-shared.test.ts (12 tests) for substituteWorkflowVariables
- Add executor.test.ts (11 tests) for concurrent-run, model resolution, resume
* fix(workflows): add migration guide, port preamble tests, improve error message
- Add docs/sequential-dag-migration-guide.md with 3 conversion patterns
(single step, chain with clearContext, parallel block) and a Claude Code
migration command for automated conversion
- Update loader error message to point to migration guide and include
ready-to-run claude command
- Port 8 preamble tests from deleted executor.test.ts to new
executor-preamble.test.ts: staleness detection (3), concurrent-run
guard (3), DAG resume (2)
Addresses review feedback from #805.
* fix(workflows): update loader test to match new error message wording
* fix: address review findings — fail stuck runs, remove dead code, fix docs
- Mark workflow run as failed when artifacts mkdir fails (prevents
15-min concurrent-run guard block)
- Remove vestigial totalSteps from WorkflowStartedEvent and executor
- Delete dead WorkflowToolbar.tsx (369 lines, no importers)
- Remove stepIndex prop from StepLogs (always 0, label now "Node logs")
- Restore cn() in StatusBar for consistent conditional classes
- Promote resume-check log to error, add errorType to failure logs
- Remove ghost $PLAN/$IMPLEMENTATION_SUMMARY from docs (never implemented)
- Update workflows.md rules to DAG-only format
- Fix migration guide trigger_rule example
- Clean up blank-line residues and stale comments
* fix: resolve rebase conflicts with #729 (forkSession) and #730 (dashboard)
- Remove sequential forkSession/persistSession code from #729 (dead after
sequential removal)
- Fix loader type narrowing for DagNode context field
- Update dashboard components from #730 to use dagNodes instead of steps
- Remove WorkflowStepEvent/ParallelAgentEvent from dashboard SSE hook