# Workflows

LangGraph-based workflows for multi-step agent orchestration.

## What are Workflows?

Workflows are state machines that orchestrate complex multi-step tasks with:

- **State Management**: Typed state with annotations
- **Conditional Routing**: Different paths based on state
- **Validation Loops**: Retry with fixes
- **Human-in-the-Loop**: Approval gates and interrupts
- **Error Recovery**: Graceful handling of failures

Built on [LangGraph.js](https://langchain-ai.github.io/langgraphjs/).

## Directory Structure

```
workflows/
├── base-workflow.ts          # Base class and utilities
├── {workflow-name}/
│   ├── config.yaml           # Workflow configuration
│   ├── state.ts              # State schema (Annotations)
│   ├── nodes.ts              # Node implementations
│   └── graph.ts              # StateGraph definition
└── README.md                 # This file
```

## Workflow Components

### State (state.ts)

Defines what data flows through the workflow:

```typescript
import { Annotation } from '@langchain/langgraph';
import { BaseWorkflowState } from '../base-workflow.js';

export const MyWorkflowState = Annotation.Root({
  ...BaseWorkflowState.spec,  // Inherit base fields

  // Your custom fields
  input: Annotation<string>(),
  result: Annotation<string | null>({ default: () => null }),
  errorCount: Annotation<number>({ default: () => 0 }),
});

export type MyWorkflowStateType = typeof MyWorkflowState.State;
```

### Nodes (nodes.ts)

Functions that transform state:

```typescript
export function createMyNode(deps: Dependencies) {
  return async (state: MyWorkflowStateType): Promise<Partial<MyWorkflowStateType>> => {
    // Do work
    const result = await doSomething(state.input);

    // Return partial state update
    return { result };
  };
}
```

### Graph (graph.ts)

Connects nodes with edges:

```typescript
import { StateGraph } from '@langchain/langgraph';
import { BaseWorkflow } from '../base-workflow.js';

export class MyWorkflow extends BaseWorkflow<MyWorkflowStateType> {
  buildGraph(): StateGraph<MyWorkflowStateType> {
    const graph = new StateGraph(MyWorkflowState);

    // Add nodes
    graph
      .addNode('step1', createStep1Node())
      .addNode('step2', createStep2Node());

    // Add edges
    graph
      .addEdge('__start__', 'step1')
      .addEdge('step1', 'step2')
      .addEdge('step2', '__end__');

    return graph;
  }
}
```

### Config (config.yaml)

Workflow settings:

```yaml
name: my-workflow
description: What it does

timeout: 300000        # 5 minutes
maxRetries: 3
requiresApproval: true
approvalNodes:
  - human_approval

# Custom settings
myCustomSetting: value
```

## Common Patterns

### 1. Validation Loop (Retry with Fixes)

```typescript
graph
  .addNode('validate', validateNode)
  .addNode('fix', fixNode)
  .addConditionalEdges('validate', (state) => {
    if (state.isValid) return 'next_step';
    if (state.retryCount >= 3) return '__end__';  // Give up
    return 'fix';  // Try to fix
  })
  .addEdge('fix', 'validate');  // Loop back
```

### 2. Human-in-the-Loop (Approval)

```typescript
const approvalNode = async (state) => {
  // Send approval request to user's channel
  await sendToChannel(state.userContext.activeChannel, {
    type: 'approval_request',
    data: {
      action: 'execute_trade',
      details: state.tradeDetails,
    }
  });

  // Mark as waiting for approval
  return { approvalRequested: true, userApproved: false };
};

graph.addConditionalEdges('approval', (state) => {
  return state.userApproved ? 'execute' : '__end__';
});

// To resume after user input:
// const updated = await workflow.execute({ ...state, userApproved: true });
```

### 3. Parallel Execution

```typescript
import { Branch } from '@langchain/langgraph';

graph
  .addNode('parallel_start', startNode)
  .addNode('task_a', taskANode)
  .addNode('task_b', taskBNode)
  .addNode('merge', mergeNode);

// Branch to parallel tasks
graph.addEdge('parallel_start', Branch.parallel(['task_a', 'task_b']));

// Merge results
graph
  .addEdge('task_a', 'merge')
  .addEdge('task_b', 'merge');
```

### 4. Error Recovery

```typescript
const resilientNode = async (state) => {
  try {
    const result = await riskyOperation();
    return { result, error: null };
  } catch (error) {
    logger.error({ error }, 'Operation failed');
    return {
      error: error.message,
      fallbackUsed: true,
      result: await fallbackOperation()
    };
  }
};
```

### 5. Conditional Routing

```typescript
graph.addConditionalEdges('decision', (state) => {
  if (state.score > 0.8) return 'high_confidence';
  if (state.score > 0.5) return 'medium_confidence';
  return 'low_confidence';
});

graph
  .addNode('high_confidence', autoApproveNode)
  .addNode('medium_confidence', humanReviewNode)
  .addNode('low_confidence', rejectNode);
```

## Available Workflows

### strategy-validation

Validates trading strategies with multiple steps and a validation loop.

**Flow:**
1. Code Review (using CodeReviewerSubagent)
2. If issues → Fix Code → loop back
3. Backtest (via MCP)
4. If failed → Fix Code → loop back
5. Risk Assessment
6. Human Approval
7. Final Recommendation

**Features:**
- Max 3 retry attempts
- Multi-file memory from subagent
- Risk-based auto-approval
- Comprehensive state tracking

### trading-request

Human-in-the-loop workflow for trade execution.

**Flow:**
1. Analyze market conditions
2. Calculate risk and position size
3. Request human approval (PAUSE)
4. If approved → Execute trade
5. Generate summary

**Features:**
- Interrupt at approval node
- Channel-aware approval UI
- Risk validation
- Execution confirmation

## Creating a New Workflow

### 1. Create Directory

```bash
mkdir -p workflows/my-workflow
```

### 2. Define State

```typescript
// state.ts
import { Annotation } from '@langchain/langgraph';
import { BaseWorkflowState } from '../base-workflow.js';

export const MyWorkflowState = Annotation.Root({
  ...BaseWorkflowState.spec,

  // Add your fields
  input: Annotation<string>(),
  step1Result: Annotation<string | null>({ default: () => null }),
  step2Result: Annotation<string | null>({ default: () => null }),
});

export type MyWorkflowStateType = typeof MyWorkflowState.State;
```

### 3. Create Nodes

```typescript
// nodes.ts
import { MyWorkflowStateType } from './state.js';

export function createStep1Node(deps: any) {
  return async (state: MyWorkflowStateType) => {
    const result = await doStep1(state.input);
    return { step1Result: result };
  };
}

export function createStep2Node(deps: any) {
  return async (state: MyWorkflowStateType) => {
    const result = await doStep2(state.step1Result);
    return { step2Result: result, output: result };
  };
}
```

### 4. Build Graph

```typescript
// graph.ts
import { StateGraph } from '@langchain/langgraph';
import { BaseWorkflow, WorkflowConfig } from '../base-workflow.js';
import { MyWorkflowState, MyWorkflowStateType } from './state.js';
import { createStep1Node, createStep2Node } from './nodes.js';

export class MyWorkflow extends BaseWorkflow<MyWorkflowStateType> {
  constructor(config: WorkflowConfig, private deps: any, logger: Logger) {
    super(config, logger);
  }

  buildGraph(): StateGraph<MyWorkflowStateType> {
    const graph = new StateGraph(MyWorkflowState);

    const step1 = createStep1Node(this.deps);
    const step2 = createStep2Node(this.deps);

    graph
      .addNode('step1', step1)
      .addNode('step2', step2)
      .addEdge('__start__', 'step1')
      .addEdge('step1', 'step2')
      .addEdge('step2', '__end__');

    return graph;
  }
}
```

### 5. Create Config

```yaml
# config.yaml
name: my-workflow
description: My workflow description

timeout: 60000
maxRetries: 3
requiresApproval: false

model: claude-sonnet-4-6
```

### 6. Add Factory Function

```typescript
// graph.ts (continued)
export async function createMyWorkflow(
  deps: any,
  logger: Logger,
  configPath: string
): Promise<MyWorkflow> {
  const config = await loadYAML(configPath);
  const workflow = new MyWorkflow(config, deps, logger);
  workflow.compile();
  return workflow;
}
```

## Usage

### Execute Workflow

```typescript
import { createMyWorkflow } from './harness/workflows';

const workflow = await createMyWorkflow(deps, logger, configPath);

const result = await workflow.execute({
  userContext,
  input: 'my input'
});

console.log(result.output);
```

### Stream Workflow

```typescript
for await (const state of workflow.stream({ userContext, input })) {
  console.log('Current state:', state);
}
```

### With Interrupts (Human-in-the-Loop)

```typescript
// Initial execution (pauses at interrupt)
const pausedState = await workflow.execute(initialState);

// User provides input
const userInput = await getUserApproval();

// Resume from paused state
const finalState = await workflow.execute({
  ...pausedState,
  userApproved: userInput.approved
});
```

## Best Practices

### State Design

- **Immutable Updates**: Return partial state, don't mutate
- **Type Safety**: Use TypeScript annotations
- **Defaults**: Provide sensible defaults
- **Nullable Fields**: Use `| null` with `default: () => null`

### Node Implementation

- **Pure Functions**: Avoid side effects in state logic
- **Error Handling**: Catch errors, return error state
- **Logging**: Log entry/exit of nodes
- **Partial Updates**: Only return fields that changed

### Graph Design

- **Single Responsibility**: Each node does one thing
- **Clear Flow**: Easy to visualize the graph
- **Error Paths**: Handle failures gracefully
- **Idempotency**: Safe to retry nodes

### Configuration

- **Timeouts**: Set reasonable limits
- **Retries**: Don't retry forever
- **Approvals**: Mark approval nodes explicitly
- **Documentation**: Explain complex config values

## Debugging

### View Graph

```typescript
// Get graph structure
const compiled = workflow.compile();
console.log(compiled.getGraph());
```

### Log State

```typescript
const debugNode = async (state) => {
  logger.debug({ state }, 'Current state');
  return {};  // No changes
};

graph.addNode('debug', debugNode);
```

### Test Nodes in Isolation

```typescript
const step1 = createStep1Node(deps);
const result = await step1({ input: 'test', /* ... */ });
expect(result.step1Result).toBe('expected');
```

## References

- [LangGraph.js Docs](https://langchain-ai.github.io/langgraphjs/)
- [LangChain.js Docs](https://js.langchain.com/)
- [Example: strategy-validation](./strategy-validation/graph.ts)
- [Example: trading-request](./trading-request/graph.ts)
