# AGENTS.md

**Welcome, AI coding agent!** 👋

This is Flock Flow, a production-grade blackboard-first AI agent orchestration framework. This guide gets you up to speed quickly on the current project state and development patterns.

**Current Version:** 0.1.16
**Architecture:** Hybrid Python/TypeScript with real-time dashboard
**Package Manager:** UV (NOT pip!)
**Status:** Production-ready with comprehensive monitoring

---

## 🎯 Project Snapshot

### What Is Flock Flow?

A blackboard architecture framework where specialized AI agents collaborate through a shared typed workspace—no direct coupling, no rigid workflows, just emergent intelligence.

**Core Pattern:** Blackboard Architecture (like Hearsay-II from 1970s, but for modern LLMs)

**Key Differentiator:** The only framework treating blackboard orchestration as a first-class citizen with built-in visibility controls, real-time monitoring, and enterprise-grade safety features.

### Architecture in 30 Seconds

```
┌─────────────────────────────────────────────┐
│         Blackboard (Typed Artifacts)        │
│  ┌──────┐ → ┌──────┐ → ┌──────┐ → ┌──────┐│
│  │ Idea │   │Movie │   │Script│   │Review││
│  └──────┘   └──────┘   └──────┘   └──────┘│
└─────────────────────────────────────────────┘
      ↑           ↑           ↑           ↑
  Agent A     Agent B     Agent C     Agent D
  (produce)   (consume    (consume    (consume
              & produce)  & produce)  & produce)
```

**Key Concepts:**
- **Artifacts:** Typed data (Pydantic models) published to blackboard
- **Subscriptions:** Declarative rules for when agents react
- **Visibility:** Built-in access control (Public/Private/Tenant/Label-based/Time-based)
- **Components:** Pluggable utilities (metrics, budgets, guards) + engines (DSPy, custom)
- **Real-time Dashboard:** React/TypeScript interface for live monitoring

---

## 🚀 Quick Setup

### Prerequisites

- **Python 3.10+** (we use modern async features)
- **UV package manager** (faster than pip, handles virtual envs)
- **Node.js 22+** (for dashboard frontend)
- **OpenAI API key** (for running examples)

### Installation

```bash
# Clone repo
git clone https://github.com/yourusername/flock-flow.git
cd flock-flow

# Install Python dependencies (UV creates venv automatically)
poe install  # Equivalent to: uv sync --dev --all-groups --all-extras

# Set up environment
export OPENAI_API_KEY="sk-..."
export DEFAULT_MODEL="openai/gpt-4o-mini"

# Verify installation
uv run python -c "from flock import Flock; print('✅ Ready!')"
```

### Run Examples

```bash
# Showcase examples (workshops & demos)
uv run python examples/showcase/01_hello_flock.py
uv run python examples/showcase/02_blog_review.py
uv run python examples/showcase/04_dashboard.py

# Feature examples (with assertions)
uv run python examples/features/feedback_prevention.py
uv run python examples/features/visibility/public_visibility.py
```

---

## 🏗️ Current Project Structure

```
flock-flow/
├── src/flock/              # Core Python framework
│   ├── orchestrator.py          # Main orchestrator (scheduling, blackboard)
│   ├── agent.py                 # Agent & AgentBuilder (fluent API)
│   ├── artifacts.py             # Artifact model & specs
│   ├── subscription.py          # Subscription system
│   ├── visibility.py            # Visibility/security controls
│   ├── components.py            # Component base classes
│   ├── runtime.py               # EvalInputs, EvalResult, Context
│   ├── registry.py              # Type & function registries
│   ├── store.py                 # Blackboard storage abstractions
│   ├── dashboard/               # 🆕 Real-time dashboard backend
│   │   ├── collector.py         # Event collection for streaming
│   │   ├── websocket.py         # WebSocket manager
│   │   ├── service.py           # Dashboard HTTP service
│   │   └── launcher.py          # One-line dashboard activation
│   ├── mcp/                     # MCP (Model Context Protocol) support
│   │   ├── client.py            # MCP client implementation
│   │   ├── manager.py           # MCP server management
│   │   └── servers/             # MCP server implementations
│   └── engines/                 # Engine implementations
│       └── dspy_engine.py       # DSPy LLM engine with streaming
├── frontend/                    # 🆕 React/TypeScript dashboard
│   ├── src/
│   │   ├── components/          # React components
│   │   ├── hooks/               # Custom React hooks
│   │   ├── store/               # Zustand state management
│   │   ├── services/            # WebSocket, API clients
│   │   └── types/               # TypeScript type definitions
│   ├── package.json             # Node.js dependencies
│   └── vite.config.ts           # Vite build configuration
├── examples/                    # Working examples
│   ├── showcase/                # Engaging demos for workshops
│   └── features/                # Feature validation with assertions
├── tests/                       # Comprehensive test suite
│   ├── contract/                # Contract tests (system behavior)
│   ├── integration/             # Component interaction tests
│   ├── e2e/                     # End-to-end tests
│   └── conftest.py              # Shared test fixtures
├── docs/patterns/               # 🆕 Analysis documentation
│   ├── development-workflow.md  # Development patterns
│   ├── project-configuration.md # Technical configuration
│   ├── core-architecture.md     # Architecture deep dive
│   └── repository-structure.md  # Code organization
├── pyproject.toml               # UV project configuration
├── uv.lock                      # Locked dependencies
├── README.md                    # User-facing documentation
└── AGENTS.md                    # You are here! 👈
```

---

## 📦 Dependencies & Package Management

### UV Commands (NOT pip!)

```bash
# Poe tasks (recommended)
poe install          # Complete installation workflow
poe build           # Sync dependencies, build, and install
poe test            # Run tests
poe test-cov        # Run with coverage
poe lint            # Lint code
poe format          # Format code

# Manual UV commands
uv sync --dev --all-groups --all-extras  # Install all dependencies
uv add package-name                        # Add production dependency
uv add --dev package-name                  # Add development dependency
uv remove package-name                     # Remove dependency
uv run pytest tests/test_specific.py       # Run specific test
```

**⚠️ CRITICAL: NEVER use `pip install`** - Always use `uv add` to maintain `uv.lock` consistency.

### Key Dependencies

| Category | Package | Purpose |
|----------|---------|---------|
| **AI/LLM** | `dspy==3.0.0` | DSPy framework for prompt programming |
| | `litellm==1.75.3` | LLM API abstraction layer |
| **Web Framework** | `fastapi>=0.117.1` | Modern web framework |
| | `uvicorn>=0.37.0` | ASGI server |
| | `websockets>=15.0.1` | WebSocket support |
| **Data & Validation** | `pydantic[email]>=2.11.9` | Data validation and settings |
| **CLI & UX** | `typer>=0.19.2` | Modern CLI framework |
| | `rich>=14.1.0` | Rich text and formatting |
| **Observability** | `opentelemetry-***` | Distributed tracing |
| | `loguru>=0.7.3` | Structured logging |
| **Protocol** | `mcp>=1.7.1` | Model Context Protocol support |
| **Testing** | `pytest>=8.3.3` | Testing framework |
| | `ruff>=0.7.2` | Linting and formatting |
| | `mypy>=1.15.0` | Type checking |

### Frontend Dependencies

| Category | Package | Purpose |
|----------|---------|---------|
| **Core** | `react^19.2.0` | UI framework |
| | `typescript^5.9.3` | Type safety |
| | `vite^7.1.9` | Build tool |
| **Visualization** | `@xyflow/react^12.8.6` | Graph visualization |
| | `dagre^0.8.5` | Graph layout algorithm |
| **State** | `zustand^5.0.8` | State management |
| | `idb^8.0.3` | IndexedDB wrapper |
| **Testing** | `vitest^3.2.4` | Test framework |
| | `@testing-library/react^16.3.0` | React testing utilities |

---

## 🔬 Core Architecture

### 1. Flock Orchestrator (`src/flock/orchestrator.py`)

**Purpose:** Central coordinator—manages blackboard, schedules agents

**Key Methods:**
- `agent(name)` → Create new agent builder
- `run(agent, *inputs)` → Synchronous execution
- `arun(agent, *inputs)` → Async execution
- `run_until_idle()` → Wait for all tasks to complete
- `serve(dashboard=True)` → Start HTTP service + dashboard
- `publish_external(type_name, payload)` → External artifact publishing

**Safety Features:**
- **Circuit Breaker**: `max_agent_iterations=1000` stops runaway agents
- **Duplicate Prevention**: Tracks processed (artifact_id, agent_name) pairs
- **Self-Trigger Protection**: `prevent_self_trigger=True` by default

### 2. Agent System (`src/flock/agent.py`)

**Builder Pattern:**
```python
agent = (
    orchestrator.agent("name")
    .description("What this agent does")
    .consumes(InputType, where=lambda x: x.valid)
    .publishes(OutputType, visibility=PrivateVisibility(["agent_a"]))
    .with_utilities(MetricsComponent(), LoggingComponent())
    .with_engines(DSPyEngine(model="gpt-4o"))
    .best_of(5, score=lambda r: r.metrics["confidence"])
    .max_concurrency(10)
    .prevent_self_trigger(False)  # Explicit opt-in for feedback loops
)
```

**Lifecycle Stages (9 total):**
1. `on_initialize` - Setup
2. `on_pre_consume` - Transform input artifacts
3. `on_pre_evaluate` - Prepare evaluation
4. **Engines run** - Core processing
5. `on_post_evaluate` - Transform results
6. **Publish** - Create artifacts
7. `on_post_publish` - React to publications
8. `on_error` - Handle failures
9. `on_terminate` - Cleanup

### 3. Dashboard System (`src/flock/dashboard/`)

**One-Line Activation:**
```python
# Start dashboard with real-time monitoring
await orchestrator.serve(dashboard=True)
```

**Components:**
- **DashboardEventCollector** - Captures agent lifecycle events
- **WebSocketManager** - Real-time event broadcasting to frontend
- **DashboardHTTPService** - HTTP API for dashboard controls
- **DashboardLauncher** - Handles npm install, process management, browser launch

**Frontend Features:**
- **Dual Visualization Modes**: Agent View vs Blackboard View
- **Real-time Updates**: WebSocket streaming with 2-minute heartbeat
- **Control Panel**: Publish artifacts and invoke agents from UI
- **EventLog Module**: Comprehensive event viewing with filtering
- **Persistence**: Node positions, preferences, session data in IndexedDB

### 4. MCP Integration (`src/flock/mcp/`)

**Purpose:** Model Context Protocol support for external tool integration

**Key Classes:**
- **MCPClientManager** - Manages MCP server connections
- **FlockMCPConfiguration** - Server configuration and tool assignments
- **MCPClientWrapper** - Client interface with lazy connection establishment

**Usage:**
```python
# Register MCP server with tools
orchestrator.register_mcp_server("server_name", config)

# Assign tools to agents
agent.uses_mcp("server_name")
```

### 5. Visibility System (`src/flock/visibility.py`)

**Access Control Types:**
- `PublicVisibility` - Everyone can see
- `PrivateVisibility` - Allowlist of agents
- `LabelledVisibility` - RBAC (agents need required labels)
- `TenantVisibility` - Multi-tenancy (per-tenant isolation)
- `AfterVisibility` - Time-delayed (embargo periods)

**Enforcement:**
```python
if not artifact.visibility.allows(agent.identity):
    continue  # Don't schedule agent
```

---

## 🎨 Code Style & Conventions

### Python Style

```python
# ✅ Good: Type hints everywhere
async def execute(self, ctx: Context, artifacts: List[Artifact]) -> List[Artifact]:
    ...

# ✅ Good: Descriptive names and docstrings
def _schedule_artifact(self, artifact: Artifact) -> None:
    """Schedule artifact for agent processing."""
    ...

# ✅ Good: Pydantic models with Field descriptions
@flock_type
class Movie(BaseModel):
    title: str = Field(description="Movie title in CAPS")
    runtime: int = Field(ge=60, le=400, description="Runtime in minutes")
```

### TypeScript/React Style

```typescript
// ✅ Good: Type-safe components with proper props
interface DashboardLayoutProps {
  children: React.ReactNode;
}

const DashboardLayout: React.FC<DashboardLayoutProps> = ({ children }) => {
  // Component implementation
};

// ✅ Good: Custom hooks with proper typing
const useWebSocket = (url: string) => {
  const [isConnected, setIsConnected] = useState<boolean>(false);
  // Hook implementation
};
```

### Async Patterns

```python
# ✅ Always use async for I/O operations
async def publish(self, artifact: Artifact) -> None:
    async with self._lock:
        await self.store.publish(artifact)

# ✅ Use asyncio.gather for parallel operations
results = await asyncio.gather(
    agent_a.execute(ctx, artifacts),
    agent_b.execute(ctx, artifacts),
)
```

---

## 🧪 Testing Strategy

### Test Coverage Goals

- **Overall Target:** 80%+ project-wide
- **Core Framework Modules:** 90-100% coverage
- **Critical Paths:** 100% coverage (security & correctness)
- **Frontend Components:** 80%+ coverage
- **Integration Tests:** Full E2E validation

### Test Categories

1. **Unit Tests** (`tests/test_*.py`) - Individual component testing
2. **Contract Tests** (`tests/contract/`) - System behavior contracts
3. **Integration Tests** (`tests/integration/`) - Component interaction testing
4. **End-to-End Tests** (`tests/e2e/`) - Full workflow testing
5. **Frontend Tests** (`frontend/src/test/**/*.test.tsx`) - React component testing

### Running Tests

```bash
# Run all tests
poe test

# Run with coverage report
poe test-cov

# Run critical path tests (100% coverage required)
poe test-critical

# Run frontend tests
cd frontend && npm test

# Run E2E tests
poe test-e2e

# Determinism test (10 consecutive runs)
poe test-determinism
```

### Current Test Status

- **Backend:** 750+ tests passing
- **Frontend:** 367 tests passing
- **E2E:** 6 tests passing
- **Total:** 1,100+ tests passing
- **Coverage:** Core modules 90-100%, overall 79%
- **100% Coverage Modules:** CLI, Components, Runtime, Store, MCP Tool, Helper CLI
- **Near-Perfect Coverage:** Logging (99.11%), Utilities (95.85%)

---

## 🔧 Development Workflow

### Quick Start for Contributors

```bash
# 1. Clone and setup
git clone https://github.com/yourusername/flock-flow.git
cd flock-flow
poe install

# 2. Install pre-commit hooks (quality automation)
pip install pre-commit
pre-commit install
pre-commit install --hook-type pre-push

# 3. Create feature branch
git checkout -b feature/your-feature-name

# 4. Make changes...
# Write code, tests, documentation

# 5. Run quality checks (or let pre-commit do it automatically)
poe lint          # Lint code
poe format        # Format code
poe test          # Run tests
poe test-cov      # Check coverage

# 6. Commit (pre-commit hooks run automatically)
git add .
git commit -m "feat: Add your feature description"

# 7. Push (build checks and version validation run)
git push origin feature/your-feature-name
```

**📚 For detailed contribution guidelines, see [`CONTRIBUTING.md`](CONTRIBUTING.md)**

### Quality Standards

**Before submitting changes, ensure:**
- [ ] All tests pass (`poe test`)
- [ ] Coverage requirements met (`poe test-cov-fail`)
- [ ] Code is properly formatted (`poe format`)
- [ ] Linting passes (`poe lint`)
- [ ] Type checking passes (`uv run mypy src/flock/`)
- [ ] Frontend tests pass (`cd frontend && npm test`)
- [ ] **Backend builds without errors** (`uv build`) ⚠️ **REQUIRED**
- [ ] **Frontend builds without errors** (`cd frontend && npm run build`) ⚠️ **REQUIRED**
- [ ] Pre-commit hooks installed and passing
- [ ] Versions bumped if code changed (`poe version-check`)
- [ ] Documentation is updated
- [ ] No hardcoded secrets

**💡 Tip**: Pre-commit hooks automatically check most of these when you commit!

**🚨 CRITICAL BUILD REQUIREMENTS:**

For **UI/Frontend changes:**
- **MUST run `npm run build` successfully** before committing
- Fix all TypeScript compilation errors
- Fix all linting errors
- Ensure no runtime errors in production build

For **Backend/Python changes:**
- **MUST run `uv build` successfully** before committing
- Fix all type checking errors
- Fix all import errors
- Ensure package builds cleanly

**Failure to build is a blocking issue - do not commit broken builds!**

### Versioning

Flock Flow uses **smart versioning** that only bumps versions for components that actually changed:

```bash
# Check what would be bumped (dry run)
poe version-check

# Bump versions based on what changed
poe version-patch   # 0.1.18 → 0.1.19 (bug fixes)
poe version-minor   # 0.1.18 → 0.2.0 (new features)
poe version-major   # 0.1.18 → 1.0.0 (breaking changes)
```

**Smart detection**:
- ✅ Backend changes (`src/`, `tests/`) → Bump `pyproject.toml`
- ✅ Frontend changes (`frontend/`) → Bump `package.json`
- ❌ Docs changes (`docs/`, `README.md`) → No version bump

**Typical workflow**:
1. Make code changes and commit
2. Run `poe version-minor` (or patch/major)
3. Commit version bump: `git commit -m "chore: bump version to 0.2.0"`
4. Push (pre-push hook will validate)

See [`docs/VERSIONING.md`](docs/VERSIONING.md) for complete guide.

### Pre-commit Hooks

Automated quality checks run on every commit and push:

**Install hooks** (one-time setup):
```bash
pip install pre-commit
pre-commit install
pre-commit install --hook-type pre-push
```

**What runs automatically:**
- **On commit**: Linting, formatting, type checking, security scans, fast tests
- **On push**: Build validation, comprehensive tests, version check

**Manual runs:**
```bash
pre-commit run --all-files  # Run all hooks
pre-commit run ruff         # Run specific hook
```

**Skip hooks** (emergency only):
```bash
git commit --no-verify -m "emergency fix"
```

See [`docs/PRE_COMMIT_HOOKS.md`](docs/PRE_COMMIT_HOOKS.md) for complete guide.

### Commit Message Convention

```bash
# Feature
git commit -m "feat: Add dashboard event streaming"

# Bug fix
git commit -m "fix: Resolve WebSocket reconnection issue"

# Documentation
git commit -m "docs: Update AGENTS.md with dashboard info"

# Tests
git commit -m "test: Add E2E tests for dashboard controls"

# Performance
git commit -m "perf: Optimize graph rendering performance"
```

---

## 🚀 Running the Dashboard

### Prerequisites

- **Node.js 22+** for frontend development
- **npm** or **yarn** package manager

### Quick Start

```python
import asyncio
from pydantic import BaseModel
from flock.orchestrator import Flock
from flock.registry import flock_type

# Define artifacts
@flock_type
class Idea(BaseModel):
    topic: str
    genre: str

@flock_type
class Movie(BaseModel):
    title: str
    synopsis: str

# Create orchestrator and agents
orchestrator = Flock("openai/gpt-4o")

movie = (
    orchestrator.agent("movie")
    .description("Generate movie concepts")
    .consumes(Idea)
    .publishes(Movie)
)

# 🎉 ONE LINE TO START THE DASHBOARD!
asyncio.run(orchestrator.serve(dashboard=True))
```

### What Happens

1. ✅ **Auto-Install** - Runs `npm install` if `node_modules` missing
2. ✅ **Start Services** - Launches both Python API and React dev server
3. ✅ **Open Browser** - Automatically opens http://localhost:8000
4. ✅ **Inject Collectors** - Adds event collectors to all agents
5. ✅ **Stream Events** - Real-time WebSocket connection for live updates

### Dashboard Features

**Visualization Modes:**
- **Agent View** - Nodes are agents, edges show message flows
- **Blackboard View** - Nodes are artifacts, edges show transformations

**Controls:**
- **Publish Control** - Publish artifacts with auto-filtering
- **Invoke Control** - Invoke agents by name
- **EventLog Module** - Right-click → Add Module → EventLog

**Keyboard Shortcuts:** ⌨️
- **Ctrl+Shift+P** - Toggle Publish Panel
- **Ctrl+Shift+D** - Toggle Agent Details
- **Ctrl+Shift+F** - Toggle Filters Panel
- **Ctrl+,** - Toggle Settings Panel
- **Ctrl+M** - Toggle Agent/Blackboard View
- **Ctrl+F** - Focus filter input
- **Ctrl+/** - Show keyboard shortcuts help dialog
- **Esc** - Close panels and windows

**Real-time Features:**
- **WebSocket Streaming** - Live event updates
- **Connection Status** - Visual indicator for connection state
- **Auto-Filter** - Correlation ID tracking after publish/invoke
- **Keyboard Navigation** - Full accessibility support with WCAG 2.1 AA compliance

---

## 📁 Frontend Development

### Structure

```
frontend/src/
├── components/
│   ├── common/          # Reusable components
│   ├── layout/          # Layout components
│   └── modules/         # Dashboard modules
├── hooks/               # Custom React hooks
├── services/            # WebSocket, API clients
├── store/               # Zustand state management
├── types/               # TypeScript type definitions
└── utils/               # Utility functions
```

### Development Commands

```bash
cd frontend

# Start development server
npm run dev

# Run tests
npm test

# Run tests with UI
npm run test:ui

# Type checking
npm run type-check

# Build for production
npm run build
```

### Key Technologies

- **React 19** - UI framework with latest features
- **TypeScript** - Type safety and better DX
- **Vite** - Fast build tool and dev server
- **Zustand** - Lightweight state management
- **React Flow** - Graph visualization library
- **Vitest** - Fast test framework
- **IndexedDB** - Client-side persistence

---

## 🔐 Security Considerations

### API Keys

```bash
# ✅ Use environment variables
export OPENAI_API_KEY="sk-..."
export AZURE_API_KEY="..."

# ❌ Never commit API keys to git!
# Add to .gitignore:
.env
*.key
```

### Visibility Enforcement

```python
# Always check visibility before scheduling
if not artifact.visibility.allows(agent.identity):
    logger.warning(f"Access denied: {agent.name} → {artifact.id}")
    continue  # Don't schedule
```

### Input Validation

```python
# ✅ Validate all external inputs
@flock_type
class UserInput(BaseModel):
    query: str = Field(max_length=1000)  # Prevent abuse

# ✅ Sanitize before processing
def sanitize(text: str) -> str:
    # Remove HTML, SQL injection attempts, etc.
    return text
```

---

## 🚀 Performance Tips

### Backend Optimization

```python
# ✅ Use asyncio.gather for parallel operations
results = await asyncio.gather(
    agent_a.execute(ctx, artifacts),
    agent_b.execute(ctx, artifacts),
)

# ✅ Use semaphores for concurrency control
async with self._semaphore:  # Limit concurrent executions
    result = await expensive_operation()
```

### Frontend Optimization

```typescript
// ✅ Use React.memo for expensive components
const ExpensiveNode = React.memo(({ data }: NodeProps) => {
  // Component implementation
});

// ✅ Use useMemo for expensive calculations
const layout = useMemo(() => {
  return calculateLayout(nodes, edges);
}, [nodes, edges]);
```

### Monitoring

```python
# Add metrics to components
class MetricsComponent(AgentComponent):
    async def on_post_evaluate(self, agent, ctx, inputs, result):
        result.metrics["latency_ms"] = elapsed * 1000
        result.metrics["tokens"] = count_tokens(result.artifacts)
        return result
```

---

## 🛠️ Common Tasks

### Add a New Agent

```python
# 1. Define artifact types
@flock_type
class MyInput(BaseModel):
    data: str

@flock_type
class MyOutput(BaseModel):
    result: str

# 2. Create agent
agent = (
    orchestrator.agent("my-agent")
    .description("Process my custom data")
    .consumes(MyInput)
    .publishes(MyOutput)
    .with_engines(DSPyEngine())
)

# 3. Write tests
# tests/test_my_agent.py
```

### Add a New Component

```python
# 1. Create component class
from flock.components import AgentComponent

class MyComponent(AgentComponent):
    name: str = "my_component"

    async def on_pre_evaluate(self, agent, ctx, inputs):
        # Your logic here
        return inputs

# 2. Register with agent
agent.with_utilities(MyComponent())

# 3. Write tests
# tests/test_my_component.py
```

### Add a New Dashboard Module

```typescript
// 1. Create module component
const MyModule: React.FC<ModuleProps> = ({ id, position }) => {
  // Module implementation
};

// 2. Register module
import { ModuleRegistry } from '../modules/ModuleRegistry';

ModuleRegistry.register({
  name: 'MyModule',
  component: MyModule,
  icon: '📊',
  defaultSize: { width: 400, height: 300 }
});

// 3. Write tests
// frontend/src/test/modules.test.tsx
```

### Add New Dependencies

```bash
# Python dependency
uv add package-name

# Python dev dependency
uv add --dev package-name

# Frontend dependency
cd frontend && npm install package-name

# Frontend dev dependency
cd frontend && npm install --save-dev package-name
```

---

## 📊 Metrics & Monitoring

### Current Metrics

**Backend Metrics:**
- `artifacts_published` - Total artifacts published
- `agent_runs` - Total agent executions
- `websocket_connections` - Active dashboard connections
- `event_latency` - Event processing latency

**Frontend Metrics:**
- Graph rendering time (<200ms target)
- WebSocket message throughput (>100 events/sec)
- Autocomplete response time (<50ms target)

### Adding Custom Metrics

```python
# In your component
class MetricsComponent(AgentComponent):
    async def on_post_evaluate(self, agent, ctx, inputs, result):
        result.metrics["my_metric"] = value
        return result

# Metrics automatically aggregated in orchestrator
```

---

## 🚨 CRITICAL PATTERNS (Learn from Our Experience)

### ⚡ invoke() vs run_until_idle() - The Double Execution Trap

**This pattern cost us hours of debugging - learn from our pain!**

#### The Problem
After API migration from `arun()` to `invoke()`, tests started executing component hooks **twice**:

```python
# Expected: ['A', 'B', 'C']
# Actual: ['A', 'B', 'C', 'A', 'B', 'C']  # Double execution!
```

#### Root Cause Analysis
`invoke()` method behavior depends on `publish_outputs` parameter:

```python
# invoke() with default publish_outputs=True:
await orchestrator.invoke(agent, input_artifact)
await orchestrator.run_until_idle()  # ❌ Triggers SECOND execution!

# invoke() with publish_outputs=False:
await orchestrator.invoke(agent, input_artifact, publish_outputs=False)
# ✅ No double execution - just direct agent execution
```

**Why this happens:**
1. `invoke(..., publish_outputs=True)` executes agent AND publishes results to blackboard
2. `run_until_idle()` processes published artifacts, triggering agent execution again
3. Component hooks fire on BOTH executions → double execution

#### When to Use Which Pattern

**✅ USE `publish_outputs=False` for:**
- Unit testing specific agent behavior
- Testing component hooks in isolation
- Direct execution without cascade
- Most test scenarios

```python
# ✅ CORRECT: Test component execution order
await orchestrator.invoke(agent, input_artifact, publish_outputs=False)
assert component_order == ["A", "B", "C"]
```

**✅ USE `publish_outputs=True` + `run_until_idle()` for:**
- Integration testing agent cascades
- Testing multi-agent workflows
- End-to-end scenario validation
- Event-driven behavior testing

```python
# ✅ CORRECT: Test agent cascade
await orchestrator.invoke(agent_a, input_artifact, publish_outputs=True)
await orchestrator.run_until_idle()  # Process agent_b, agent_c...
assert len(output_artifacts) == 3
```

#### Quick Reference

| Scenario | invoke() call | run_until_idle() | Result |
|----------|---------------|------------------|---------|
| Unit test | `invoke(..., publish_outputs=False)` | No | ✅ Single execution |
| Integration test | `invoke(..., publish_outputs=True)` | Yes | ✅ Cascade execution |
| Bug we fixed | `invoke(..., publish_outputs=True)` | Yes | ❌ Double execution |

#### Legacy Migration Pattern

During API migration from `arun()` to `invoke()`:

```python
# OLD (arun() - auto-handled publication):
await orchestrator.arun(agent, input)
await orchestrator.run_until_idle()

# NEW (invoke() - manual control):
await orchestrator.invoke(agent, input, publish_outputs=False)
# OR
await orchestrator.invoke(agent, input, publish_outputs=True)
await orchestrator.run_until_idle()
```

**Rule of thumb:** Start with `publish_outputs=False` for tests, only enable publication if you specifically need cascade behavior.

### 🔒 Test Isolation and Mock Cleanup - The Contamination Trap

**We fixed 32 failing tests caused by test contamination - here's what we learned:**

#### The Problem
Tests were modifying class-level properties with PropertyMock that persisted across test boundaries:

```python
# ❌ WRONG: Contaminates other tests
def test_something(orchestrator):
    type(orchestrator).agents = PropertyMock(return_value=[mock_agent])
    # No cleanup - mock persists!
```

#### The Solution
Always use fixture cleanup with yield pattern:

```python
# ✅ CORRECT: Proper cleanup
@pytest.fixture
def dashboard_service_with_mocks(orchestrator):
    original_agents = getattr(type(orchestrator), "agents", None)
    type(orchestrator).agents = PropertyMock(return_value=[mock_agent])
    try:
        yield service
    finally:
        # Restore original or delete if it didn't exist
        if original_agents is not None:
            type(orchestrator).agents = original_agents
        elif hasattr(type(orchestrator), "agents"):
            delattr(type(orchestrator), "agents")
```

#### Best Practices for Test Isolation

1. **Create Helper Functions for Complex Mocks**
```python
def create_mock_agent():
    """Create a properly structured mock agent."""
    mock_agent = Mock()
    mock_agent.subscriptions = []  # Required attribute
    mock_agent.name = "test_agent"
    mock_agent.description = "Test agent"
    return mock_agent
```

2. **Use Fixture Cleanup Pattern**
- Store original values before modification
- Use try/finally blocks in fixtures
- Restore or delete modified attributes

3. **Test in Isolation First**
- Run individual test files to check for contamination
- If tests pass alone but fail in suite = contamination issue

4. **Common Contamination Sources**
- PropertyMock on class attributes
- Module-level patches without cleanup
- Shared mutable state in fixtures
- Async event loops not properly closed
- Rich/logging state pollution across tests

5. **Test Ordering for Contamination-Sensitive Tests**

When some tests are sensitive to state pollution from other tests, use pytest hooks to control execution order:

```python
# In tests/conftest.py
def pytest_collection_modifyitems(config, items):
    """Reorder tests to run contamination-prone tests first sequentially."""
    priority_modules = [
        "test_utilities.py",
        "test_cli.py",
        "test_engines.py",
        "test_orchestrator.py",
        "test_service.py",
    ]

    # Separate priority tests from others
    priority_tests = []
    other_tests = []

    for item in items:
        test_file = Path(item.fspath).name
        if test_file in priority_modules:
            priority_tests.append(item)
        else:
            other_tests.append(item)

    # Sort priority tests by module order
    def get_priority(item):
        test_file = Path(item.fspath).name
        try:
            return priority_modules.index(test_file)
        except ValueError:
            return 999

    priority_tests.sort(key=get_priority)

    # Reorder: priority tests first, then everything else
    items[:] = priority_tests + other_tests
```

**Why this works:**
- Tests sensitive to Rich/logging state run first before any contamination
- Eliminates contamination failures without code changes
- Tests pass reliably in both local and CI environments

---

## 🔍 Observability & Debugging with OpenTelemetry + DuckDB

Flock includes **production-grade distributed tracing** that captures every operation with full input/output data—enabling both human and AI-assisted debugging.

### Quick Start: Enable Tracing

```bash
# Enable auto-tracing (add to .env or export)
FLOCK_AUTO_TRACE=true      # Enable tracing for all operations
FLOCK_TRACE_FILE=true      # Store traces in DuckDB

# Run your application
python examples/showcase/01_declarative_pizza.py

# Traces stored in: .flock/traces.duckdb
```

### 🆕 Unified Tracing with traced_run()

**New in v0.5.0**: Wrap entire workflows in a single parent trace for better observability!

**The Problem**: By default, each top-level operation (`publish()`, `run_until_idle()`) creates separate root traces, making it difficult to see the complete workflow.

**The Solution**: Use `traced_run()` context manager to group all operations under one parent trace:

```python
# ❌ Before: Separate traces
await flock.publish(pizza_idea)      # ← Trace 1
await flock.run_until_idle()         # ← Trace 2

# ✅ After: Unified trace
async with flock.traced_run("pizza_workflow"):
    await flock.publish(pizza_idea)  # ← Part of "pizza_workflow"
    await flock.run_until_idle()     # ← Part of "pizza_workflow"
```

**Result**: Single trace with proper hierarchy:
```
pizza_workflow (5319ms) ← ROOT
├─ Flock.publish (3ms)
│  └─ Agent.execute (5218ms)
└─ Flock.run_until_idle (5268ms)
```

**Key Features**:
- ✅ Single `trace_id` for entire workflow
- ✅ Proper parent-child span hierarchy
- ✅ Custom workflow names
- ✅ Add custom attributes
- ✅ Nested workflow support
- ✅ 100% backward compatible (opt-in)

**Advanced Usage**:
```python
# Custom attributes
async with flock.traced_run("ml_pipeline") as span:
    span.set_attribute("pipeline.version", "2.0")
    span.set_attribute("dataset.size", 10000)
    await flock.publish(training_data)
    await flock.run_until_idle()

# Nested workflows
async with flock.traced_run("outer_workflow"):
    await flock.publish(data)
    async with flock.traced_run("inner_task"):
        await flock.publish(sub_data)
        await flock.run_until_idle()
    await flock.run_until_idle()
```

📖 **Full documentation**: [docs/UNIFIED_TRACING.md](docs/UNIFIED_TRACING.md)

### 🗑️ Clearing Traces

Clear trace database for fresh debug sessions:

```python
# Clear all traces
result = Flock.clear_traces()
print(f"Deleted {result['deleted_count']} spans")

# Custom database path
result = Flock.clear_traces(".flock/custom_traces.duckdb")

# Check success
if result['success']:
    print("✅ Traces cleared!")
else:
    print(f"❌ Error: {result['error']}")
```

**What it does**:
- Deletes all spans from DuckDB
- Runs VACUUM to reclaim disk space
- Preserves table schema
- Returns detailed operation result

**Use cases**:
- Resetting debug sessions
- Cleaning up test data
- Reducing database file size
- Starting fresh trace analysis

### Filtering: Control What Gets Traced

**Important:** Use filtering to avoid tracing noisy operations (especially streaming tokens) which can cause performance issues.

```bash
# Recommended: Trace only core services
FLOCK_TRACE_SERVICES=["flock", "agent", "dspyengine", "outpututilitycomponent"]

# Exclude specific operations
FLOCK_TRACE_IGNORE=["DashboardEventCollector.set_websocket_manager"]
```

**How Filtering Works (Two-Stage Process):**

1. **Stage 1 (Compile Time):** Metaclasses (`AutoTracedMeta`, `TracedModelMeta`) wrap all public methods
   - Applied to: `Agent`, `Flock`, `AgentComponent` and all subclasses
   - Methods NOT wrapped (plain functions, non-component classes) are never traced

2. **Stage 2 (Runtime):** Filter check before span creation
   - **Whitelist** (`FLOCK_TRACE_SERVICES`): Only trace specified classes (case-insensitive)
   - **Blacklist** (`FLOCK_TRACE_IGNORE`): Never trace specific operations (exact match)
   - Filter check happens **before** span creation → near-zero overhead for filtered ops

**Example:** With `FLOCK_TRACE_SERVICES=["agent", "flock"]`:
- ✅ `Agent.execute` - traced (in whitelist)
- ✅ `Flock.publish` - traced (in whitelist)
- ❌ `DSPyEngine.evaluate` - NOT traced (not in whitelist)
- ❌ `OutputUtilityComponent.on_post_evaluate` - NOT traced (not in whitelist)

📖 **Full filtering documentation:** [docs/AUTO_TRACING.md](docs/AUTO_TRACING.md)

### Why DuckDB for Traces?

**DuckDB is 10-100x faster than SQLite for analytical queries:**
- ✅ **Columnar storage** optimized for OLAP workloads
- ✅ **Zero configuration** - single embedded file, no external services
- ✅ **SQL interface** - AI agents can query traces directly
- ✅ **Out-of-the-box** - no Docker, no complex setup
- ✅ **Analytical queries** - aggregations, p95/p99, time-series analysis

**File-based storage (JSONL) vs DuckDB:**
```python
# ❌ Old way (JSONL)
with open('traces.jsonl') as f:
    for line in f:
        span = json.loads(line)
        if span['duration_ms'] > 1000:  # Manual filtering
            slow_spans.append(span)

# ✅ New way (DuckDB)
conn.execute("""
    SELECT name, AVG(duration_ms), COUNT(*)
    FROM spans
    WHERE duration_ms > 1000
    GROUP BY name
""").fetchall()
```

### Trace Viewer UI

The dashboard includes a **Jaeger-style trace viewer** at `http://localhost:8000`:

**Features:**
- 🔍 **Timeline View**: Waterfall visualization with span hierarchies
- 📊 **Statistics View**: Tabular view with durations, status codes
- 📥 **Full I/O Capture**: Complete input/output data for every operation
- 🎨 **JSON Viewer**: Collapsible, syntax-highlighted with expand/collapse all
- 🎨 **Service Colors**: Visual distinction between services
- 🔀 **Multi-Trace Support**: Compare multiple traces side-by-side

### AI-Powered Debugging

**AI agents (including Claude Code) can analyze traces with SQL:**

```python
import duckdb

conn = duckdb.connect('.flock/traces.duckdb', read_only=True)

# Example 1: Find slow operations
slow_ops = conn.execute("""
    SELECT name, AVG(duration_ms) as avg_ms, COUNT(*) as count
    FROM spans
    WHERE duration_ms > 1000
    GROUP BY name
    ORDER BY avg_ms DESC
""").fetchall()

# Example 2: Find errors with their inputs
errors = conn.execute("""
    SELECT
        name,
        status_description,
        json_extract(attributes, '$.input.message') as input_msg,
        json_extract(attributes, '$.input.artifacts') as input_artifacts
    FROM spans
    WHERE status_code = 'ERROR'
    ORDER BY start_time DESC
""").fetchall()

# Example 3: Performance by service
perf = conn.execute("""
    SELECT
        service,
        COUNT(*) as total_calls,
        AVG(duration_ms) as avg_ms,
        MAX(duration_ms) as max_ms,
        PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY duration_ms) as p95_ms,
        PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY duration_ms) as p99_ms
    FROM spans
    GROUP BY service
    ORDER BY avg_ms DESC
""").fetchall()

# Example 4: Trace full execution path
trace_path = conn.execute("""
    SELECT
        name,
        duration_ms,
        status_code,
        json_extract(attributes, '$.correlation_id') as correlation_id
    FROM spans
    WHERE trace_id = '<trace_id_here>'
    ORDER BY start_time ASC
""").fetchall()

conn.close()
```

### Common Debugging Scenarios

#### Scenario 1: "Agent is slow, why?"

```python
# Find which operations are bottlenecks
import duckdb
conn = duckdb.connect('.flock/traces.duckdb', read_only=True)

# Analyze average duration by operation
result = conn.execute("""
    SELECT
        service,
        operation,
        COUNT(*) as calls,
        AVG(duration_ms) as avg_ms,
        MAX(duration_ms) as max_ms
    FROM spans
    WHERE service = 'DSPyEngine'  -- Focus on LLM engine
    GROUP BY service, operation
    ORDER BY avg_ms DESC
""").fetchall()

print("Slowest operations:")
for row in result:
    print(f"  {row[1]}: {row[3]:.1f}ms avg ({row[2]} calls)")
```

**AI-assisted analysis:**
```
You: "My pizza agent is slow"
AI Agent: [queries DuckDB]
  "Found bottleneck: DSPyEngine.evaluate takes 23s on average.
   Checking input data... You're passing 50KB conversation history.
   Recommendation: Limit context to last 5 messages using:
   agent.consumes(Message, where=lambda m: m.created_at > recent_time)"
```

#### Scenario 2: "Agent crashed, what input caused it?"

```python
# Find the error and its input
conn.execute("""
    SELECT
        name,
        status_description,
        json_extract(attributes, '$.input.message') as input,
        json_extract(attributes, '$.input.artifacts') as artifacts
    FROM spans
    WHERE status_code = 'ERROR'
      AND name LIKE '%pizza_master%'
    ORDER BY start_time DESC
    LIMIT 1
""").fetchone()
```

#### Scenario 3: "Performance regression after code change"

```python
# Compare before/after performance
baseline_start = 1728000000000000000  # nanoseconds
current_start = 1728086400000000000

baseline = conn.execute("""
    SELECT AVG(duration_ms) as avg_ms
    FROM spans
    WHERE start_time BETWEEN ? AND ?
      AND name = 'DSPyEngine.evaluate'
""", [baseline_start, baseline_start + 3600000000000]).fetchone()

current = conn.execute("""
    SELECT AVG(duration_ms) as avg_ms
    FROM spans
    WHERE start_time BETWEEN ? AND ?
      AND name = 'DSPyEngine.evaluate'
""", [current_start, current_start + 3600000000000]).fetchone()

print(f"Baseline: {baseline[0]:.1f}ms")
print(f"Current: {current[0]:.1f}ms")
print(f"Change: {((current[0] / baseline[0]) - 1) * 100:+.1f}%")
```

#### Scenario 4: "Which agents are called most frequently?"

```python
# Agent execution frequency
conn.execute("""
    SELECT
        service,
        COUNT(*) as executions,
        AVG(duration_ms) as avg_duration,
        SUM(CASE WHEN status_code = 'ERROR' THEN 1 ELSE 0 END) as errors
    FROM spans
    WHERE name LIKE '%.execute'  -- Agent execution spans
    GROUP BY service
    ORDER BY executions DESC
""").fetchall()
```

### DuckDB Schema Reference

**Spans Table:**
```sql
CREATE TABLE spans (
    trace_id VARCHAR NOT NULL,           -- Trace identifier
    span_id VARCHAR PRIMARY KEY,         -- Unique span ID
    parent_id VARCHAR,                   -- Parent span ID (for hierarchy)
    name VARCHAR NOT NULL,               -- Operation name (e.g., "Flock.publish")
    service VARCHAR,                     -- Service name (e.g., "Flock")
    operation VARCHAR,                   -- Operation name (e.g., "publish")
    kind VARCHAR,                        -- Span kind (INTERNAL, CLIENT, etc.)
    start_time BIGINT NOT NULL,          -- Start timestamp (nanoseconds)
    end_time BIGINT NOT NULL,            -- End timestamp (nanoseconds)
    duration_ms DOUBLE NOT NULL,         -- Duration in milliseconds
    status_code VARCHAR NOT NULL,        -- Status (OK, ERROR, UNSET)
    status_description VARCHAR,          -- Error message if failed
    attributes JSON,                     -- All span attributes (input/output)
    events JSON,                         -- Span events
    links JSON,                          -- Span links
    resource JSON,                       -- Resource attributes
    created_at TIMESTAMP DEFAULT NOW()  -- Database insert time
);

-- Indexes for fast queries
CREATE INDEX idx_trace_id ON spans(trace_id);
CREATE INDEX idx_service ON spans(service);
CREATE INDEX idx_start_time ON spans(start_time);
CREATE INDEX idx_name ON spans(name);
```

**Attributes JSON Structure:**
```json
{
  "function": "publish",
  "module": "flock.orchestrator",
  "class": "Flock",
  "input.obj": "{\"__class__\": \"MyDreamPizza\", \"pizza_idea\": \"...\"}"
,
  "input.correlation_id": "null",
  "output.value": "{\"__class__\": \"Artifact\", \"id\": \"...\", ...}",
  "output.type": "Artifact",
  "correlation_id": "d1da07d1-9f73-4ff9-ac86-ddea29c8cc06"
}
```

### Advanced Query Examples

**1. Find longest trace execution:**
```sql
SELECT
    trace_id,
    COUNT(*) as span_count,
    MIN(start_time) as trace_start,
    MAX(end_time) as trace_end,
    (MAX(end_time) - MIN(start_time)) / 1000000.0 as total_duration_ms
FROM spans
GROUP BY trace_id
ORDER BY total_duration_ms DESC
LIMIT 10;
```

**2. Analyze input sizes (detect large payloads):**
```sql
SELECT
    name,
    AVG(LENGTH(attributes->>'input.message')) as avg_input_size,
    MAX(LENGTH(attributes->>'input.message')) as max_input_size
FROM spans
WHERE attributes->>'input.message' IS NOT NULL
GROUP BY name
ORDER BY max_input_size DESC;
```

**3. Error patterns by time of day:**
```sql
SELECT
    strftime(TIMESTAMP 'epoch' + (start_time / 1000000000) * INTERVAL '1 second', '%H') as hour,
    COUNT(*) as total_spans,
    SUM(CASE WHEN status_code = 'ERROR' THEN 1 ELSE 0 END) as errors,
    (errors * 100.0 / total_spans) as error_rate
FROM spans
GROUP BY hour
ORDER BY hour;
```

**4. Agent dependency graph (what consumes what):**
```sql
WITH agent_outputs AS (
    SELECT DISTINCT
        service as producer,
        json_extract(attributes, '$.output.type') as artifact_type
    FROM spans
    WHERE attributes->>'output.type' IS NOT NULL
),
agent_inputs AS (
    SELECT DISTINCT
        service as consumer,
        json_extract(attributes, '$.input.artifacts[0].type') as artifact_type
    FROM spans
    WHERE attributes->>'input.artifacts' IS NOT NULL
)
SELECT
    ao.producer,
    ao.artifact_type,
    ai.consumer
FROM agent_outputs ao
JOIN agent_inputs ai ON ao.artifact_type = ai.artifact_type
ORDER BY ao.producer, ai.consumer;
```

### Integration with External Tools

**Export to Parquet for BigQuery/Snowflake:**
```python
import duckdb

conn = duckdb.connect('.flock/traces.duckdb')
conn.execute("""
    COPY (SELECT * FROM spans)
    TO 'traces.parquet' (FORMAT PARQUET)
""")
```

**Send to Grafana Tempo/Jaeger:**
```bash
# Use OTLP exporter instead of DuckDB
export OTEL_EXPORTER_OTLP_ENDPOINT="http://tempo:4317"
export FLOCK_AUTO_TRACE=true
# Don't set FLOCK_TRACE_FILE=true
```

### Performance Tips

1. **Limit trace retention:**
```sql
DELETE FROM spans WHERE start_time < (EXTRACT(EPOCH FROM NOW()) * 1000000000) - 604800000000000;  -- Keep 7 days
```

2. **Vacuum database periodically:**
```python
conn.execute("VACUUM")
```

3. **Use read-only connections for queries:**
```python
conn = duckdb.connect('.flock/traces.duckdb', read_only=True)
```

### Debugging Checklist

When debugging a Flock application:

- [ ] **Enable tracing:** `FLOCK_AUTO_TRACE=true FLOCK_TRACE_FILE=true`
- [ ] **Reproduce the issue** to capture trace data
- [ ] **Open trace viewer:** Visit `http://localhost:8000` → Trace Viewer tab
- [ ] **Check Timeline View:** Look for unusually long spans
- [ ] **Check Statistics View:** Sort by duration to find bottlenecks
- [ ] **Examine I/O data:** Expand JSON attributes to see full inputs/outputs
- [ ] **Query DuckDB:** Use SQL for deeper analysis
- [ ] **Ask AI agent:** Provide trace_id and description, let AI query and diagnose

---

## ❓ FAQ for AI Agents

### Q: Where should I save new files?

**⚠️ CRITICAL: NEVER save files to the project root directory!**

Always use the appropriate subdirectory:
- **Tests**: `/tests` - All test files only
- **Source Code**: `/src/flock` - Production code only
- **Documentation**: `/docs` - Documentation only
- **Examples**: `/examples` - Example scripts only
- **Frontend**: `/frontend/src` - React components and frontend code

**Never create files in the root directory** - it should only contain configuration files like `pyproject.toml`, `README.md`, etc.

### Q: How do I add a new dependency?

```bash
# Python
uv add package-name

# Frontend
cd frontend && npm install package-name
```

### Q: How do I run a specific test?

```bash
# Backend
uv run pytest tests/test_file.py::test_name -v

# Frontend
cd frontend && npm test -- test_name
```

### Q: How do I start the dashboard?

```python
await orchestrator.serve(dashboard=True)
```

### Q: How do I use unified tracing?

```python
# Wrap workflows in a single trace
async with flock.traced_run("workflow_name"):
    await flock.publish(data)
    await flock.run_until_idle()

# Clear traces for fresh debug session
result = Flock.clear_traces()
```

See [docs/UNIFIED_TRACING.md](docs/UNIFIED_TRACING.md) for complete guide.

### Q: Where should I add new tests?

Add to existing test file if relevant, or create new file following naming convention `test_<module>.py` for backend, `<name>.test.tsx` for frontend.

### Q: What Python version features can I use?

Python 3.10+, so you can use:
- `match`/`case` statements
- `TaskGroup` for parallel execution
- Improved type hints (`list[str]` not `List[str]`)

### Q: How do I debug WebSocket issues?

Check browser console for WebSocket logs, use Network tab to inspect connection, and verify backend WebSocket server is running on correct port.

---

## 🎯 Quick Reference

### Essential Commands

```bash
# Setup
poe install          # Install all dependencies

# Development
poe build           # Build project
poe lint            # Lint code
poe format          # Format code

# Testing
poe test            # Run tests
poe test-cov        # Run with coverage
poe test-critical   # Run critical path tests

# Frontend
cd frontend
npm run dev         # Start dev server
npm test            # Run frontend tests
npm run build       # Build for production
```

### Code Snippets

**Create orchestrator:**
```python
from flock.orchestrator import Flock
orchestrator = Flock("openai/gpt-4o")
```

**Define agent:**
```python
agent = (
    orchestrator.agent("name")
    .description("What it does")
    .consumes(InputType)
    .publishes(OutputType)
)
```

**Run agent:**
```python
await orchestrator.arun(agent, input_data)
```

**Unified tracing:**
```python
# Wrap workflow in single trace
async with flock.traced_run("workflow_name"):
    await flock.publish(data)
    await flock.run_until_idle()

# Clear traces
Flock.clear_traces()
```

**Start dashboard:**
```python
await orchestrator.serve(dashboard=True)
```

---

## 📞 Getting Help

### Documentation
- **Contributing Guide:** [`CONTRIBUTING.md`](CONTRIBUTING.md) - Complete contribution workflow
- **Development Workflow:** [`docs/patterns/development-workflow.md`](docs/patterns/development-workflow.md)
- **Versioning Guide:** [`docs/VERSIONING.md`](docs/VERSIONING.md) - Smart version bumping
- **Pre-commit Hooks:** [`docs/PRE_COMMIT_HOOKS.md`](docs/PRE_COMMIT_HOOKS.md) - Quality automation
- **Architecture:** [`docs/patterns/core-architecture.md`](docs/patterns/core-architecture.md)
- **Unified Tracing:** [`docs/UNIFIED_TRACING.md`](docs/UNIFIED_TRACING.md) - Workflow tracing & trace management
- **Examples:** [`examples/`](examples/) - Working code examples
- **Analysis Documents:** [`docs/patterns/`](docs/patterns/)

---

**Welcome to the team! Let's build the future of AI agent orchestration together.** 🚀

---

*Last updated: October 7, 2025*
*This file follows the modern AGENTS.md format for AI coding agents.*
