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:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-06-07
|
||||
@@ -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. |
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user