chore(openspec): drop 9 superseded proposals + 11 stub archive files

Drop 9 batch proposals that are superseded by the boocode-lift-analysis
(boocontext-audit, conductor upgrades, self-healing/verify-gate skills):
add-3tier-memory, import-llm-evaluator, import-pregel-engine, plugin-platform,
conductor-evolution, code-intelligence-upgrade, dev-workflow, ui-overhaul,
agent-reliability.

Delete 11 stub archive files (49-66B each, 'Status: Shipped. Archived.' only)
that provide zero documentation value over the existing CHANGELOG.md + git tags.
This commit is contained in:
2026-06-07 22:15:38 +00:00
parent 0d6e9a2413
commit c935687725
119 changed files with 4897 additions and 45 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-07

View File

@@ -0,0 +1,62 @@
## Context
Three workflow engine patterns were researched: **Archon** (DAG-based YAML, git isolation), **Agent SOP** (markdown instructions with RFC 2119 constraints), and **Vercel Workflow** (event-sourced durable execution). Each excels in one dimension but has fundamental gaps:
- **Archon**: Clean DAG format + variable substitution + approval gates, but no crash recovery, tightly coupled to its monorepo (Bun/SQLite/Claude SDK)
- **Agent SOP**: Zero parser complexity, AI-native markdown, but completely stateless — no execution engine, no validation, no persistence
- **Vercel Workflow**: Gold-standard durability via event sourcing, but requires Rust SWC plugin, VM sandbox, 24-36 week rebuild — extreme complexity for the value in most use cases
**Ion** extracts the portable essence of each: Archon's DAG schema and executor, Agent SOP's markdown readability, Vercel's event sourcing (simplified — no SWC, no VM, no compile transforms).
## Goals / Non-Goals
**Goals:**
- Fully portable DAG execution engine in pure TypeScript (zero Rust/SWC/wasm)
- YAML-first workflow definitions with 7 node types (command, prompt, bash, script, loop, approval, cancel)
- `.sop.md` markdown format as a secondary input (transpiled to DAG nodes)
- Event-sourced persistence for crash recovery with deterministic replay — simplified to "log of node outcomes" rather than "log of every async operation"
- Plugable storage backends: filesystem (dev), SQLite/Postgres (production)
- CLI tool + library API dual distribution
- Approval gates with capture_response and on_reject
- Variable substitution ($nodeId.output, $ARGUMENTS, $LOOP_PREV_OUTPUT, etc.)
- Script execution via bun/node (TS) and uv/python3 (Python) with deps support
**Non-Goals:**
- No SWC compiler plugin or build-time transforms (Vercel's approach is overkill for this scope)
- No VM sandbox for workflow execution (workflows run as regular async functions)
- No git worktree isolation (leave to the host application)
- No multi-tenant or serverless platform (single-tenant CLI/library focus)
- No web UI in the initial build (CLI + library only; web can be added later)
- No AI provider integration (host application provides the AI; Ion just routes prompts)
## Decisions
### Decision 1: Event Log = Node Outcomes, Not Every Async Operation
**Vercel** logs every `step_created`, `step_completed`, `wait_created`, `hook_received` etc. — 17 event types. This requires SWC transforms to intercept all async boundaries.
**Ion** logs only *node-level* events: `node_started`, `node_completed`, `node_failed`, `workflow_started`, `workflow_completed`, `workflow_failed`. No micro-events. Replay means "re-run the DAG from the top, skipping completed nodes using stored outputs" — identical to Archon's `resume` approach.
**Rationale**: Simpler by an order of magnitude. No interceptors, no transforms, no VM. Crash recovery works: if the process dies mid-workflow, replay skips completed nodes and re-executes from the last failed/incomplete layer.
### Decision 2: Pure TypeScript — No Rust, No SWC, No WASM
All three engines studied: Archon (pure TS), Vercel (Rust SWC plugin), Agent SOP (pure Python). The SWC plugin is the single biggest contributor to Vercel's 24-36 week build time.
**Ion** stays pure TS. The DAG executor, YAML loader, variable substitution, event log — all standard async/await. No build step beyond `tsc` or `bun build`.
### Decision 3: YAML Primary, Markdown Secondary
**Archon's YAML** format is the primary definition: structured, validated by Zod, machine-parseable. **Agent SOP's markdown** is the secondary format: human-writable, conversational, auto-converted.
The transpiler is simple: parse `## Parameters` → extract required fields, parse `## Steps` → convert each step to a `prompt:` node with constraints embedded in the prompt text. No AST-level parsing needed.
### Decision 4: Storage via IWorkflowStore Interface
**Archon's pattern**: `IWorkflowStore` interface with `createWorkflowRun`, `getWorkflowRun`, `updateWorkflowRun`, `failWorkflowRun`, `createWorkflowEvent`, `getCompletedDagNodeOutputs`. Adapters implement the interface.
**Ion** copies this pattern exactly. FilesystemStore (JSON files per run), SqliteStore, PostgresStore. The interface is the seam.
### Decision 5: CLI + Library, Not Server
**Archon** has a server + web UI. **Vercel** is a platform SDK. **Ion** ships only as a CLI + library.
The CLI wraps the library: `ion run <workflow>`, `ion list`, `ion approve`, `ion reject`, `ion resume`. The library exports `executeWorkflow()`, `createStore()`, `parseWorkflow()`, `discoverWorkflows()`.
## Risks / Trade-offs
| Risk | Mitigation |
|---|---|
| **Event-sourcing is simplified to node-level only** — means no intra-node recovery (if a 30-min AI prompt crashes at 29 min, it restarts from scratch). | Acceptable tradeoff. AI prompts are idempotent. For script/bash nodes, provide `timeout` and `retry` config. Node-level replay is 90% of the value at 10% of the complexity. |
| **No VM sandbox** — workflows run as regular async functions, so `while(true){}` hangs the process. | Document that workflow code must be well-behaved. The `idle_timeout` per node provides a circuit breaker. Production deployments can run workflows in a separate child process. |
| **Markdown-to-YAML transpiler** may lose nuance — SOP's RFC 2119 constraints are prose, not structured. | Constraints stay embedded in the prompt text of the resulting `prompt:` node. The transpiler extracts Parameters (→ node metadata) and Steps (→ prompt body). Lossless for the critical path. |
| **Competing with existing engines** — Archon exists, Temporal exists, Inngest exists. | Ion targets a different niche: portable CLI-first engine that fits in a single repo. Not a platform, not a cloud service. |

View File

@@ -0,0 +1,41 @@
## Why
Current workflow engines force a tradeoff between simplicity and durability. Archon has a clean DAG-based YAML format but no crash recovery. Vercel Workflow has bulletproof deterministic replay but requires a Rust compiler plugin and 24-36 weeks to build. Agent SOP proves that human-readable markdown workflows work, but lacks structured execution. There is no portable workflow engine that combines a simple DAG format, human-readable definitions, and durable event-sourced execution in a single, buildable package.
## What Changes
Introduce **Ion** — a portable hybrid workflow engine that combines the three approaches:
- **Archon-style YAML DAG format** with `nodes:`, `depends_on:`, and trigger rules as the primary workflow definition
- **Agent SOP-style `.sop.md` markdown** as a secondary human-readable format, auto-converted to the DAG representation
- **Vercel-style event log** for deterministic replay and crash recovery, but simplified — no SWC plugin, no VM sandbox, no compile-time transforms
- **Multi-backend storage** (filesystem for dev, SQLite/Postgres for production)
- **CLI + library** dual distribution: use as a CLI tool or embed as a library
- **No Rust compiler plugins, no SWC, no VM sandbox** — pure TypeScript/JavaScript, zero compile-time transforms
## Capabilities
### New Capabilities
- `dag-engine`: DAG execution engine with topological ordering, concurrent layers, trigger rules (`all_success`, `one_success`, `all_done`, `none_failed_min_one_success`), and `when:` condition evaluation
- `yaml-format`: Workflow definition in YAML with 7 node types (command, prompt, bash, script, loop, approval, cancel) plus `depends_on`, `trigger_rule`, `output_format`, `retry`, `timeout`
- `markdown-format`: `.sop.md` human-readable workflow format with RFC 2119 constraint keywords, auto-converted to DAG nodes
- `event-sourcing`: Append-only event log for workflow runs with deterministic replay for crash recovery — simplified (no SWC, no VM sandbox)
- `variable-substitution`: `$nodeId.output`, `$nodeId.output.field`, `$ARGUMENTS`, `$ARTIFACTS_DIR`, `$WORKFLOW_ID`, `$LOOP_PREV_OUTPUT`, `$REJECTION_REASON` with strict field access
- `script-execution`: Script node type running TypeScript (bun/node) and Python (uv/python3) with `deps:` support and `timeout:`
- `human-approval`: Approval gate nodes that pause execution for human review with `capture_response` and `on_reject` retry support
- `storage-backends`: Pluggable storage — filesystem (dev), SQLite, Postgres — with IWorkflowStore interface
- `workflow-lifecycle`: Run states `pending → running → paused/completed/failed/cancelled`, resume skipping completed nodes, event-driven observability
- `cli-tool`: Command-line interface for listing, running, approving, rejecting, resuming, and cleaning up workflow runs
- `library-api`: Programmatic API for embedding the engine in other applications
### Modified Capabilities
<!-- No existing specs to modify — this is a greenfield change. -->
## Impact
- **Greenfield project** — no existing code to modify, all new artifacts under `ion/` or equivalent package path
- **Dependencies**: Zod (schema validation), nanoid/ulid (ID generation), js-yaml (YAML parsing), chokidar (file watching for dev mode)
- **Optional dependencies**: better-sqlite3 / postgres.js (production storage backends), bun (fast script runtime), highlight.js (markdown rendering)
- **No** Rust, SWC, wasm, or compile-time transforms in the core engine

View File

@@ -0,0 +1,76 @@
## ADDED Requirements
### Requirement: Workflow listing
The CLI SHALL provide a `list` command that displays all discovered workflows and their descriptions.
#### Scenario: List workflows
- **WHEN** `ion list` is run
- **THEN** all discovered workflows SHALL be listed with name, description, and source (bundled/project)
### Requirement: Workflow execution
The CLI SHALL provide a `run` command that executes a workflow by name with optional arguments.
#### Scenario: Run workflow with message
- **WHEN** `ion run analyze "analyze the codebase"` is run
- **THEN** the `analyze` workflow SHALL execute with the provided user message
#### Scenario: Run in specific directory
- **WHEN** `ion run build --cwd /path/to/project` is run
- **THEN** the workflow SHALL use the specified working directory
#### Scenario: Run with specific store
- **WHEN** `ion run deploy --store sqlite --db-path ./ion.db` is run
- **THEN** the specified store backend SHALL be used
### Requirement: Workflow approval commands
The CLI SHALL provide `approve` and `reject` commands for responding to approval gates.
#### Scenario: Approve a paused workflow
- **WHEN** `ion approve <run-id>` is run
- **THEN** the workflow SHALL resume from the paused approval node
#### Scenario: Approve with comment
- **WHEN** `ion approve <run-id> "looks good"` is run
- **THEN** the comment SHALL be recorded and available as `$nodeId.output`
#### Scenario: Reject with reason
- **WHEN** `ion reject <run-id> "needs changes"` is run
- **THEN** `$REJECTION_REASON` SHALL be set to "needs changes"
- **THEN** if `on_reject` is configured, the handler SHALL execute
### Requirement: Workflow run management
The CLI SHALL provide `status`, `runs`, `resume`, `abandon`, and `cleanup` commands.
#### Scenario: Show running workflows
- **WHEN** `ion status` is run
- **THEN** all active (running + paused) workflow runs SHALL be displayed
#### Scenario: List recent runs
- **WHEN** `ion runs` is run
- **THEN** recent workflow runs SHALL be listed with status and timestamps
#### Scenario: Resume failed run
- **WHEN** `ion resume <run-id>` is run
- **THEN** the failed run SHALL be resumed, skipping completed nodes
#### Scenario: Abandon run
- **WHEN** `ion abandon <run-id>` is run
- **THEN** the run SHALL be marked as cancelled
#### Scenario: Cleanup old runs
- **WHEN** `ion cleanup` is run (default 7 days)
- **THEN** runs older than the retention period SHALL have their artifacts removed
### Requirement: SOP-to-YAML conversion
The CLI SHALL provide a `convert` command to transpile `.sop.md` files to `.yaml`.
#### Scenario: Convert SOP to YAML
- **WHEN** `ion convert workflow.sop.md` is run
- **THEN** a `workflow.yaml` SHALL be written with the equivalent DAG representation
### Requirement: Machine-readable output
Workflow commands SHALL support `--json` flag for machine-readable output.
#### Scenario: JSON output for automation
- **WHEN** `ion list --json` is run
- **THEN** output SHALL be valid JSON array of workflow objects

View File

@@ -0,0 +1,54 @@
## ADDED Requirements
### Requirement: DAG topological execution
The engine SHALL execute workflow nodes in topological order determined by `depends_on` edges using Kahn's algorithm.
#### Scenario: Independent nodes run concurrently
- **WHEN** a workflow has nodes A and B with no `depends_on`
- **THEN** both A and B SHALL execute in the same topological layer, concurrently
#### Scenario: Dependent nodes run sequentially
- **WHEN** node B lists `depends_on: [A]`
- **THEN** A SHALL complete before B begins
#### Scenario: Cycle detection
- **WHEN** nodes form a cycle (A → B → C → A)
- **THEN** the loader SHALL reject the workflow with a cycle detection error
### Requirement: Trigger rules
The engine SHALL support 4 trigger rules for join semantics.
#### Scenario: all_success (default)
- **WHEN** a node has multiple upstream dependencies and no explicit `trigger_rule`
- **THEN** it SHALL only run if ALL upstream nodes completed successfully
- **THEN** it SHALL be skipped if any upstream node failed
#### Scenario: one_success
- **WHEN** a node sets `trigger_rule: one_success`
- **THEN** it SHALL run if at least one upstream node completed successfully
#### Scenario: all_done
- **WHEN** a node sets `trigger_rule: all_done`
- **THEN** it SHALL run when all upstream nodes have finished (any status), regardless of success/failure
#### Scenario: none_failed_min_one_success
- **WHEN** a node sets `trigger_rule: none_failed_min_one_success`
- **THEN** it SHALL run only if no upstream node failed AND at least one succeeded
### Requirement: when conditions
Nodes SHALL support a `when:` string that evaluates to a boolean condition.
#### Scenario: when condition prevents execution
- **WHEN** a node has `when: "false"` or any expression that evaluates falsy
- **THEN** the node SHALL be skipped as if its trigger_rule prevented execution
### Requirement: Node retry with configurable policy
Nodes SHALL support a `retry` config with `max_attempts`, `delay_ms`, and `on_error` (transient|all).
#### Scenario: retry on transient error
- **WHEN** a node with `retry: { max_attempts: 3 }` fails with a transient error
- **THEN** it SHALL retry up to 3 times with configured delay between attempts
#### Scenario: retry exhausted
- **WHEN** all retry attempts fail
- **THEN** the node SHALL be marked as failed and trigger_rule evaluation proceeds

View File

@@ -0,0 +1,59 @@
## ADDED Requirements
### Requirement: Append-only event log
Workflow runs SHALL produce append-only event records. Events SHALL NOT be modified after creation.
#### Scenario: Events are chronological
- **WHEN** a workflow executes
- **THEN** events SHALL be stored with monotonically increasing timestamps or sequence numbers
- **THEN** event order SHALL match execution order
#### Scenario: Events are immutable
- **WHEN** an event has been persisted
- **THEN** it SHALL NOT be updated or deleted
### Requirement: Event types
The event log SHALL support exactly 8 event types: `workflow_started`, `workflow_completed`, `workflow_failed`, `workflow_cancelled`, `node_started`, `node_completed`, `node_failed`, `node_skipped`.
#### Scenario: Workflow lifecycle events
- **WHEN** a workflow run begins
- **THEN** a `workflow_started` event SHALL be recorded
- **WHEN** a workflow run completes successfully
- **THEN** a `workflow_completed` event SHALL be recorded
- **WHEN** a workflow run fails
- **THEN** a `workflow_failed` event SHALL be recorded
#### Scenario: Node lifecycle events
- **WHEN** a node begins execution
- **THEN** a `node_started` event SHALL be recorded
- **WHEN** a node completes successfully
- **THEN** a `node_completed` event SHALL record the node's output
- **WHEN** a node fails
- **THEN** a `node_failed` event SHALL record the error
- **WHEN** a node is skipped (trigger_rule not met)
- **THEN** a `node_skipped` event SHALL be recorded
### Requirement: Deterministic replay for crash recovery
When a workflow run is resumed after an interruption, the engine SHALL load completed node outputs from the event log and skip re-execution of completed nodes.
#### Scenario: Resume skips completed nodes
- **WHEN** a workflow run is resumed after a crash
- **THEN** all nodes with a `node_completed` event SHALL be skipped
- **THEN** execution SHALL begin from the first node without a completed event
#### Scenario: Resume after partial execution
- **WHEN** a workflow had 5 nodes and the first 3 completed before the crash
- **THEN** nodes 1-3 SHALL be skipped (outputs loaded from event log)
- **THEN** node 4 SHALL be re-executed
### Requirement: Event storage via plugable backend
Events SHALL be persisted through the `IWorkflowStore` interface, with at least a filesystem backend.
#### Scenario: Filesystem event store
- **WHEN** using the filesystem backend
- **THEN** each run SHALL have a JSON file at `{runId}/events.jsonl`
- **THEN** events SHALL be appended as newline-delimited JSON
#### Scenario: SQLite event store
- **WHEN** using the SQLite backend
- **THEN** events SHALL be stored in a `workflow_events` table with columns for run_id, sequence, event_type, timestamp, and payload

View File

@@ -0,0 +1,37 @@
## ADDED Requirements
### Requirement: Approval gate pauses execution
An ApprovalNode SHALL pause workflow execution and send a message for human review. Execution SHALL only continue when the user approves or rejects.
#### Scenario: Approval pauses workflow
- **WHEN** an approval node executes
- **THEN** the workflow status SHALL transition to `paused`
- **THEN** a message SHALL be sent with the approval message text
#### Scenario: Approve resumes execution
- **WHEN** the user approves a paused workflow
- **THEN** the workflow SHALL resume with the next node in the DAG
#### Scenario: Reject fails the node
- **WHEN** the user rejects a paused workflow
- **THEN** the node SHALL be marked as failed
- **THEN** downstream nodes SHALL evaluate their trigger rules
### Requirement: Capture response from approval
An approval node MAY support `capture_response: true` to store the user's comment as `$nodeId.output`.
#### Scenario: Approval with captured response
- **WHEN** an approval node has `capture_response: true` and the user provides a comment during approval
- **THEN** the comment SHALL be stored as the node's output, available via `$nodeId.output`
### Requirement: On-reject retry
An approval node MAY specify `on_reject` with a `prompt` and optional `max_attempts` for re-presenting after rejection.
#### Scenario: Reject with retry prompt
- **WHEN** an approval node has `on_reject: { prompt: "..." }` and the user rejects
- **THEN** the on_reject prompt SHALL be executed (typically the AI revises based on feedback)
- **THEN** the approval gate SHALL be re-presented to the user
#### Scenario: Max attempts exceeded
- **WHEN** the number of rejections exceeds `on_reject.max_attempts`
- **THEN** the node SHALL fail permanently

View File

@@ -0,0 +1,44 @@
## ADDED Requirements
### Requirement: Programmatic execution
The engine SHALL export an `executeWorkflow()` function that accepts a workflow definition, store, and options.
#### Scenario: Execute workflow from code
- **WHEN** a host application calls `executeWorkflow(workflowDef, store, { userMessage: "..." })`
- **THEN** the workflow SHALL execute and return a `WorkflowExecutionResult`
### Requirement: Workflow parsing
The engine SHALL export `parseWorkflow(yaml: string): WorkflowDefinition` and `parseWorkflowFile(path: string): WorkflowDefinition` functions.
#### Scenario: Parse YAML string
- **WHEN** a host application calls `parseWorkflow(yamlString)`
- **THEN** it SHALL return a validated `WorkflowDefinition`
#### Scenario: Parse YAML file
- **WHEN** a host application calls `parseWorkflowFile("./workflows/my-workflow.yaml")`
- **THEN** it SHALL read and parse the file, returning a validated `WorkflowDefinition`
### Requirement: Workflow discovery
The engine SHALL export `discoverWorkflows(cwd: string): WorkflowLoadResult` for finding workflows in the filesystem.
#### Scenario: Discover workflows
- **WHEN** a host application calls `discoverWorkflows(cwd)`
- **THEN** it SHALL return all discovered workflows from the project's `.archon/workflows/` directory
### Requirement: Store constructors
The engine SHALL export store constructors for each backend: `createFsStore(path)`, `createSqliteStore(path)`, `createPostgresStore(connectionString)`.
#### Scenario: Create filesystem store
- **WHEN** a host application calls `createFsStore("./data")`
- **THEN** it SHALL return an initialized `IWorkflowStore` using the filesystem backend
#### Scenario: Create SQLite store
- **WHEN** a host application calls `createSqliteStore("./ion.db")`
- **THEN** it SHALL return an initialized `IWorkflowStore` using SQLite
### Requirement: TypeScript types
All public APIs SHALL export full TypeScript type definitions.
#### Scenario: Types available
- **WHEN** a host application imports from the package
- **THEN** `WorkflowDefinition`, `DagNode`, `NodeOutput`, `WorkflowRun`, `WorkflowExecutionResult`, `IWorkflowStore` types SHALL all be exported

View File

@@ -0,0 +1,34 @@
## ADDED Requirements
### Requirement: SOP markdown as secondary format
Workflows MAY be defined as `.sop.md` files in addition to YAML. The engine SHALL detect `.sop.md` files during discovery and transpile them to the DAG node representation.
#### Scenario: SOP file discovered alongside YAML
- **WHEN** a `.sop.md` file exists in the workflows directory alongside `.yaml` workflow files
- **THEN** both SHALL be discovered and listed as available workflows
#### Scenario: SOP transpiled to prompt nodes
- **WHEN** a `.sop.md` file is loaded
- **THEN** each `## Steps` section item SHALL become a `prompt:` node
- **THEN** `## Parameters` SHALL be extracted as node metadata
### Requirement: RFC 2119 constraint extraction
The transpiler SHALL extract RFC 2119 constraints from `**Constraints:**` blocks and embed them in the prompt text of the corresponding node.
#### Scenario: Constraints included in prompt
- **WHEN** a step has `**Constraints:** - You MUST do X`
- **THEN** the constraint text SHALL be appended to the node's prompt
### Requirement: Overview as workflow description
The `## Overview` section of a `.sop.md` file SHALL become the workflow's `description` field.
#### Scenario: Overview maps to description
- **WHEN** a `.sop.md` has `## Overview\nThis SOP does X`
- **THEN** the resulting workflow SHALL have `description: "This SOP does X"`
### Requirement: Parameter acquisition constraints
The transpiler SHALL validate that all required parameters from `## Parameters` are present before execution, using the constraint pattern from the SOP.
#### Scenario: Missing required parameter
- **WHEN** a required parameter has no value provided
- **THEN** the workflow SHALL prompt the user for the missing parameter before executing

View File

@@ -0,0 +1,44 @@
## ADDED Requirements
### Requirement: Inline script execution
Script nodes SHALL execute inline TypeScript (runtime: `bun`) or Python (runtime: `uv`) code and capture stdout as the node output.
#### Scenario: Bun inline execution
- **WHEN** a script node has `runtime: bun` and `script: console.log("hello")`
- **THEN** the executor SHALL run the script via `bun -e`
- **THEN** stdout SHALL be captured as `$nodeId.output`
#### Scenario: Python inline execution
- **WHEN** a script node has `runtime: uv` and `script: print("hello")`
- **THEN** the executor SHALL run the script via `uv run python -c`
- **THEN** stdout SHALL be captured as `$nodeId.output`
### Requirement: Dependency installation
Script nodes SHALL support a `deps:` array that installs dependencies before execution.
#### Scenario: Bun with npm deps
- **WHEN** a script node has `runtime: bun` and `deps: ["lodash", "zod"]`
- **THEN** the executor SHALL run `bun install lodash zod` before executing
#### Scenario: Python with pip deps
- **WHEN** a script node has `runtime: uv` and `deps: ["requests", "click"]`
- **THEN** the executor SHALL run `uv pip install requests click` before executing
### Requirement: Named script files
Script nodes MAY reference named scripts from a `.archon/scripts/` directory by name instead of inline code.
#### Scenario: Named script discovery
- **WHEN** a script node has `script: analyze` and `scripts/analyze.ts` exists
- **THEN** the executor SHALL load and execute the file
#### Scenario: Runtime inferred from extension
- **WHEN** a script has `runtime: bun` and the named file has a `.ts` extension
- **THEN** the executor SHALL run it via `bun run`
### Requirement: Script timeout
Script nodes SHALL support a `timeout:` field in milliseconds. If execution exceeds the timeout, the process SHALL be killed and the node SHALL fail.
#### Scenario: Timeout exceeded
- **WHEN** a script node sets `timeout: 5000` and the script runs for 10 seconds
- **THEN** the process SHALL be killed after 5 seconds
- **THEN** the node SHALL be marked as failed with a timeout error

View File

@@ -0,0 +1,48 @@
## ADDED Requirements
### Requirement: IWorkflowStore interface
All storage backends SHALL implement the `IWorkflowStore` interface providing run lifecycle, event persistence, and node output retrieval.
#### Scenario: Store provides run CRUD
- **WHEN** a workflow run is created
- **THEN** `createWorkflowRun()` SHALL persist the run and return it
- **WHEN** a workflow run status is updated
- **THEN** `updateWorkflowRun()` SHALL persist the status change
#### Scenario: Store provides event persistence
- **WHEN** a workflow event is created
- **THEN** `createWorkflowEvent()` SHALL append it to the event log
#### Scenario: Store provides completed node outputs
- **WHEN** a workflow is resumed
- **THEN** `getCompletedDagNodeOutputs()` SHALL return all completed node outputs keyed by node ID
### Requirement: Filesystem backend
The filesystem backend SHALL store each workflow run as files in a directory: `{artifactsDir}/{runId}/`.
#### Scenario: Filesystem stores events as JSONL
- **WHEN** events are created using the filesystem backend
- **THEN** each run SHALL have `events.jsonl` with newline-delimited JSON
- **THEN** node outputs SHALL be stored as individual JSON files
#### Scenario: Filesystem stores run metadata
- **WHEN** a run is created using the filesystem backend
- **THEN** `run.json` SHALL contain the run metadata
### Requirement: SQLite backend
The SQLite backend SHALL store workflow data in a SQLite database with tables for runs, events, and node outputs.
#### Scenario: SQLite stores runs table
- **WHEN** using the SQLite backend
- **THEN** a `workflow_runs` table SHALL exist with columns for id, workflow_name, status, user_message, created_at, updated_at
#### Scenario: SQLite stores events table
- **WHEN** using the SQLite backend
- **THEN** a `workflow_events` table SHALL exist with columns for run_id, sequence, event_type, timestamp, payload
### Requirement: Postgres backend
The Postgres backend SHALL use a PostgreSQL database with the same schema as SQLite, accessed via the `IWorkflowStore` interface.
#### Scenario: Postgres uses same interface
- **WHEN** switching from SQLite to Postgres
- **THEN** no workflow engine code SHALL change — only the store implementation

View File

@@ -0,0 +1,57 @@
## ADDED Requirements
### Requirement: Node output references
Prompts and commands SHALL support `$nodeId.output` to reference the output text of an upstream node, and `$nodeId.output.field` to reference a specific field from a structured output.
#### Scenario: Output reference substitution
- **WHEN** a prompt contains `$analysis.output`
- **THEN** it SHALL be replaced with the full output text of the node with id `analysis`
#### Scenario: Field reference with structured output
- **WHEN** a prompt contains `$analysis.output.summary` and the upstream node declared `output_format: { type: "object", properties: { summary: ... } }`
- **THEN** it SHALL be replaced with the value of the `summary` field from the parsed JSON output
#### Scenario: Missing node reference
- **WHEN** a prompt references `$nonexistent.output`
- **THEN** the reference SHALL resolve to an empty string with a warning
#### Scenario: Missing field on schemaless node
- **WHEN** a prompt references `$node.output.field` and the upstream node has no `output_format` and its output is not valid JSON
- **THEN** the consuming node SHALL fail with an error
#### Scenario: Strict field access for declared schemas
- **WHEN** a prompt references `$node.output.field` and the upstream node's `output_format` declares properties but `field` is not among them
- **THEN** the consuming node SHALL fail with a field-not-found error
### Requirement: Built-in variables
The engine SHALL support `$ARGUMENTS`, `$ARTIFACTS_DIR`, `$WORKFLOW_ID`, `$BASE_BRANCH`, `$DOCS_DIR`.
#### Scenario: $ARGUMENTS substitution
- **WHEN** a prompt contains `$ARGUMENTS`
- **THEN** it SHALL be replaced with the full user message/arguments string
#### Scenario: $ARTIFACTS_DIR substitution
- **WHEN** a prompt contains `$ARTIFACTS_DIR`
- **THEN** it SHALL be replaced with the path to the run's artifact directory
#### Scenario: $WORKFLOW_ID substitution
- **WHEN** a prompt contains `$WORKFLOW_ID`
- **THEN** it SHALL be replaced with the workflow run ID
### Requirement: Loop-specific variables
Loop nodes SHALL support `$LOOP_USER_INPUT` (from approve at interactive gates) and `$LOOP_PREV_OUTPUT` (output of the previous iteration).
#### Scenario: $LOOP_PREV_OUTPUT on first iteration
- **WHEN** a loop node is on its first iteration
- **THEN** `$LOOP_PREV_OUTPUT` SHALL resolve to an empty string
#### Scenario: $LOOP_PREV_OUTPUT on subsequent iterations
- **WHEN** a loop node is on iteration 2+
- **THEN** `$LOOP_PREV_OUTPUT` SHALL contain the cleaned output of the previous iteration
### Requirement: Approval-specific variables
Approval nodes SHALL support `$REJECTION_REASON`.
#### Scenario: $REJECTION_REASON in on_reject prompt
- **WHEN** an approval node is rejected with a reason
- **THEN** `$REJECTION_REASON` SHALL contain the reviewer's feedback text

View File

@@ -0,0 +1,51 @@
## ADDED Requirements
### Requirement: Run states
A workflow run SHALL transition through states: `pending → running → completed | failed | cancelled`. It MAY transition to `paused` for approval gates.
#### Scenario: Normal completion
- **WHEN** all DAG nodes complete successfully
- **THEN** the run status SHALL be `completed`
#### Scenario: Node failure
- **WHEN** a node fails and no retry succeeds
- **THEN** the run status SHALL be `failed`
#### Scenario: User cancellation
- **WHEN** a user cancels a running workflow
- **THEN** the run status SHALL be `cancelled`
#### Scenario: Approval pause
- **WHEN** an approval node is reached
- **THEN** the run status SHALL transition to `paused`
- **THEN** it SHALL transition back to `running` on approval
### Requirement: Resume from failure
A failed workflow SHALL support resumption, skipping already-completed nodes using stored outputs from the event log.
#### Scenario: Resume skips completed nodes
- **WHEN** a failed workflow has 2 completed nodes out of 5
- **THEN** resuming SHALL skip nodes 1-2 and re-execute from node 3
#### Scenario: Resume with always_run
- **WHEN** a node has `always_run: true` and the workflow is resumed
- **THEN** the node SHALL re-execute even if it completed previously
### Requirement: Event-based observability
All lifecycle transitions SHALL emit typed events through the event emitter for observability and external subscribers.
#### Scenario: Events for every state transition
- **WHEN** a workflow starts
- **THEN** a `workflow_started` event SHALL be emitted
- **WHEN** a workflow completes
- **THEN** a `workflow_completed` event SHALL be emitted
- **WHEN** a node starts/completes/fails/skips
- **THEN** corresponding node events SHALL be emitted
### Requirement: Cleanup
The engine SHALL support cleaning up old workflow runs and their artifacts.
#### Scenario: Cleanup by age
- **WHEN** cleanup is invoked with a retention period (default 7 days)
- **THEN** runs older than the retention period SHALL have their artifacts removed
- **THEN** run records MAY be pruned from the store

View File

@@ -0,0 +1,77 @@
## ADDED Requirements
### Requirement: Workflow definition structure
A workflow YAML SHALL have a top-level `name`, `description`, and `nodes:` array. It MAY have `provider`, `model`, `interactive`, `mutates_checkout`, `tags`.
#### Scenario: Minimal valid workflow
- **WHEN** a YAML file contains a `name`, `description`, and at least one node
- **THEN** the loader SHALL parse it as a valid workflow definition
#### Scenario: Missing name
- **WHEN** a YAML file lacks a `name` field
- **THEN** the loader SHALL reject it with a validation error
### Requirement: Seven node types
The engine SHALL support exactly 7 node types, mutually exclusive per node: `command`, `prompt`, `bash`, `script`, `loop`, `approval`, `cancel`.
#### Scenario: Node with exactly one mode field
- **WHEN** a node has `prompt:` but no other mode field
- **THEN** it SHALL be classified as a PromptNode
#### Scenario: Node with multiple mode fields
- **WHEN** a node has both `prompt:` and `bash:`
- **THEN** the loader SHALL reject it with a mutual-exclusivity error
#### Scenario: Node with no mode field
- **WHEN** a node has none of the 7 mode fields
- **THEN** the loader SHALL reject it
### Requirement: Common node fields
All node types SHALL support `id`, `depends_on`, `when`, `trigger_rule`, `retry`, `timeout`, `output_type`, `always_run`.
#### Scenario: Node id must be unique
- **WHEN** two nodes in the same workflow share the same `id`
- **THEN** the loader SHALL reject the workflow
### Requirement: Prompt node
A PromptNode SHALL have a `prompt:` string field containing the AI prompt text.
#### Scenario: Empty prompt rejected
- **WHEN** a node has `prompt: ""`
- **THEN** the loader SHALL reject it
### Requirement: Bash node
A BashNode SHALL have a `bash:` string field and MAY have `timeout` (ms). AI-specific fields SHALL be ignored with a warning.
#### Scenario: Bash node with timeout
- **WHEN** a bash node includes `timeout: 30000`
- **THEN** the executor SHALL kill the subprocess after 30 seconds
### Requirement: Script node
A ScriptNode SHALL have `script:` (inline or named), `runtime:` (`bun` or `uv`), MAY have `deps:` and `timeout:`.
#### Scenario: Script with deps
- **WHEN** a script node has `runtime: bun` and `deps: ["lodash"]`
- **THEN** the executor SHALL install dependencies before running the script
#### Scenario: Named script from disk
- **WHEN** `script: analyze` and a file `scripts/analyze.ts` exists
- **THEN** the executor SHALL load and run it
### Requirement: Loop node
A LoopNode SHALL have `loop:` with `prompt`, `until`, `max_iterations`, and optional `fresh_context`, `interactive`, `gate_message`, `until_bash`.
#### Scenario: Loop with completion signal
- **WHEN** the AI response contains the `until` string
- **THEN** the loop SHALL stop and the node SHALL complete
#### Scenario: Loop exceeds max_iterations
- **WHEN** the loop reaches `max_iterations` without the completion signal
- **THEN** the node SHALL fail
### Requirement: Cancel node
A CancelNode SHALL have a `cancel:` string containing a reason. It SHALL terminate the workflow run.
#### Scenario: Cancel terminates workflow
- **WHEN** a cancel node executes
- **THEN** the workflow SHALL be marked as cancelled with the cancel reason

View File

@@ -0,0 +1,102 @@
## 1. Project Scaffold
- [ ] 1.1 Initialize package with `package.json`, `tsconfig.json`, module structure (`src/`, `src/cli/`, `src/engine/`, `src/store/`, `src/format/`)
- [ ] 1.2 Add core dependencies: `zod`, `js-yaml`, `nanoid`, `ulid`
- [ ] 1.3 Configure build (tsc or bun build), lint, format, and test scripts
- [ ] 1.4 Create public exports index (`src/index.ts`) with all type and function exports
## 2. Schema Layer — Workflow and Node Types
- [ ] 2.1 Implement `dag-node.ts`: Zod schema for all 7 node types with mutual-exclusivity superRefine, type guards, and AI-field warnings
- [ ] 2.2 Implement `workflow.ts`: WorkflowDefinition schema extending WorkflowBase with nodes array, WorkflowExecutionResult, WorkflowSource types
- [ ] 2.3 Implement `loop.ts`: LoopNodeConfig (prompt, until, max_iterations, fresh_context, interactive, gate_message, until_bash)
- [ ] 2.4 Implement `retry.ts`: Retry config (max_attempts, delay_ms, on_error)
- [ ] 2.5 Implement `workflow-run.ts`: WorkflowRun, WorkflowRunStatus, NodeState, NodeOutput, ApprovalContext schemas
## 3. YAML Format — Loader and Validation
- [ ] 3.1 Implement `loader.ts`: YAML parsing via js-yaml, per-node dagNodeSchema validation, DAG structure validation (unique IDs, depends_on refs, cycle detection via Kahn's)
- [ ] 3.2 Implement `command-validation.ts`: Command name format validation
- [ ] 3.3 Implement `model-validation.ts`: Provider/model resolution (optional — skip before AI provider integration)
- [ ] 3.4 Add workflow-level validation: required fields, provider identity, node ref integrity
## 4. DAG Engine — Core Execution
- [ ] 4.1 Implement `deps.ts`: WorkflowDeps injection interface, IWorkflowPlatform, WorkflowConfig types
- [ ] 4.2 Implement `dag-executor.ts`: Kahn's algorithm topological layering, `buildTopologicalLayers()`, `checkTriggerRule()` (4 trigger rules), Promise.allSettled concurrent layer execution
- [ ] 4.3 Implement node dispatch: execution handlers for PromptNode (AI), CommandNode (command loading), BashNode (subprocess), CancelNode (termination)
- [ ] 4.4 Implement `executor-shared.ts`: `substituteWorkflowVariables()`, `loadCommandPrompt()`, `classifyError()`, `safeSendMessage()`
- [ ] 4.5 Implement `output-ref.ts`: `$nodeId.output` and `$nodeId.output.field` resolution with strict field access
- [ ] 4.6 Implement `condition-evaluator.ts`: `when:` expression parser (==, !=, <, >, <=, >=, AND/OR, comparators with $nodeId.output)
- [ ] 4.7 Implement `event-emitter.ts`: Typed events (workflow_started/completed/failed, node_started/completed/failed/skipped)
## 5. Event Sourcing — Persistence and Replay
- [ ] 5.1 Implement `store.ts`: IWorkflowStore interface (createWorkflowRun, getWorkflowRun, updateWorkflowRun, failWorkflowRun, createWorkflowEvent, getCompletedDagNodeOutputs, getActiveWorkflowRunByPath)
- [ ] 5.2 Implement `executor.ts`: Top-level workflow orchestrator — create run, path-lock guard, dispatch to dag-executor, handle resume with prior completed nodes, event emission
- [ ] 5.3 Implement event persistence: 8 event types stored chronologically, node outputs stored for resume
- [ ] 5.4 Implement resume: `hydrateResumableRun()` loads prior completed node outputs, skips re-execution
- [ ] 5.5 Implement cleanup: retention-based run record and artifact removal
## 6. Storage Backends
- [ ] 6.1 Implement filesystem store: `createFsStore(path)` — run.json per run, events.jsonl, node outputs as JSON files, file-level locking
- [ ] 6.2 Implement SQLite store: `createSqliteStore(path)` — workflow_runs, workflow_events, node_outputs tables with WAL mode
- [ ] 6.3 Implement Postgres store: `createPostgresStore(connectionString)` — same schema as SQLite, pg driver
## 7. Variable Substitution
- [ ] 7.1 Implement workflow-level variable substitution: $WORKFLOW_ID, $ARGUMENTS, $ARTIFACTS_DIR, $BASE_BRANCH, $DOCS_DIR
- [ ] 7.2 Implement node output references in prompts: `$nodeId.output` (full text), `$nodeId.output.field` (structured field access)
- [ ] 7.3 Implement loop-specific variables: `$LOOP_USER_INPUT`, `$LOOP_PREV_OUTPUT`, `$REJECTION_REASON`
- [ ] 7.4 Implement command-level variable substitution: $1-$9 positional args
## 8. Script and Bash Execution
- [ ] 8.1 Implement BashNode execution: `bash -c` subprocess with timeout, stdout capture, env var injection
- [ ] 8.2 Implement ScriptNode — bun runtime: inline `bun -e`, named scripts from `.archon/scripts/`, deps installation
- [ ] 8.3 Implement ScriptNode — uv runtime: `uv run python -c`, named scripts, uv deps installation
- [ ] 8.4 Implement `script-discovery.ts`: discover scripts by extension (.ts→bun, .py→uv) from project and home scopes
## 9. Approval Gates and Human-in-the-Loop
- [ ] 9.1 Implement ApprovalNode handler: pause workflow status, send approval message, store approval context
- [ ] 9.2 Implement approve/resume: transition from paused→running, continue DAG execution
- [ ] 9.3 Implement reject handling: reject node with reason, populate $REJECTION_REASON, execute on_reject prompt if configured
- [ ] 9.4 Implement capture_response: store user comment as $nodeId.output
- [ ] 9.5 Implement interactive loop support: loop.interactive=true pauses between iterations, gate_message shown to user
## 10. Loop Nodes
- [ ] 10.1 Implement LoopNode execution: iterative AI prompt loop with completion signal detection (`until`)
- [ ] 10.2 Implement `max_iterations` enforcement: fail node when exceeded
- [ ] 10.3 Implement `fresh_context` for loop iterations: new session vs. accumulated context
- [ ] 10.4 Implement `until_bash`: bash exit code 0 as completion signal (alternative to text signal)
## 11. CLI Tool (MVP)
- [ ] 11.1 Implement main CLI entry point with subcommand routing (workflow list, run, status, resume)
- [ ] 11.2 Implement `workflow list`: discover and display all workflows with source info
- [ ] 11.3 Implement `workflow run`: execute workflow by name with arguments, --cwd, --store flags
- [ ] 11.4 Implement `workflow status`: display active and recent runs
- [ ] 11.5 Implement `workflow resume`: resume a failed workflow
## 12. Workflow Discovery
- [ ] 12.1 Implement `workflow-discovery.ts`: filesystem discovery across bundled→home→project scopes with precedence
- [ ] 12.2 Implement bundled defaults: embedded default workflows (assist, plan, implement)
- [ ] 12.3 Implement home-global scope: user-level workflows directory
- [ ] 12.4 Implement project scope: repo-local `.workflows/` directory
- [ ] 12.5 Implement resilient loading: per-file error handling, one broken YAML doesn't abort discovery
## 13. Testing
- [ ] 13.1 Unit test DAG executor: topological layering, trigger rules, when conditions, node output refs
- [ ] 13.2 Unit test schema validation: all node types, mutual exclusivity, field validation
- [ ] 13.3 Unit test variable substitution: $nodeId.output, $ARGUMENTS, $LOOP_PREV_OUTPUT edge cases
- [ ] 13.4 Unit test condition evaluator: comparison operators, compound AND/OR, error cases
- [ ] 13.5 Unit test filesystem store: create/read/update runs, events, node outputs, resume data
- [ ] 13.6 Unit test SQLite store: same coverage as filesystem
- [ ] 13.7 Unit test CLI commands: argument parsing, output formatting, approval flow
- [ ] 13.8 Integration test: end-to-end workflow execution with bash and script nodes
- [ ] 13.9 Integration test: resume after failure with prior node outputs loaded