## 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
