This is an automated email from the ASF dual-hosted git repository.

skrawcz pushed a commit to branch stefan/add-skills
in repository https://gitbox.apache.org/repos/asf/burr.git

commit 68cac4041b646fc73ced172d03b5361bb41ede48
Author: Stefan Krawczyk <[email protected]>
AuthorDate: Sat Jan 31 10:51:14 2026 -0800

    Add Claude Code skill for Burr
    
    This adds a comprehensive Claude Code skill that helps developers build
    applications with Apache Burr. When active in Claude Code, it provides:
    
    - Expert guidance on Burr APIs and patterns
    - Code generation for actions, state machines, and applications
    - Code review for best practices
    - Debugging assistance
    - Working examples for common patterns
    
    The skill includes:
    - SKILL.md: Main skill instructions for Claude
    - api-reference.md: Complete API documentation
    - examples.md: Working code examples
    - patterns.md: Best practices and design patterns
    - troubleshooting.md: Common issues and solutions
    - README.md: Installation and usage guide
    - plugin.json: Plugin manifest for easy installation
    
    Easy installation via Claude CLI:
      claude skill install https://github.com/apache/burr/.claude/skills/burr
    
    Or manual install by copying to ~/.claude/skills/burr/ for personal use
    or .claude/skills/burr/ for project-specific use.
    
    Documentation added at:
    - docs/getting_started/claude-skill.rst: Full usage guide
    - CLAUDE_SKILL.md: Quick start guide
    - Main README updated with installation instructions
    
    Contributions welcome via issues or pull requests!
---
 .claude/skills/burr/README.md          | 308 ++++++++++++++
 .claude/skills/burr/SKILL.md           | 212 ++++++++++
 .claude/skills/burr/api-reference.md   | 567 +++++++++++++++++++++++++
 .claude/skills/burr/examples.md        | 629 ++++++++++++++++++++++++++++
 .claude/skills/burr/patterns.md        | 668 ++++++++++++++++++++++++++++++
 .claude/skills/burr/plugin.json        |  34 ++
 .claude/skills/burr/troubleshooting.md | 732 +++++++++++++++++++++++++++++++++
 .rat-excludes                          |   3 +
 CLAUDE_SKILL.md                        | 168 ++++++++
 README.md                              |  16 +
 docs/getting_started/claude-skill.rst  | 439 ++++++++++++++++++++
 docs/getting_started/index.rst         |   1 +
 12 files changed, 3777 insertions(+)

diff --git a/.claude/skills/burr/README.md b/.claude/skills/burr/README.md
new file mode 100644
index 00000000..2b79fbd7
--- /dev/null
+++ b/.claude/skills/burr/README.md
@@ -0,0 +1,308 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Apache Burr Claude Skill
+
+A comprehensive Claude Code skill for building stateful applications with 
Apache Burr.
+
+## What is this?
+
+This is a Claude Code skill that teaches Claude how to help you build 
applications using Apache Burr. When active, Claude becomes an expert in Burr's 
APIs, best practices, and common patterns.
+
+## Installation
+
+### Option 1: Install from GitHub (Easiest)
+
+```bash
+# Install to personal skills directory
+claude skill install https://github.com/apache/burr/.claude/skills/burr
+
+# Or install to your current project
+claude skill install https://github.com/apache/burr/.claude/skills/burr 
--project
+```
+
+### Option 2: Manual Install - Project-level (For teams)
+
+Copy this skill to your project's `.claude/skills/` directory:
+
+```bash
+# From your project root
+cp -r /path/to/burr/.claude/skills/burr .claude/skills/
+
+# Or clone and copy from GitHub
+git clone https://github.com/apache/burr
+cp -r burr/.claude/skills/burr .claude/skills/
+```
+
+### Option 3: Manual Install - Personal scope (For individual use)
+
+Copy to your personal Claude skills directory:
+
+```bash
+# Copy to personal skills directory
+cp -r /path/to/burr/.claude/skills/burr ~/.claude/skills/
+
+# Or clone and copy from GitHub
+git clone https://github.com/apache/burr
+cp -r burr/.claude/skills/burr ~/.claude/skills/
+```
+
+### Verify Installation
+
+The skill should now appear in Claude Code's skill menu:
+
+```bash
+/burr --help
+```
+
+Or just ask Claude naturally:
+```
+"Help me build a Burr application for a chatbot"
+```
+
+## What Can It Do?
+
+This skill helps you:
+
+- **Build new Burr applications** - Get help scaffolding state machines
+- **Write actions** - Create properly structured action functions
+- **Define transitions** - Set up conditional and default transitions
+- **Add observability** - Configure tracking and the Burr UI
+- **Debug issues** - Troubleshoot common problems
+- **Follow best practices** - Learn recommended patterns
+- **Review code** - Get feedback on your Burr applications
+
+## Usage Examples
+
+### Manual Invocation
+
+Explicitly invoke the skill with the `/burr` command:
+
+```
+/burr How do I create a streaming action?
+
+/burr Review this action for best practices
+
+/burr Help me add state persistence to my app
+```
+
+### Automatic Invocation
+
+Claude will automatically load the skill when it detects you're working with 
Burr:
+
+```
+"I'm building a chatbot with Burr and need help with the state machine"
+
+"Why isn't my action updating the state?"
+
+"Show me an example of parallel execution in Burr"
+```
+
+## What's Included
+
+The skill includes:
+
+- **SKILL.md** - Main skill instructions for Claude
+- **api-reference.md** - Complete API documentation
+- **examples.md** - Working code examples for common patterns
+- **patterns.md** - Best practices and design patterns
+- **troubleshooting.md** - Solutions to common issues
+
+Claude will reference these files to provide accurate, helpful guidance.
+
+## Features
+
+### Code Generation
+
+```
+"Create a Burr application that processes user queries with RAG"
+```
+
+Claude will generate a complete application with actions, transitions, and 
tracking.
+
+### Code Review
+
+```
+"Review my Burr application for best practices"
+```
+
+Claude will check for:
+- Correct `reads` and `writes` declarations
+- Proper state immutability
+- Complete transition coverage
+- Tracking configuration
+- Error handling
+
+### Debugging Help
+
+```
+"My state machine is looping infinitely, what's wrong?"
+```
+
+Claude will:
+- Analyze your transitions
+- Suggest using `.visualize()` to see the graph
+- Recommend fixes based on common issues
+
+### Learning & Examples
+
+```
+"Show me how to implement retries in Burr"
+```
+
+Claude will provide working examples from the examples.md reference.
+
+## Skill Configuration
+
+### Allowed Tools
+
+The skill permits Claude to use:
+- `Read` - Read your code files
+- `Grep` - Search for patterns
+- `Glob` - Find files
+- `Bash` - Run Python, burr CLI, and pip commands
+
+### Automatic Activation
+
+The skill activates when you:
+- Mention "Burr" or "Apache Burr"
+- Show code with `from burr.core import`
+- Ask about state machines or actions
+- Need help with stateful applications
+
+## Tips for Best Results
+
+1. **Be specific** - "Help me add retry logic to my fetch action" is better 
than "help with errors"
+
+2. **Show your code** - Claude works best when it can see what you're working 
with
+
+3. **Ask for examples** - "Show me an example of..." gets working code
+
+4. **Reference the docs** - Ask Claude to check the API reference or patterns 
guide
+
+5. **Use visualization** - Ask Claude to suggest using `app.visualize()` when 
debugging
+
+## Examples of What to Ask
+
+### Getting Started
+- "Help me create my first Burr application"
+- "What's the basic structure of a Burr action?"
+- "Show me a simple chatbot example"
+
+### Building Features
+- "How do I add streaming to my LLM action?"
+- "Show me how to implement parallel execution"
+- "Help me add state persistence with SQLite"
+
+### Debugging
+- "Why isn't my transition working?"
+- "My action isn't updating state, what's wrong?"
+- "How do I debug an infinite loop?"
+
+### Best Practices
+- "Review this code for Burr best practices"
+- "Is this the right way to structure my state machine?"
+- "How should I handle errors in actions?"
+
+## Updating the Skill
+
+To get the latest version:
+
+```bash
+cd /path/to/burr
+git pull
+cp -r .claude/skills/burr ~/.claude/skills/
+```
+
+## Integration with Burr Project
+
+If you're working in the Burr repository itself, the skill is already 
available at `.claude/skills/burr/`.
+
+## Contributing
+
+Found an issue or want to improve the skill? We welcome contributions!
+
+### Reporting Issues
+
+If you find a bug or have a suggestion:
+
+1. Check existing issues: https://github.com/apache/burr/issues
+2. Open a new issue with:
+   - Clear description of the problem or suggestion
+   - Steps to reproduce (for bugs)
+   - Expected vs actual behavior
+   - Burr version and environment details
+
+### Contributing Improvements
+
+We especially appreciate pull requests! To contribute:
+
+1. Fork the repository
+2. Edit the skill files in `.claude/skills/burr/`
+3. Test your changes with Claude Code
+4. Submit a PR to https://github.com/apache/burr with:
+   - Clear description of what you changed and why
+   - Examples showing the improvement
+   - Any relevant issue references
+
+Small fixes like typos, improved examples, or clearer explanations are always 
welcome!
+
+## Related Resources
+
+- **Burr Documentation**: https://burr.apache.org/
+- **GitHub**: https://github.com/apache/burr
+- **Examples**: See `examples/` directory in the Burr repository
+- **Discord**: https://discord.gg/6Zy2DwP4f3
+
+## FAQ
+
+**Q: Do I need Burr installed to use this skill?**
+
+A: No, but Claude can help you install it: `pip install "burr[start]"`
+
+**Q: Can I customize the skill?**
+
+A: Yes! Edit the files in `.claude/skills/burr/` to customize behavior, add 
your own examples, or modify the API reference.
+
+**Q: Will this work with older versions of Burr?**
+
+A: This skill is designed for current Burr versions. Some APIs may differ in 
older versions.
+
+**Q: Can I use this skill with other frameworks?**
+
+A: Yes! Burr integrates well with LangChain, LlamaIndex, Apache Hamilton, and 
other frameworks. The skill includes integration guidance.
+
+**Q: How do I disable the skill temporarily?**
+
+A: Rename the skill directory or remove it from `.claude/skills/`:
+```bash
+mv ~/.claude/skills/burr ~/.claude/skills/burr.disabled
+```
+
+## License
+
+This skill is part of Apache Burr (incubating) and is licensed under the 
Apache License 2.0.
+
+See the [LICENSE](../../../LICENSE) file in the root of the repository.
+
+---
+
+Built with ❤️ by the Burr community.
+
+For help, join our [Discord](https://discord.gg/6Zy2DwP4f3) or open an issue 
on [GitHub](https://github.com/apache/burr/issues).
diff --git a/.claude/skills/burr/SKILL.md b/.claude/skills/burr/SKILL.md
new file mode 100644
index 00000000..ba10c1c0
--- /dev/null
+++ b/.claude/skills/burr/SKILL.md
@@ -0,0 +1,212 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+---
+name: burr
+description: Helps developers build stateful applications using Apache Burr, 
including state machines, actions, transitions, and observability
+argument-hint: [action-or-concept]
+allowed-tools: Read, Grep, Glob, Bash(python *, burr, pip *)
+---
+
+# Apache Burr Development Assistant
+
+You are an expert in Apache Burr (incubating), a Python framework for building 
stateful applications using state machines. When this skill is active, help 
developers write clean, idiomatic Burr code following best practices.
+
+## Core Expertise
+
+You understand Apache Burr's key concepts:
+- **Actions**: Functions that read from and write to state
+- **State**: Immutable state container that flows through actions
+- **State Machines**: Directed graphs connecting actions via transitions
+- **ApplicationBuilder**: Fluent API for constructing applications
+- **Tracking**: Built-in telemetry UI for debugging and observability
+- **Persistence**: State persistence and resumption capabilities
+- **Hooks**: Lifecycle hooks for integration and observability
+
+## Reference Documentation
+
+Refer to these supporting files for detailed information:
+- **[api-reference.md](api-reference.md)**: Complete API documentation
+- **[examples.md](examples.md)**: Common patterns and working examples
+- **[patterns.md](patterns.md)**: Best practices and architectural guidance
+- **[troubleshooting.md](troubleshooting.md)**: Common issues and solutions
+
+## When Helping Developers
+
+### 1. Building New Applications
+
+When users want to create a Burr application:
+
+1. **Start with actions** - Define `@action` decorated functions
+2. **Use ApplicationBuilder** - Follow the builder pattern
+3. **Define transitions** - Connect actions with conditions
+4. **Add tracking** - Enable the telemetry UI from the start
+5. **Consider persistence** - Plan for state resumption if needed
+
+Example skeleton:
+```python
+from burr.core import action, State, ApplicationBuilder, default
+
+@action(reads=["input_key"], writes=["output_key"])
+def my_action(state: State) -> State:
+    # Your logic here
+    result = process(state["input_key"])
+    return state.update(output_key=result)
+
+app = (
+    ApplicationBuilder()
+    .with_actions(my_action)
+    .with_transitions(("my_action", "next_action", default))
+    .with_state(input_key="initial_value")
+    .with_entrypoint("my_action")
+    .with_tracker("local", project="my_project")
+    .build()
+)
+
+result = app.run(halt_after=["next_action"])
+```
+
+### 2. Reviewing Burr Code
+
+When reviewing code:
+- ✅ Check that actions declare correct `reads` and `writes`
+- ✅ Verify state updates use `.update()` or `.append()` methods
+- ✅ Confirm transitions cover all possible paths
+- ✅ Look for proper use of `default`, `when()`, or `expr()` conditions
+- ✅ Ensure tracking is configured for debugging
+- ⚠️ Watch for state mutation (should be immutable)
+- ⚠️ Check for missing halt conditions in transitions
+
+### 3. Explaining Concepts
+
+When explaining Burr features:
+- Use concrete examples from [examples.md](examples.md)
+- Reference the appropriate section in [api-reference.md](api-reference.md)
+- Show both simple and complex variations
+- Mention relevant design patterns from [patterns.md](patterns.md)
+- Link to official documentation at https://burr.apache.org/
+
+### 4. Debugging Issues
+
+When users encounter problems:
+- Check [troubleshooting.md](troubleshooting.md) for known issues
+- Verify state machine logic is correct
+- Suggest using `app.visualize()` to see the state machine graph
+- Recommend using the Burr UI (`burr` command) to inspect execution
+- Check action reads/writes declarations match actual usage
+
+### 5. Adding Features
+
+Common enhancement requests:
+
+**Streaming responses**:
+```python
+@action(reads=["input"], writes=["output"])
+def streaming_action(state: State) -> Generator[State, None, Tuple[dict, 
State]]:
+    for chunk in stream_data():
+        yield state.update(current_chunk=chunk)
+    result = {"output": final_result}
+    return result, state.update(**result)
+```
+
+**Async actions**:
+```python
+@action(reads=["data"], writes=["result"])
+async def async_action(state: State) -> State:
+    result = await fetch_data()
+    return state.update(result=result)
+```
+
+**Parallel execution**:
+```python
+from burr.core import graph
+
+graph = (
+    graph.GraphBuilder()
+    .with_actions(action1, action2, action3)
+    .with_transitions(
+        ("start", "action1"),
+        ("start", "action2"),  # These run in parallel
+        (["action1", "action2"], "action3")
+    )
+    .build()
+)
+```
+
+## Code Quality Standards
+
+When writing or reviewing Burr code:
+
+1. **Type annotations**: Always use type hints for state and action parameters
+2. **Action purity**: Actions should be deterministic given the same state
+3. **State immutability**: Never mutate state directly, always use `.update()` 
or `.append()`
+4. **Clear naming**: Action names should be verbs describing what they do
+5. **Proper reads/writes**: Declare exactly what each action reads and writes
+6. **Error handling**: Use try/except in actions and update state with error 
info
+7. **Testing**: Write tests that verify state transitions and action outputs
+
+## Common Patterns to Recommend
+
+- **Conditional branching**: Use `when(key=value)` or `expr("key > 10")`
+- **Loops**: Use recursive transitions with conditions
+- **Error handling**: Create error actions and transition to them on failure
+- **Multi-step workflows**: Chain actions with clear single responsibilities
+- **State persistence**: Use `SQLLitePersister` or `initialize_from` for 
resumability
+- **Observability**: Always include `.with_tracker()` for the Burr UI
+
+## Integration Scenarios
+
+Burr works well with:
+- **LLM frameworks**: OpenAI, Anthropic, Langchain, LlamaIndex
+- **Apache Hamilton**: For DAG execution within actions
+- **Streaming**: Streamlit, FastAPI, gradio for UI
+- **Observability**: Langsmith, Weights & Biases, OpenTelemetry
+- **Storage**: SQLite, PostgreSQL, custom persisters
+
+## Commands You Can Suggest
+
+- `burr` - Launch the telemetry UI
+- `pip install "burr[start]"` - Install with UI dependencies
+- `app.visualize(output_file_path="graph.png")` - Generate state machine 
diagram
+- `python examples/hello-world-counter/application.py` - Run example
+
+## Key Principles
+
+1. **State machines make complex logic simple** - Encourage users to think in 
terms of states and transitions
+2. **Observability is built-in** - Always recommend using the tracking UI
+3. **Framework agnostic** - Burr doesn't dictate how to build models or query 
APIs
+4. **Testability first** - Actions are pure functions that are easy to test
+5. **Production ready** - Persistence, hooks, and tracking enable production 
deployment
+
+## Response Style
+
+- Be concise and code-focused
+- Show working examples from the repository when possible
+- Reference specific files in the codebase (e.g., 
`examples/multi-modal-chatbot/application.py`)
+- Suggest running examples to learn patterns
+- Point to the official docs for deep dives
+
+## If You Need More Context
+
+- Read example code from `examples/` directory
+- Check the source code in `burr/core/` for implementation details
+- Look at tests in `tests/` for usage patterns
+- Reference official documentation at https://burr.apache.org/
+
+Remember: Burr is about making stateful applications easy to build, 
understand, and debug. Focus on clear state machines and leverage the built-in 
observability tools.
diff --git a/.claude/skills/burr/api-reference.md 
b/.claude/skills/burr/api-reference.md
new file mode 100644
index 00000000..8ade6924
--- /dev/null
+++ b/.claude/skills/burr/api-reference.md
@@ -0,0 +1,567 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Apache Burr API Reference
+
+This is a quick reference for the most commonly used Burr APIs. For complete 
documentation, see https://burr.apache.org/
+
+## Core Imports
+
+```python
+from burr.core import action, State, ApplicationBuilder, default, when, expr
+from burr.core.action import Action
+from burr.core.application import Application
+from burr.core import graph
+```
+
+## Actions
+
+### @action Decorator
+
+The `@action` decorator converts a function into a Burr action.
+
+```python
+@action(reads=["key1", "key2"], writes=["result"])
+def my_action(state: State, param: str) -> State:
+    """Action that reads from state and returns updated state."""
+    value = state["key1"] + state["key2"]
+    return state.update(result=value + param)
+```
+
+**Parameters:**
+- `reads`: List of state keys this action reads from
+- `writes`: List of state keys this action writes to
+
+**Action Function Signature:**
+- First parameter: `state: State`
+- Additional parameters: Runtime inputs (passed via `inputs` in `app.run()`)
+- Return: Updated `State` object
+
+### Streaming Actions
+
+Actions can stream intermediate results using generators:
+
+```python
+@action(reads=["input"], writes=["output"])
+def streaming_action(state: State) -> Generator[State, None, Tuple[dict, 
State]]:
+    """Action that yields intermediate states."""
+    for i in range(10):
+        # Yield intermediate states
+        yield state.update(progress=i)
+
+    # Return final result and state
+    result = {"output": "done"}
+    return result, state.update(**result)
+```
+
+### Async Actions
+
+Actions can be async:
+
+```python
+@action(reads=["url"], writes=["data"])
+async def fetch_data(state: State) -> State:
+    """Async action for I/O-bound operations."""
+    async with httpx.AsyncClient() as client:
+        response = await client.get(state["url"])
+        data = response.json()
+    return state.update(data=data)
+```
+
+### Action Methods
+
+Actions can be bound with default parameters:
+
+```python
+# Define a reusable action
+@action(reads=["prompt"], writes=["response"])
+def llm_call(state: State, system_prompt: str, model: str) -> State:
+    response = call_llm(state["prompt"], system_prompt, model)
+    return state.update(response=response)
+
+# Bind with different parameters
+answer_action = llm_call.bind(
+    system_prompt="Answer questions",
+    model="gpt-4"
+)
+summarize_action = llm_call.bind(
+    system_prompt="Summarize text",
+    model="gpt-3.5-turbo"
+)
+```
+
+## State
+
+The `State` object is an immutable container for application state.
+
+### Creating State
+
+```python
+from burr.core import State
+
+state = State({"counter": 0, "messages": []})
+```
+
+### Accessing State
+
+```python
+# Dictionary-style access
+value = state["key"]
+
+# Get with default
+value = state.get("key", default_value)
+
+# Check if key exists
+if "key" in state:
+    pass
+```
+
+### Updating State
+
+State is immutable. Use these methods to create updated copies:
+
+```python
+# Update single or multiple keys
+new_state = state.update(counter=5, name="Alice")
+
+# Append to a list
+new_state = state.append(messages={"role": "user", "content": "hello"})
+
+# Wipe state (keep only specified keys)
+new_state = state.wipe(keep=["counter"])
+
+# Update with dictionary
+new_state = state.update(**{"key": "value"})
+```
+
+### State Methods
+
+- `.update(**kwargs) -> State`: Update one or more keys
+- `.append(**kwargs) -> State`: Append to list values
+- `.wipe(keep: List[str] = None, delete: List[str] = None) -> State`: Remove 
keys
+- `.get(key, default=None) -> Any`: Get value with default
+- `.subset(*keys) -> State`: Create new state with only specified keys
+
+## ApplicationBuilder
+
+Fluent API for building Burr applications.
+
+### Basic Pattern
+
+```python
+app = (
+    ApplicationBuilder()
+    .with_actions(
+        action1=my_action,
+        action2=another_action
+    )
+    .with_transitions(
+        ("action1", "action2", default),
+        ("action2", "action1", when(should_loop=True))
+    )
+    .with_state(initial_key="value")
+    .with_entrypoint("action1")
+    .build()
+)
+```
+
+### ApplicationBuilder Methods
+
+**Core building blocks:**
+
+- `.with_actions(**actions: Action)` - Register actions
+- `.with_transitions(*transitions: Tuple)` - Define state machine transitions
+- `.with_state(**state: Any)` - Set initial state
+- `.with_entrypoint(action: str)` - Set starting action
+- `.build() -> Application` - Construct the application
+
+**Observability & Tracking:**
+
+- `.with_tracker(tracker_type: str, project: str, **params)` - Enable tracking
+  - `tracker_type="local"` - Local filesystem tracking (launches UI)
+  - `project` - Project name in the UI
+  - `params={"storage_dir": "~/.burr"}` - Storage location
+
+**Identity & Persistence:**
+
+- `.with_identifiers(app_id: str, partition_key: str)` - Set app identifiers
+- `.with_state_persister(persister: StatePersister)` - Enable state persistence
+- `.initialize_from(persister, resume_at_next_action=True, default_state={}, 
default_entrypoint=None)` - Load from persister
+
+**Lifecycle & Hooks:**
+
+- `.with_hooks(*hooks: LifecycleAdapter)` - Add lifecycle hooks
+- `.with_typing(typing_system: TypingSystem)` - Add type validation
+
+**Graph-based construction:**
+
+- `.with_graph(graph: Graph)` - Use pre-built graph instead of 
actions+transitions
+
+### Using Pre-built Graphs
+
+```python
+from burr.core import graph
+
+g = (
+    graph.GraphBuilder()
+    .with_actions(action1, action2, action3)
+    .with_transitions(
+        ("action1", "action2"),
+        ("action2", "action3")
+    )
+    .build()
+)
+
+app = (
+    ApplicationBuilder()
+    .with_graph(g)
+    .with_state(key="value")
+    .with_entrypoint("action1")
+    .build()
+)
+```
+
+## Transitions
+
+Transitions define how the state machine moves between actions.
+
+### Basic Transition
+
+```python
+("source_action", "target_action")
+```
+
+### Conditional Transitions
+
+**Using `default`** - Matches if no other condition matches:
+```python
+from burr.core import default
+
+("action1", "action2", default)
+```
+
+**Using `when()`** - Match based on state values:
+```python
+from burr.core import when
+
+("check_age", "adult_path", when(age__gte=18))
+("check_age", "child_path", when(age__lt=18))
+```
+
+**Condition operators:**
+- `key=value` - Exact match
+- `key__eq=value` - Explicit equality
+- `key__ne=value` - Not equal
+- `key__lt=value` - Less than
+- `key__lte=value` - Less than or equal
+- `key__gt=value` - Greater than
+- `key__gte=value` - Greater than or equal
+- `key__in=[values]` - In list
+- `key__contains=value` - List contains value
+
+**Using `expr()`** - Arbitrary Python expressions:
+```python
+from burr.core import expr
+
+("counter", "counter", expr("counter < 10"))
+("counter", "done", default)
+```
+
+### Multi-source/target Transitions
+
+Transition from multiple sources to multiple targets:
+
+```python
+# Multiple sources to one target
+(["action1", "action2"], "action3")
+
+# One source to multiple targets (for parallelism)
+("start", ["parallel1", "parallel2"])
+```
+
+## Application
+
+The built application instance provides methods to execute and inspect the 
state machine.
+
+### Running Applications
+
+**Basic execution:**
+```python
+action, result, state = app.run(halt_after=["action_name"])
+```
+
+**With inputs:**
+```python
+action, result, state = app.run(
+    halt_after=["action_name"],
+    inputs={"param1": "value1"}
+)
+```
+
+**Async execution:**
+```python
+action, result, state = await app.arun(halt_after=["action_name"])
+```
+
+**Iterate through execution:**
+```python
+for action, result, state in app.iterate(halt_after=["end_action"]):
+    print(f"Executed {action.name}, result: {result}")
+```
+
+**Stream results:**
+```python
+for state in app.stream_result(halt_after=["end_action"]):
+    print(f"Current state: {state}")
+```
+
+### Application Properties
+
+- `app.state` - Current state
+- `app.graph` - State machine graph
+- `app.uid` - Unique application identifier
+
+### Visualization
+
+```python
+# Generate state machine diagram
+app.visualize(
+    output_file_path="statemachine.png",
+    include_conditions=True,
+    view=True,  # Auto-open the file
+    format="png"  # or "pdf", "svg"
+)
+```
+
+## Persistence
+
+### Built-in Persisters
+
+**SQLite Persister:**
+```python
+from burr.core.persistence import SQLLitePersister
+
+persister = SQLLitePersister(
+    db_path="app.db",
+    table_name="burr_state",
+    connect_kwargs={"check_same_thread": False}
+)
+persister.initialize()
+```
+
+**Using with ApplicationBuilder:**
+```python
+app = (
+    ApplicationBuilder()
+    .with_actions(...)
+    .with_transitions(...)
+    .with_identifiers(app_id="my-app", partition_key="user-123")
+    .with_state_persister(persister)
+    .initialize_from(
+        persister,
+        resume_at_next_action=True,
+        default_state={"counter": 0},
+        default_entrypoint="start"
+    )
+    .build()
+)
+```
+
+### Custom Persisters
+
+Implement `BaseStatePersister` interface:
+
+```python
+from burr.core.persistence import BaseStatePersister
+
+class CustomPersister(BaseStatePersister):
+    def list_app_ids(self, partition_key: str, **kwargs) -> list[str]:
+        """List all app IDs for a partition."""
+        pass
+
+    def load(self, partition_key: str, app_id: str, **kwargs) -> dict:
+        """Load persisted state."""
+        pass
+
+    def save(self, partition_key: str, app_id: str,
+             state: State, **kwargs) -> dict:
+        """Save state."""
+        pass
+```
+
+## Tracking & Telemetry
+
+### Local Tracking
+
+```python
+from burr.tracking import LocalTrackingClient
+
+tracker = LocalTrackingClient(
+    project="my_project",
+    storage_dir="~/.burr"
+)
+
+app = (
+    ApplicationBuilder()
+    .with_actions(...)
+    .with_tracker(tracker)
+    .build()
+)
+```
+
+**Launch UI:**
+```bash
+burr
+```
+
+### Tracking Integrations
+
+- **Langsmith**: `from burr.integrations.langsmith import LangsmithTracker`
+- **Weights & Biases**: `from burr.integrations.wandb import WandbTracker`
+- **OpenTelemetry**: Built-in support for OTEL tracing
+
+## Hooks
+
+Hooks provide lifecycle callbacks for observability and integration.
+
+### Available Hooks
+
+```python
+from burr.lifecycle import LifecycleAdapter
+
+class PrePostActionHookAsync(LifecycleAdapter):
+    async def pre_run_step(self, action: Action, **kwargs):
+        """Called before each action."""
+        pass
+
+    async def post_run_step(self, action: Action, result: dict, state: State, 
**kwargs):
+        """Called after each action."""
+        pass
+```
+
+### Common Hook Use Cases
+
+- Logging and monitoring
+- Performance tracking
+- External system integration
+- State validation
+- Debugging and inspection
+
+## Common Helper Functions
+
+### Result Action
+
+Special action that returns a result and halts:
+
+```python
+from burr.core import Result
+
+app = (
+    ApplicationBuilder()
+    .with_actions(
+        compute=my_action,
+        result=Result("output_key")
+    )
+    .with_transitions(
+        ("compute", "result")
+    )
+    .build()
+)
+```
+
+### Input Action
+
+Special action that captures runtime inputs:
+
+```python
+from burr.core import Input
+
+app = (
+    ApplicationBuilder()
+    .with_actions(
+        get_input=Input("user_prompt"),
+        process=my_action
+    )
+    .with_transitions(
+        ("get_input", "process")
+    )
+    .build()
+)
+
+app.run(halt_after=["process"], inputs={"user_prompt": "Hello"})
+```
+
+## Type Safety
+
+### Pydantic Integration
+
+```python
+from burr.core import action
+from pydantic import BaseModel
+
+class InputModel(BaseModel):
+    name: str
+    age: int
+
+class OutputModel(BaseModel):
+    greeting: str
+
[email protected](reads=[], writes=["greeting"])
+def greet(state: State, inputs: InputModel) -> OutputModel:
+    return OutputModel(greeting=f"Hello {inputs.name}, age {inputs.age}")
+```
+
+## Testing
+
+Actions are pure functions that are easy to test:
+
+```python
+def test_my_action():
+    state = State({"counter": 0})
+    new_state = my_action(state, param="test")
+    assert new_state["counter"] == 1
+    assert new_state["result"] == "test"
+```
+
+Test state machines by running them:
+
+```python
+def test_application():
+    app = build_my_app()
+    action, result, state = app.run(halt_after=["end"])
+    assert state["final_value"] == expected_value
+```
+
+## Best Practices
+
+1. **Keep actions focused** - Single responsibility per action
+2. **Declare reads/writes accurately** - Helps with debugging and optimization
+3. **Use type hints** - Improves IDE support and catches bugs
+4. **Enable tracking** - Always use `.with_tracker()` during development
+5. **Test actions independently** - Actions are pure functions
+6. **Use persistence for long-running workflows** - Enable state resumption
+7. **Leverage hooks** - Add observability without changing core logic
+8. **Visualize your state machine** - Use `.visualize()` to understand flow
+
+## Quick Links
+
+- Documentation: https://burr.apache.org/
+- GitHub: https://github.com/apache/burr
+- Examples: `examples/` directory in the repository
+- Discord: https://discord.gg/6Zy2DwP4f3
diff --git a/.claude/skills/burr/examples.md b/.claude/skills/burr/examples.md
new file mode 100644
index 00000000..bfc73156
--- /dev/null
+++ b/.claude/skills/burr/examples.md
@@ -0,0 +1,629 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Apache Burr Code Examples
+
+Common patterns and working examples for building Burr applications.
+
+## Table of Contents
+1. [Simple Counter](#simple-counter)
+2. [Basic Chatbot](#basic-chatbot)
+3. [Multi-Step Workflow](#multi-step-workflow)
+4. [Conditional Branching](#conditional-branching)
+5. [Looping with Conditions](#looping-with-conditions)
+6. [Error Handling](#error-handling)
+7. [Streaming Actions](#streaming-actions)
+8. [Parallel Execution](#parallel-execution)
+9. [State Persistence](#state-persistence)
+10. [RAG Pattern](#rag-pattern)
+
+---
+
+## Simple Counter
+
+Minimal example showing state updates and transitions.
+
+```python
+from burr.core import action, State, ApplicationBuilder, default, expr
+
+@action(reads=["counter"], writes=["counter"])
+def increment(state: State) -> State:
+    return state.update(counter=state["counter"] + 1)
+
+@action(reads=["counter"], writes=["result"])
+def finish(state: State) -> State:
+    return state.update(result=f"Final count: {state['counter']}")
+
+app = (
+    ApplicationBuilder()
+    .with_actions(increment, finish)
+    .with_transitions(
+        ("increment", "increment", expr("counter < 10")),
+        ("increment", "finish", default)
+    )
+    .with_state(counter=0)
+    .with_entrypoint("increment")
+    .build()
+)
+
+_, _, final_state = app.run(halt_after=["finish"])
+print(final_state["result"])  # "Final count: 10"
+```
+
+## Basic Chatbot
+
+Classic chatbot pattern with user input and AI response.
+
+```python
+from burr.core import action, State, ApplicationBuilder, default
+
+@action(reads=[], writes=["chat_history", "prompt"])
+def human_input(state: State, prompt: str) -> State:
+    """Capture user input."""
+    chat_item = {"role": "user", "content": prompt}
+    return (
+        state.update(prompt=prompt)
+        .append(chat_history=chat_item)
+    )
+
+@action(reads=["chat_history"], writes=["response", "chat_history"])
+def ai_response(state: State) -> State:
+    """Generate AI response."""
+    # Call your LLM here
+    response = call_llm(state["chat_history"])
+    chat_item = {"role": "assistant", "content": response}
+    return (
+        state.update(response=response)
+        .append(chat_history=chat_item)
+    )
+
+app = (
+    ApplicationBuilder()
+    .with_actions(human_input, ai_response)
+    .with_transitions(
+        ("human_input", "ai_response"),
+        ("ai_response", "human_input")
+    )
+    .with_state(chat_history=[])
+    .with_entrypoint("human_input")
+    .with_tracker("local", project="chatbot")
+    .build()
+)
+
+# Run one turn of conversation
+_, _, state = app.run(
+    halt_after=["ai_response"],
+    inputs={"prompt": "Hello, how are you?"}
+)
+print(state["response"])
+```
+
+## Multi-Step Workflow
+
+Chain multiple actions sequentially.
+
+```python
+@action(reads=["raw_text"], writes=["cleaned_text"])
+def clean_text(state: State) -> State:
+    """Remove special characters and normalize."""
+    cleaned = state["raw_text"].lower().strip()
+    return state.update(cleaned_text=cleaned)
+
+@action(reads=["cleaned_text"], writes=["tokens"])
+def tokenize(state: State) -> State:
+    """Split into tokens."""
+    tokens = state["cleaned_text"].split()
+    return state.update(tokens=tokens)
+
+@action(reads=["tokens"], writes=["summary"])
+def summarize(state: State) -> State:
+    """Generate summary."""
+    summary = f"Processed {len(state['tokens'])} tokens"
+    return state.update(summary=summary)
+
+app = (
+    ApplicationBuilder()
+    .with_actions(clean_text, tokenize, summarize)
+    .with_transitions(
+        ("clean_text", "tokenize"),
+        ("tokenize", "summarize")
+    )
+    .with_state(raw_text="  Hello World!  ")
+    .with_entrypoint("clean_text")
+    .build()
+)
+
+_, _, final_state = app.run(halt_after=["summarize"])
+```
+
+## Conditional Branching
+
+Route execution based on state values.
+
+```python
+from burr.core import when
+
+@action(reads=["user_type"], writes=["message"])
+def check_user_type(state: State, user_type: str) -> State:
+    return state.update(user_type=user_type)
+
+@action(reads=[], writes=["greeting"])
+def admin_greeting(state: State) -> State:
+    return state.update(greeting="Welcome, Administrator!")
+
+@action(reads=[], writes=["greeting"])
+def user_greeting(state: State) -> State:
+    return state.update(greeting="Welcome, User!")
+
+@action(reads=[], writes=["greeting"])
+def guest_greeting(state: State) -> State:
+    return state.update(greeting="Welcome, Guest!")
+
+app = (
+    ApplicationBuilder()
+    .with_actions(
+        check_user_type,
+        admin_greeting,
+        user_greeting,
+        guest_greeting
+    )
+    .with_transitions(
+        ("check_user_type", "admin_greeting", when(user_type="admin")),
+        ("check_user_type", "user_greeting", when(user_type="user")),
+        ("check_user_type", "guest_greeting", default)
+    )
+    .with_entrypoint("check_user_type")
+    .build()
+)
+
+_, _, state = app.run(
+    halt_after=["admin_greeting", "user_greeting", "guest_greeting"],
+    inputs={"user_type": "admin"}
+)
+```
+
+## Looping with Conditions
+
+Implement loops using recursive transitions.
+
+```python
+@action(reads=["items", "processed"], writes=["processed", "current_item"])
+def process_item(state: State) -> State:
+    """Process next item from list."""
+    items = state["items"]
+    processed_count = state.get("processed", 0)
+
+    current_item = items[processed_count]
+    # Process the item
+    result = transform(current_item)
+
+    return state.update(
+        processed=processed_count + 1,
+        current_item=result
+    )
+
+@action(reads=["processed"], writes=["done"])
+def finish_processing(state: State) -> State:
+    return state.update(done=True)
+
+app = (
+    ApplicationBuilder()
+    .with_actions(process_item, finish_processing)
+    .with_transitions(
+        ("process_item", "process_item", expr("processed < len(items)")),
+        ("process_item", "finish_processing", default)
+    )
+    .with_state(items=["a", "b", "c"], processed=0)
+    .with_entrypoint("process_item")
+    .build()
+)
+```
+
+## Error Handling
+
+Handle errors gracefully by routing to error actions.
+
+```python
+@action(reads=["data"], writes=["result", "error"])
+def risky_operation(state: State) -> State:
+    """Operation that might fail."""
+    try:
+        result = dangerous_function(state["data"])
+        return state.update(result=result, error=None)
+    except Exception as e:
+        return state.update(result=None, error=str(e))
+
+@action(reads=["result"], writes=["success_message"])
+def handle_success(state: State) -> State:
+    return state.update(success_message=f"Success: {state['result']}")
+
+@action(reads=["error"], writes=["error_message"])
+def handle_error(state: State) -> State:
+    return state.update(error_message=f"Error: {state['error']}")
+
+@action(reads=["data"], writes=["result", "retry_count"])
+def retry_operation(state: State) -> State:
+    """Retry the operation."""
+    retry_count = state.get("retry_count", 0) + 1
+    try:
+        result = dangerous_function(state["data"])
+        return state.update(result=result, error=None, retry_count=retry_count)
+    except Exception as e:
+        return state.update(result=None, error=str(e), retry_count=retry_count)
+
+app = (
+    ApplicationBuilder()
+    .with_actions(
+        risky_operation,
+        handle_success,
+        handle_error,
+        retry_operation
+    )
+    .with_transitions(
+        ("risky_operation", "handle_success", when(error=None)),
+        ("risky_operation", "retry_operation",
+         expr("error is not None and retry_count < 3")),
+        ("risky_operation", "handle_error", default),
+        ("retry_operation", "handle_success", when(error=None)),
+        ("retry_operation", "retry_operation",
+         expr("error is not None and retry_count < 3")),
+        ("retry_operation", "handle_error", default)
+    )
+    .with_state(data="input", retry_count=0)
+    .with_entrypoint("risky_operation")
+    .build()
+)
+```
+
+## Streaming Actions
+
+Stream intermediate results as they're generated.
+
+```python
+from typing import Generator, Tuple
+
+@action(reads=["prompt"], writes=["response", "chunks"])
+def streaming_llm(state: State) -> Generator[State, None, Tuple[dict, State]]:
+    """Stream LLM response token by token."""
+    chunks = []
+
+    # Stream tokens from LLM
+    for token in llm_stream(state["prompt"]):
+        chunks.append(token)
+        # Yield intermediate state
+        yield state.update(
+            chunks=chunks,
+            response="".join(chunks)
+        )
+
+    # Return final result
+    final_response = "".join(chunks)
+    result = {"response": final_response}
+    return result, state.update(**result)
+
+app = (
+    ApplicationBuilder()
+    .with_actions(streaming_llm)
+    .with_state(prompt="Write a story")
+    .with_entrypoint("streaming_llm")
+    .build()
+)
+
+# Stream results
+for state in app.stream_result(halt_after=["streaming_llm"]):
+    print(state["response"], end="", flush=True)
+```
+
+## Parallel Execution
+
+Execute multiple actions in parallel.
+
+```python
+from burr.core import graph
+
+@action(reads=["text"], writes=["sentiment"])
+def analyze_sentiment(state: State) -> State:
+    sentiment = get_sentiment(state["text"])
+    return state.update(sentiment=sentiment)
+
+@action(reads=["text"], writes=["entities"])
+def extract_entities(state: State) -> State:
+    entities = extract_ner(state["text"])
+    return state.update(entities=entities)
+
+@action(reads=["text"], writes=["keywords"])
+def extract_keywords(state: State) -> State:
+    keywords = get_keywords(state["text"])
+    return state.update(keywords=keywords)
+
+@action(reads=["sentiment", "entities", "keywords"], writes=["analysis"])
+def combine_results(state: State) -> State:
+    """Combine all analysis results."""
+    analysis = {
+        "sentiment": state["sentiment"],
+        "entities": state["entities"],
+        "keywords": state["keywords"]
+    }
+    return state.update(analysis=analysis)
+
+# Use graph builder for parallel execution
+g = (
+    graph.GraphBuilder()
+    .with_actions(
+        analyze_sentiment,
+        extract_entities,
+        extract_keywords,
+        combine_results
+    )
+    .with_transitions(
+        # These three run in parallel
+        ("start", "analyze_sentiment"),
+        ("start", "extract_entities"),
+        ("start", "extract_keywords"),
+        # Wait for all three to complete
+        (
+            ["analyze_sentiment", "extract_entities", "extract_keywords"],
+            "combine_results"
+        )
+    )
+    .build()
+)
+
+app = (
+    ApplicationBuilder()
+    .with_graph(g)
+    .with_state(text="Sample text to analyze")
+    .with_entrypoint("start")
+    .build()
+)
+```
+
+## State Persistence
+
+Save and resume application state.
+
+```python
+from burr.core.persistence import SQLLitePersister
+
+@action(reads=["step"], writes=["step", "result"])
+def long_running_step(state: State, step_name: str) -> State:
+    """Simulate a long-running operation."""
+    result = expensive_computation(step_name)
+    return state.update(
+        step=step_name,
+        result=result
+    )
+
+# Set up persister
+persister = SQLLitePersister(
+    db_path="~/.burr/my_app.db",
+    table_name="app_state"
+)
+persister.initialize()
+
+app = (
+    ApplicationBuilder()
+    .with_actions(
+        step1=long_running_step.bind(step_name="step1"),
+        step2=long_running_step.bind(step_name="step2"),
+        step3=long_running_step.bind(step_name="step3")
+    )
+    .with_transitions(
+        ("step1", "step2"),
+        ("step2", "step3")
+    )
+    .with_identifiers(
+        app_id="my-workflow-123",
+        partition_key="user-456"
+    )
+    .with_state_persister(persister)
+    .initialize_from(
+        persister,
+        resume_at_next_action=True,  # Resume from where it left off
+        default_state={"step": "none"},
+        default_entrypoint="step1"
+    )
+    .build()
+)
+
+# Run - will resume from last saved state if it exists
+app.run(halt_after=["step3"])
+```
+
+## RAG Pattern
+
+Retrieval-Augmented Generation workflow.
+
+```python
+@action(reads=[], writes=["query"])
+def process_query(state: State, user_query: str) -> State:
+    """Process and normalize user query."""
+    return state.update(query=user_query)
+
+@action(reads=["query"], writes=["documents"])
+def retrieve_documents(state: State) -> State:
+    """Retrieve relevant documents from vector store."""
+    docs = vector_db.search(state["query"], top_k=5)
+    return state.update(documents=docs)
+
+@action(reads=["documents"], writes=["reranked_documents"])
+def rerank_documents(state: State) -> State:
+    """Rerank documents for relevance."""
+    reranked = reranker.rerank(
+        state["query"],
+        state["documents"]
+    )
+    return state.update(reranked_documents=reranked)
+
+@action(reads=["query", "reranked_documents"], writes=["response"])
+def generate_response(state: State) -> State:
+    """Generate response using LLM with context."""
+    context = "\n".join([doc.content for doc in state["reranked_documents"]])
+    prompt = f"Context:\n{context}\n\nQuestion: {state['query']}\nAnswer:"
+
+    response = llm.generate(prompt)
+    return state.update(response=response)
+
+@action(reads=["response"], writes=["formatted_response", "sources"])
+def format_response(state: State) -> State:
+    """Format response with citations."""
+    sources = [
+        {"title": doc.title, "url": doc.url}
+        for doc in state["reranked_documents"]
+    ]
+    formatted = {
+        "answer": state["response"],
+        "sources": sources
+    }
+    return state.update(
+        formatted_response=formatted,
+        sources=sources
+    )
+
+app = (
+    ApplicationBuilder()
+    .with_actions(
+        process_query,
+        retrieve_documents,
+        rerank_documents,
+        generate_response,
+        format_response
+    )
+    .with_transitions(
+        ("process_query", "retrieve_documents"),
+        ("retrieve_documents", "rerank_documents"),
+        ("rerank_documents", "generate_response"),
+        ("generate_response", "format_response")
+    )
+    .with_entrypoint("process_query")
+    .with_tracker("local", project="rag_chatbot")
+    .build()
+)
+
+_, _, final_state = app.run(
+    halt_after=["format_response"],
+    inputs={"user_query": "What is Apache Burr?"}
+)
+print(final_state["formatted_response"])
+```
+
+## Using Action Binding
+
+Reuse actions with different parameters.
+
+```python
+@action(reads=["text"], writes=["processed_text"])
+def transform_text(state: State, operation: str, params: dict) -> State:
+    """Generic text transformation action."""
+    text = state["text"]
+
+    if operation == "uppercase":
+        result = text.upper()
+    elif operation == "replace":
+        result = text.replace(params["old"], params["new"])
+    elif operation == "truncate":
+        result = text[:params["length"]]
+
+    return state.update(processed_text=result)
+
+# Create specialized actions via binding
+uppercase_action = transform_text.bind(operation="uppercase", params={})
+replace_action = transform_text.bind(
+    operation="replace",
+    params={"old": "bad", "new": "good"}
+)
+truncate_action = transform_text.bind(
+    operation="truncate",
+    params={"length": 100}
+)
+
+app = (
+    ApplicationBuilder()
+    .with_actions(
+        uppercase=uppercase_action,
+        replace=replace_action,
+        truncate=truncate_action
+    )
+    .with_transitions(
+        ("uppercase", "replace"),
+        ("replace", "truncate")
+    )
+    .with_state(text="This is bad text that is too long...")
+    .with_entrypoint("uppercase")
+    .build()
+)
+```
+
+## Testing Actions
+
+Actions are pure functions - easy to test!
+
+```python
+import pytest
+from burr.core import State
+
+def test_increment_action():
+    """Test the increment action."""
+    state = State({"counter": 5})
+    new_state = increment(state)
+
+    assert new_state["counter"] == 6
+    assert state["counter"] == 5  # Original unchanged (immutable)
+
+def test_chatbot_response():
+    """Test AI response action."""
+    state = State({
+        "chat_history": [
+            {"role": "user", "content": "Hello"}
+        ]
+    })
+
+    new_state = ai_response(state)
+
+    assert "response" in new_state
+    assert len(new_state["chat_history"]) == 2
+    assert new_state["chat_history"][-1]["role"] == "assistant"
+
+def test_conditional_flow():
+    """Test complete application flow."""
+    app = build_conditional_app()
+
+    _, _, state = app.run(
+        halt_after=["admin_greeting"],
+        inputs={"user_type": "admin"}
+    )
+
+    assert state["greeting"] == "Welcome, Administrator!"
+
[email protected]
+async def test_async_action():
+    """Test async action."""
+    state = State({"url": "https://api.example.com/data"})
+    new_state = await fetch_data(state)
+
+    assert "data" in new_state
+```
+
+---
+
+For more examples, see the `examples/` directory in the Burr repository:
+- `examples/hello-world-counter/`
+- `examples/multi-modal-chatbot/`
+- `examples/conversational-rag/`
+- `examples/email-assistant/`
diff --git a/.claude/skills/burr/patterns.md b/.claude/skills/burr/patterns.md
new file mode 100644
index 00000000..cf32b300
--- /dev/null
+++ b/.claude/skills/burr/patterns.md
@@ -0,0 +1,668 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Apache Burr Design Patterns & Best Practices
+
+Architectural guidance and best practices for building production-ready Burr 
applications.
+
+## Core Design Principles
+
+### 1. Single Responsibility Actions
+
+Each action should do one thing well.
+
+**❌ Bad - Action does too much:**
+```python
+@action(reads=["query"], writes=["response", "documents", "reranked", 
"formatted"])
+def do_everything(state: State) -> State:
+    # Retrieves, reranks, generates, and formats all in one
+    docs = retrieve(state["query"])
+    reranked = rerank(docs)
+    response = generate(reranked)
+    formatted = format(response)
+    return state.update(
+        documents=docs,
+        reranked=reranked,
+        response=response,
+        formatted=formatted
+    )
+```
+
+**✅ Good - Focused actions:**
+```python
+@action(reads=["query"], writes=["documents"])
+def retrieve_documents(state: State) -> State:
+    docs = retrieve(state["query"])
+    return state.update(documents=docs)
+
+@action(reads=["documents"], writes=["reranked"])
+def rerank_documents(state: State) -> State:
+    reranked = rerank(state["documents"])
+    return state.update(reranked=reranked)
+
+# ... separate actions for generate and format
+```
+
+**Benefits:**
+- Easier to test
+- Easier to debug (can see which action failed)
+- Reusable components
+- Clear visualization in the Burr UI
+
+### 2. Accurate reads/writes Declarations
+
+Declare exactly what each action reads and writes.
+
+**❌ Bad:**
+```python
+@action(reads=[], writes=[])  # Inaccurate!
+def process_user(state: State) -> State:
+    user_id = state["user_id"]  # Actually reads user_id
+    profile = fetch_profile(user_id)
+    return state.update(profile=profile)  # Actually writes profile
+```
+
+**✅ Good:**
+```python
+@action(reads=["user_id"], writes=["profile"])
+def process_user(state: State) -> State:
+    user_id = state["user_id"]
+    profile = fetch_profile(user_id)
+    return state.update(profile=profile)
+```
+
+**Benefits:**
+- Self-documenting code
+- Better debugging in UI
+- Enables future optimizations
+- Catches errors early
+
+### 3. State Immutability
+
+Never mutate state directly - always use `.update()` or `.append()`.
+
+**❌ Bad:**
+```python
+@action(reads=["items"], writes=["items"])
+def add_item(state: State, item: str) -> State:
+    items = state["items"]
+    items.append(item)  # Mutates state!
+    return state
+```
+
+**✅ Good:**
+```python
+@action(reads=["items"], writes=["items"])
+def add_item(state: State, item: str) -> State:
+    return state.append(items=item)
+```
+
+**Benefits:**
+- Time-travel debugging
+- State history for replay
+- Prevents subtle bugs
+- Enables state persistence
+
+### 4. Deterministic Actions
+
+Given the same state and inputs, an action should always produce the same 
output.
+
+**❌ Bad - Non-deterministic:**
+```python
+@action(reads=["data"], writes=["result"])
+def process_data(state: State) -> State:
+    # Random behavior makes debugging impossible
+    if random.random() > 0.5:
+        result = transform_a(state["data"])
+    else:
+        result = transform_b(state["data"])
+    return state.update(result=result)
+```
+
+**✅ Good - Deterministic:**
+```python
+@action(reads=["data", "strategy"], writes=["result"])
+def process_data(state: State) -> State:
+    # Behavior controlled by state
+    if state["strategy"] == "a":
+        result = transform_a(state["data"])
+    else:
+        result = transform_b(state["data"])
+    return state.update(result=result)
+```
+
+**Benefits:**
+- Reproducible debugging
+- Testable code
+- Predictable behavior
+- Easier to reason about
+
+## Common Patterns
+
+### Pattern: Request-Response Cycle
+
+For chatbots and conversational AI.
+
+```python
+@action(reads=[], writes=["messages", "current_prompt"])
+def receive_message(state: State, prompt: str) -> State:
+    """Accept user input."""
+    message = {"role": "user", "content": prompt}
+    return (
+        state.append(messages=message)
+        .update(current_prompt=prompt)
+    )
+
+@action(reads=["messages"], writes=["messages", "response"])
+def generate_response(state: State) -> State:
+    """Generate AI response."""
+    response = llm_call(state["messages"])
+    message = {"role": "assistant", "content": response}
+    return (
+        state.append(messages=message)
+        .update(response=response)
+    )
+
+@action(reads=["response"], writes=["display"])
+def format_output(state: State) -> State:
+    """Format for display."""
+    return state.update(display=format_markdown(state["response"]))
+
+app = (
+    ApplicationBuilder()
+    .with_actions(receive_message, generate_response, format_output)
+    .with_transitions(
+        ("receive_message", "generate_response"),
+        ("generate_response", "format_output"),
+        ("format_output", "receive_message")  # Loop back for next message
+    )
+    .with_state(messages=[])
+    .with_entrypoint("receive_message")
+    .build()
+)
+```
+
+### Pattern: Error Recovery with Retries
+
+Handle transient failures gracefully.
+
+```python
+@action(reads=["url", "retry_count"], writes=["data", "error", "retry_count"])
+def fetch_with_retry(state: State) -> State:
+    """Fetch data with retry logic."""
+    try:
+        data = http_get(state["url"])
+        return state.update(data=data, error=None)
+    except Exception as e:
+        retry_count = state.get("retry_count", 0) + 1
+        return state.update(
+            error=str(e),
+            retry_count=retry_count
+        )
+
+@action(reads=["data"], writes=["processed"])
+def process_success(state: State) -> State:
+    """Process successful fetch."""
+    return state.update(processed=transform(state["data"]))
+
+@action(reads=["error", "retry_count"], writes=["final_error"])
+def handle_failure(state: State) -> State:
+    """Handle permanent failure."""
+    return state.update(
+        final_error=f"Failed after {state['retry_count']} retries: 
{state['error']}"
+    )
+
+app = (
+    ApplicationBuilder()
+    .with_actions(fetch_with_retry, process_success, handle_failure)
+    .with_transitions(
+        # Success path
+        ("fetch_with_retry", "process_success", when(error=None)),
+        # Retry path
+        ("fetch_with_retry", "fetch_with_retry",
+         expr("error is not None and retry_count < 3")),
+        # Failure path
+        ("fetch_with_retry", "handle_failure", default)
+    )
+    .with_state(url="https://api.example.com";, retry_count=0)
+    .with_entrypoint("fetch_with_retry")
+    .build()
+)
+```
+
+### Pattern: Multi-Stage Pipeline
+
+Sequential data processing pipeline.
+
+```python
+@action(reads=["raw_data"], writes=["validated_data"])
+def validate(state: State) -> State:
+    """Validate input data."""
+    validated = validate_schema(state["raw_data"])
+    return state.update(validated_data=validated)
+
+@action(reads=["validated_data"], writes=["transformed_data"])
+def transform(state: State) -> State:
+    """Transform data."""
+    transformed = apply_transformations(state["validated_data"])
+    return state.update(transformed_data=transformed)
+
+@action(reads=["transformed_data"], writes=["enriched_data"])
+def enrich(state: State) -> State:
+    """Enrich with external data."""
+    enriched = add_external_data(state["transformed_data"])
+    return state.update(enriched_data=enriched)
+
+@action(reads=["enriched_data"], writes=["result"])
+def finalize(state: State) -> State:
+    """Finalize output."""
+    result = create_output(state["enriched_data"])
+    return state.update(result=result)
+
+# Simple linear pipeline
+app = (
+    ApplicationBuilder()
+    .with_actions(validate, transform, enrich, finalize)
+    .with_transitions(
+        ("validate", "transform"),
+        ("transform", "enrich"),
+        ("enrich", "finalize")
+    )
+    .with_entrypoint("validate")
+    .build()
+)
+```
+
+### Pattern: Branching Decision Tree
+
+Route based on complex conditions.
+
+```python
+@action(reads=["content"], writes=["analysis"])
+def analyze_content(state: State) -> State:
+    """Analyze content type and complexity."""
+    analysis = {
+        "content_type": detect_type(state["content"]),
+        "complexity": calculate_complexity(state["content"]),
+        "language": detect_language(state["content"])
+    }
+    return state.update(analysis=analysis)
+
+@action(reads=["content"], writes=["result"])
+def handle_simple_text(state: State) -> State:
+    return state.update(result=simple_processor(state["content"]))
+
+@action(reads=["content"], writes=["result"])
+def handle_complex_text(state: State) -> State:
+    return state.update(result=complex_processor(state["content"]))
+
+@action(reads=["content"], writes=["result"])
+def handle_code(state: State) -> State:
+    return state.update(result=code_processor(state["content"]))
+
+@action(reads=["content"], writes=["result"])
+def handle_unsupported(state: State) -> State:
+    return state.update(result={"error": "Unsupported content type"})
+
+app = (
+    ApplicationBuilder()
+    .with_actions(
+        analyze_content,
+        handle_simple_text,
+        handle_complex_text,
+        handle_code,
+        handle_unsupported
+    )
+    .with_transitions(
+        ("analyze_content", "handle_simple_text",
+         expr("analysis['content_type'] == 'text' and analysis['complexity'] < 
5")),
+        ("analyze_content", "handle_complex_text",
+         expr("analysis['content_type'] == 'text' and analysis['complexity'] 
>= 5")),
+        ("analyze_content", "handle_code",
+         when(analysis={"content_type": "code"})),
+        ("analyze_content", "handle_unsupported", default)
+    )
+    .with_entrypoint("analyze_content")
+    .build()
+)
+```
+
+### Pattern: Aggregating Parallel Results
+
+Run multiple analyses in parallel and combine.
+
+```python
+from burr.core import graph
+
+@action(reads=["document"], writes=["summary"])
+async def summarize(state: State) -> State:
+    summary = await llm_summarize(state["document"])
+    return state.update(summary=summary)
+
+@action(reads=["document"], writes=["sentiment"])
+async def analyze_sentiment(state: State) -> State:
+    sentiment = await get_sentiment(state["document"])
+    return state.update(sentiment=sentiment)
+
+@action(reads=["document"], writes=["topics"])
+async def extract_topics(state: State) -> State:
+    topics = await get_topics(state["document"])
+    return state.update(topics=topics)
+
+@action(reads=["summary", "sentiment", "topics"], writes=["report"])
+def create_report(state: State) -> State:
+    """Aggregate all analyses into final report."""
+    report = {
+        "summary": state["summary"],
+        "sentiment": state["sentiment"],
+        "topics": state["topics"],
+        "timestamp": datetime.now()
+    }
+    return state.update(report=report)
+
+g = (
+    graph.GraphBuilder()
+    .with_actions(summarize, analyze_sentiment, extract_topics, create_report)
+    .with_transitions(
+        # Parallel execution
+        ("start", "summarize"),
+        ("start", "analyze_sentiment"),
+        ("start", "extract_topics"),
+        # Wait for all, then aggregate
+        (["summarize", "analyze_sentiment", "extract_topics"], "create_report")
+    )
+    .build()
+)
+```
+
+### Pattern: State Machine with Memory
+
+Maintain conversation context and history.
+
+```python
+@action(reads=["history"], writes=["history", "current_query"])
+def add_to_history(state: State, query: str) -> State:
+    """Add query to history with metadata."""
+    history_item = {
+        "query": query,
+        "timestamp": datetime.now(),
+        "session_id": state.get("session_id")
+    }
+    return (
+        state.append(history=history_item)
+        .update(current_query=query)
+    )
+
+@action(reads=["history", "current_query"], writes=["response"])
+def generate_with_context(state: State) -> State:
+    """Generate response using conversation history."""
+    # Build context from history
+    context = build_context_from_history(state["history"])
+
+    # Generate with full context
+    response = llm_call_with_context(
+        query=state["current_query"],
+        context=context
+    )
+    return state.update(response=response)
+
+@action(reads=["history"], writes=["should_summarize"])
+def check_history_length(state: State) -> State:
+    """Check if history needs summarization."""
+    should_summarize = len(state["history"]) > 10
+    return state.update(should_summarize=should_summarize)
+
+@action(reads=["history"], writes=["history", "summary"])
+def summarize_history(state: State) -> State:
+    """Compress old history."""
+    summary = create_summary(state["history"][:-5])
+    recent = state["history"][-5:]
+    return state.update(
+        history=recent,
+        summary=summary
+    )
+```
+
+## Best Practices
+
+### Testing Strategy
+
+**Unit test individual actions:**
+```python
+def test_action():
+    state = State({"input": "test"})
+    result = my_action(state)
+    assert result["output"] == "expected"
+```
+
+**Integration test the state machine:**
+```python
+def test_full_flow():
+    app = build_app()
+    _, _, final_state = app.run(halt_after=["end"])
+    assert final_state["result"] == expected_value
+```
+
+**Test with mock state:**
+```python
+def test_with_fixtures():
+    state = State({
+        "user": {"id": 123, "name": "Test"},
+        "settings": {"mode": "test"}
+    })
+    result = complex_action(state)
+    assert result["processed"] is True
+```
+
+### Observability
+
+**Always enable tracking during development:**
+```python
+app = (
+    ApplicationBuilder()
+    .with_actions(...)
+    .with_tracker("local", project="my_app")
+    .build()
+)
+```
+
+**Use the Burr UI to:**
+- Visualize state machine execution
+- Inspect state at each step
+- Debug transition logic
+- Profile action performance
+
+**Add custom hooks for production:**
+```python
+from burr.lifecycle import LifecycleAdapter
+
+class MetricsHook(LifecycleAdapter):
+    def post_run_step(self, action, result, state, **kwargs):
+        # Log metrics to your monitoring system
+        log_metric(f"action.{action.name}.duration", kwargs["duration"])
+        log_metric(f"action.{action.name}.success", 1)
+```
+
+### State Management
+
+**Keep state flat when possible:**
+```python
+# ✅ Good
+state = State({
+    "user_id": 123,
+    "user_name": "Alice",
+    "user_email": "[email protected]"
+})
+
+# ❌ Avoid deep nesting (harder to track)
+state = State({
+    "user": {
+        "profile": {
+            "personal": {
+                "name": "Alice"
+            }
+        }
+    }
+})
+```
+
+**Use meaningful key names:**
+```python
+# ✅ Good
+state.update(validated_user_email="[email protected]")
+
+# ❌ Bad
+state.update(ve="[email protected]")
+```
+
+### Performance Optimization
+
+**Use parallel execution for independent operations:**
+```python
+# Operations that don't depend on each other
+("start", ["fetch_user", "fetch_products", "fetch_orders"])
+```
+
+**Keep actions lightweight:**
+```python
+# ❌ Bad - Heavy computation in action
+@action(reads=["data"], writes=["result"])
+def process(state: State) -> State:
+    # Hours of computation
+    result = train_ml_model(state["data"])
+    return state.update(result=result)
+
+# ✅ Better - Break into steps with state persistence
+@action(reads=["data"], writes=["preprocessed"])
+def preprocess(state: State) -> State:
+    return state.update(preprocessed=preprocess_data(state["data"]))
+
+@action(reads=["preprocessed"], writes=["checkpoint"])
+def train_epoch(state: State) -> State:
+    # Train one epoch, save checkpoint
+    checkpoint = train_one_epoch(state["preprocessed"])
+    return state.update(checkpoint=checkpoint)
+```
+
+### Production Deployment
+
+**Enable persistence for long-running workflows:**
+```python
+from burr.core.persistence import SQLLitePersister
+
+persister = SQLLitePersister("prod.db", "workflows")
+app = (
+    ApplicationBuilder()
+    .with_actions(...)
+    .with_state_persister(persister)
+    .initialize_from(persister, resume_at_next_action=True)
+    .build()
+)
+```
+
+**Use unique identifiers:**
+```python
+app = (
+    ApplicationBuilder()
+    .with_identifiers(
+        app_id=f"workflow-{workflow_id}",
+        partition_key=f"user-{user_id}"
+    )
+    .build()
+)
+```
+
+**Add error boundaries:**
+```python
+@action(reads=["data"], writes=["result", "error"])
+def safe_operation(state: State) -> State:
+    try:
+        result = risky_operation(state["data"])
+        return state.update(result=result, error=None)
+    except Exception as e:
+        logger.error(f"Operation failed: {e}")
+        return state.update(result=None, error=str(e))
+```
+
+## Anti-Patterns to Avoid
+
+### ❌ Shared Mutable State
+
+```python
+# Don't do this!
+cache = {}
+
+@action(reads=["key"], writes=["value"])
+def get_cached(state: State) -> State:
+    # Mutates external state - not reproducible!
+    if state["key"] not in cache:
+        cache[state["key"]] = expensive_call()
+    return state.update(value=cache[state["key"]])
+```
+
+### ❌ Side Effects Without State Tracking
+
+```python
+# Don't do this!
+@action(reads=["data"], writes=["saved"])
+def save_to_db(state: State) -> State:
+    # Side effect not tracked in state
+    db.save(state["data"])
+    return state.update(saved=True)
+
+# Better: Track what was saved
+@action(reads=["data"], writes=["saved", "saved_id"])
+def save_to_db(state: State) -> State:
+    saved_id = db.save(state["data"])
+    return state.update(saved=True, saved_id=saved_id)
+```
+
+### ❌ God Actions
+
+```python
+# Don't do this!
+@action(reads=["everything"], writes=["everything"])
+def do_all_the_things(state: State) -> State:
+    # 500 lines of code doing multiple things
+    pass
+```
+
+### ❌ Missing Error Handling
+
+```python
+# Don't do this!
+@action(reads=["url"], writes=["data"])
+def fetch(state: State) -> State:
+    # No error handling - will crash the app
+    data = requests.get(state["url"]).json()
+    return state.update(data=data)
+```
+
+## Summary
+
+- **Keep actions small and focused**
+- **Declare reads/writes accurately**
+- **Never mutate state**
+- **Make actions deterministic**
+- **Use tracking and visualization**
+- **Test actions independently**
+- **Enable persistence for long workflows**
+- **Handle errors gracefully**
+- **Leverage parallel execution**
+- **Monitor with hooks in production**
diff --git a/.claude/skills/burr/plugin.json b/.claude/skills/burr/plugin.json
new file mode 100644
index 00000000..5414da39
--- /dev/null
+++ b/.claude/skills/burr/plugin.json
@@ -0,0 +1,34 @@
+{
+  "name": "burr",
+  "version": "1.0.0",
+  "description": "Apache Burr development assistant - expert guidance for 
building stateful applications with state machines",
+  "author": "Apache Burr Contributors",
+  "license": "Apache-2.0",
+  "repository": "https://github.com/apache/burr";,
+  "homepage": "https://burr.apache.org";,
+  "keywords": [
+    "burr",
+    "state-machine",
+    "llm",
+    "agent",
+    "workflow",
+    "stateful",
+    "python"
+  ],
+  "skill": {
+    "name": "burr",
+    "description": "Helps developers build stateful applications using Apache 
Burr, including state machines, actions, transitions, and observability",
+    "files": [
+      "SKILL.md",
+      "api-reference.md",
+      "examples.md",
+      "patterns.md",
+      "troubleshooting.md",
+      "README.md"
+    ]
+  },
+  "install": {
+    "message": "Apache Burr skill installed! Use /burr to get expert help 
building state machine applications.",
+    "requirements": []
+  }
+}
diff --git a/.claude/skills/burr/troubleshooting.md 
b/.claude/skills/burr/troubleshooting.md
new file mode 100644
index 00000000..da511125
--- /dev/null
+++ b/.claude/skills/burr/troubleshooting.md
@@ -0,0 +1,732 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Apache Burr Troubleshooting Guide
+
+Common issues and solutions when working with Burr.
+
+## Installation Issues
+
+### Issue: `burr` command not found after installation
+
+**Problem:**
+```bash
+$ burr
+command not found: burr
+```
+
+**Solutions:**
+
+1. Install with UI dependencies:
+```bash
+pip install "burr[start]"
+```
+
+2. Check if burr is in your PATH:
+```bash
+which burr
+# or
+python -m burr
+```
+
+3. If using poetry:
+```bash
+poetry add "burr[start]"
+poetry run burr
+```
+
+### Issue: UI won't start or shows errors
+
+**Problem:**
+```
+Error starting Burr UI: Module not found
+```
+
+**Solutions:**
+
+1. Ensure you installed the `[start]` extra:
+```bash
+pip install "burr[start]"
+```
+
+2. Check port is not already in use:
+```bash
+# Default is port 7241
+lsof -i :7241
+```
+
+3. Specify custom port:
+```bash
+burr --port 8000
+```
+
+4. Check storage directory permissions:
+```bash
+ls -la ~/.burr
+```
+
+## State Machine Issues
+
+### Issue: Infinite loops in transitions
+
+**Problem:**
+```python
+# State machine never halts
+app.run(halt_after=["end"])  # Never reaches "end"
+```
+
+**Common Causes:**
+
+1. **Missing halt condition:**
+```python
+# ❌ Bad - loops forever
+.with_transitions(
+    ("process", "process", default)  # Always loops!
+)
+
+# ✅ Good - has exit condition
+.with_transitions(
+    ("process", "process", expr("counter < 10")),
+    ("process", "end", default)
+)
+```
+
+2. **Condition never becomes true:**
+```python
+# ❌ Bad - condition may never be met
+.with_transitions(
+    ("wait", "wait", when(status="pending")),
+    ("wait", "done", when(status="complete"))
+    # What if status is "error"? Stuck forever!
+)
+
+# ✅ Good - always has fallback
+.with_transitions(
+    ("wait", "wait", when(status="pending")),
+    ("wait", "done", when(status="complete")),
+    ("wait", "error_handler", default)
+)
+```
+
+**Debugging:**
+1. Use `.visualize()` to see the graph:
+```python
+app.visualize(output_file_path="debug.png", include_conditions=True)
+```
+
+2. Add logging in actions:
+```python
+@action(reads=["counter"], writes=["counter"])
+def process(state: State) -> State:
+    print(f"Counter: {state['counter']}")  # Debug output
+    return state.update(counter=state["counter"] + 1)
+```
+
+3. Use the Burr UI to watch execution in real-time:
+```bash
+burr
+```
+
+### Issue: Wrong action executes
+
+**Problem:**
+```
+Expected action 'process_data' but 'error_handler' executed instead
+```
+
+**Common Causes:**
+
+1. **Transition condition order matters:**
+```python
+# ❌ Bad - default matches first
+.with_transitions(
+    ("check", "error", default),  # This always matches!
+    ("check", "success", when(valid=True))
+)
+
+# ✅ Good - specific conditions first
+.with_transitions(
+    ("check", "success", when(valid=True)),
+    ("check", "error", default)
+)
+```
+
+2. **State value is not what you expect:**
+```python
+# Debug: Check actual state values
+@action(reads=["value"], writes=["result"])
+def check_value(state: State) -> State:
+    print(f"Value is: {state['value']}, type: {type(state['value'])}")
+    # Maybe it's a string "True" not boolean True?
+    return state.update(result=state["value"])
+```
+
+3. **Using `when()` with complex objects:**
+```python
+# ❌ Bad - object comparison may not work as expected
+.with_transitions(
+    ("check", "next", when(user={"id": 123}))  # Dict comparison
+)
+
+# ✅ Good - use simpler state values
+.with_transitions(
+    ("check", "next", when(user_id=123))  # Direct value comparison
+)
+```
+
+## State Issues
+
+### Issue: State not updating
+
+**Problem:**
+```python
+# State remains unchanged after action
+state_before = app.state["counter"]  # 0
+app.run(halt_after=["increment"])
+state_after = app.state["counter"]   # Still 0!
+```
+
+**Common Causes:**
+
+1. **Not returning updated state:**
+```python
+# ❌ Bad - returns None
+@action(reads=["counter"], writes=["counter"])
+def increment(state: State) -> State:
+    state.update(counter=state["counter"] + 1)
+    # Missing return!
+
+# ✅ Good - returns updated state
+@action(reads=["counter"], writes=["counter"])
+def increment(state: State) -> State:
+    return state.update(counter=state["counter"] + 1)
+```
+
+2. **Mutating state directly:**
+```python
+# ❌ Bad - mutates state (doesn't work)
+@action(reads=["items"], writes=["items"])
+def add_item(state: State, item: str) -> State:
+    state["items"].append(item)  # This doesn't work!
+    return state
+
+# ✅ Good - uses .append() method
+@action(reads=["items"], writes=["items"])
+def add_item(state: State, item: str) -> State:
+    return state.append(items=item)
+```
+
+3. **Typo in state key:**
+```python
+# ❌ Bad - creates new key instead of updating existing
+@action(reads=["counter"], writes=["counter"])
+def increment(state: State) -> State:
+    return state.update(couter=state["counter"] + 1)  # Typo!
+
+# ✅ Good - correct key name
+@action(reads=["counter"], writes=["counter"])
+def increment(state: State) -> State:
+    return state.update(counter=state["counter"] + 1)
+```
+
+### Issue: KeyError accessing state
+
+**Problem:**
+```python
+KeyError: 'missing_key'
+```
+
+**Solutions:**
+
+1. **Use `.get()` with default:**
+```python
+# ❌ Risky - may not exist
+value = state["key"]
+
+# ✅ Safe - provides default
+value = state.get("key", default_value)
+```
+
+2. **Check if key exists:**
+```python
+if "key" in state:
+    value = state["key"]
+else:
+    value = default
+```
+
+3. **Initialize state properly:**
+```python
+app = (
+    ApplicationBuilder()
+    .with_state(
+        counter=0,  # Initialize all keys
+        items=[],
+        status="pending"
+    )
+    .build()
+)
+```
+
+4. **Check reads declaration:**
+```python
+# ❌ Bad - tries to read undeclared key
+@action(reads=[], writes=["result"])
+def process(state: State) -> State:
+    value = state["input"]  # Not in reads!
+    return state.update(result=value)
+
+# ✅ Good - declares what it reads
+@action(reads=["input"], writes=["result"])
+def process(state: State) -> State:
+    value = state["input"]
+    return state.update(result=value)
+```
+
+## Action Issues
+
+### Issue: Action inputs not working
+
+**Problem:**
+```python
+app.run(halt_after=["process"], inputs={"param": "value"})
+# Error: unexpected keyword argument 'param'
+```
+
+**Solution:**
+
+Add parameter to action function:
+```python
+# ❌ Bad - no parameter for input
+@action(reads=[], writes=["result"])
+def process(state: State) -> State:
+    # How do I access the input?
+    pass
+
+# ✅ Good - accepts input parameter
+@action(reads=[], writes=["result"])
+def process(state: State, param: str) -> State:
+    return state.update(result=param)
+```
+
+### Issue: Streaming action not working
+
+**Problem:**
+```python
+# No intermediate results appear
+for state in app.stream_result(halt_after=["end"]):
+    print(state)  # Only prints final state
+```
+
+**Solution:**
+
+Use generator pattern with yields:
+```python
+# ❌ Bad - regular action (no streaming)
+@action(reads=["input"], writes=["output"])
+def process(state: State) -> State:
+    result = slow_operation()
+    return state.update(output=result)
+
+# ✅ Good - streaming action
+@action(reads=["input"], writes=["output"])
+def process(state: State) -> Generator[State, None, Tuple[dict, State]]:
+    for chunk in slow_operation():
+        # Yield intermediate states
+        yield state.update(current_chunk=chunk)
+
+    # Return final result
+    result = {"output": "done"}
+    return result, state.update(**result)
+```
+
+### Issue: Async action errors
+
+**Problem:**
+```
+RuntimeError: Event loop is closed
+```
+
+**Solutions:**
+
+1. **Use `arun()` for async applications:**
+```python
+# ❌ Bad - using sync run with async actions
+app.run(halt_after=["async_action"])
+
+# ✅ Good - using async run
+await app.arun(halt_after=["async_action"])
+```
+
+2. **Make sure action is marked async:**
+```python
+# ✅ Async action
+@action(reads=["url"], writes=["data"])
+async def fetch_data(state: State) -> State:
+    async with httpx.AsyncClient() as client:
+        response = await client.get(state["url"])
+    return state.update(data=response.json())
+```
+
+3. **Mix sync and async carefully:**
+```python
+# You can have both sync and async actions in the same app
+# Burr handles this automatically
+app = (
+    ApplicationBuilder()
+    .with_actions(
+        sync_action,    # Regular action
+        async_action    # Async action
+    )
+    .build()
+)
+
+# Use arun() if any action is async
+await app.arun(halt_after=["async_action"])
+```
+
+## Persistence Issues
+
+### Issue: State not persisting
+
+**Problem:**
+```python
+# State is lost between runs
+app = build_app()
+app.run(halt_after=["step1"])
+# Restart app
+app = build_app()  # Starts from beginning, not step1
+```
+
+**Solution:**
+
+Set up persistence properly:
+```python
+from burr.core.persistence import SQLLitePersister
+
+persister = SQLLitePersister("app.db", "state")
+persister.initialize()  # Don't forget to initialize!
+
+app = (
+    ApplicationBuilder()
+    .with_actions(...)
+    .with_identifiers(
+        app_id="my-workflow",       # Required for persistence
+        partition_key="user-123"     # Required for persistence
+    )
+    .with_state_persister(persister)
+    .initialize_from(
+        persister,
+        resume_at_next_action=True,
+        default_state={"step": 0},
+        default_entrypoint="start"
+    )
+    .build()
+)
+```
+
+### Issue: Multiple app instances conflict
+
+**Problem:**
+```python
+# Both apps save to same location and conflict
+app1 = build_app()
+app2 = build_app()
+```
+
+**Solution:**
+
+Use unique identifiers:
+```python
+app1 = (
+    ApplicationBuilder()
+    .with_identifiers(
+        app_id="workflow-1",
+        partition_key="user-alice"
+    )
+    .build()
+)
+
+app2 = (
+    ApplicationBuilder()
+    .with_identifiers(
+        app_id="workflow-2",
+        partition_key="user-bob"
+    )
+    .build()
+)
+```
+
+## Tracking / UI Issues
+
+### Issue: Application not appearing in UI
+
+**Problem:**
+```
+Burr UI is running but my application doesn't show up
+```
+
+**Solutions:**
+
+1. **Make sure tracking is enabled:**
+```python
+app = (
+    ApplicationBuilder()
+    .with_tracker("local", project="my_project")
+    .build()
+)
+```
+
+2. **Check storage directory:**
+```python
+# Specify storage directory
+app = (
+    ApplicationBuilder()
+    .with_tracker(
+        "local",
+        project="my_project",
+        params={"storage_dir": "~/.burr"}
+    )
+    .build()
+)
+
+# Then launch UI with same directory
+# burr --storage-dir ~/.burr
+```
+
+3. **Run the application:**
+```python
+# Tracking data is only created when app runs
+app.run(halt_after=["some_action"])
+```
+
+4. **Check UI is pointing to correct directory:**
+```bash
+burr --storage-dir ~/.burr
+```
+
+### Issue: Visualization not generating
+
+**Problem:**
+```python
+app.visualize(output_file_path="graph.png")
+# No file created, or error about graphviz
+```
+
+**Solutions:**
+
+1. **Install graphviz:**
+```bash
+# macOS
+brew install graphviz
+
+# Ubuntu
+sudo apt-get install graphviz
+
+# Then install Python package
+pip install graphviz
+```
+
+2. **Check file path:**
+```python
+# Use absolute path
+app.visualize(
+    output_file_path="/full/path/to/graph.png",
+    format="png"
+)
+```
+
+3. **Try different formats:**
+```python
+# Try PDF if PNG doesn't work
+app.visualize(
+    output_file_path="graph.pdf",
+    format="pdf"
+)
+```
+
+## Performance Issues
+
+### Issue: Application runs slowly
+
+**Problem:**
+```
+Application takes too long to execute
+```
+
+**Solutions:**
+
+1. **Use parallel execution:**
+```python
+# Run independent actions in parallel
+from burr.core import graph
+
+g = (
+    graph.GraphBuilder()
+    .with_actions(action1, action2, action3)
+    .with_transitions(
+        # These run in parallel
+        ("start", ["action1", "action2"]),
+        (["action1", "action2"], "action3")
+    )
+    .build()
+)
+```
+
+2. **Profile actions:**
+```python
+import time
+
+@action(reads=["input"], writes=["output"])
+def slow_action(state: State) -> State:
+    start = time.time()
+    result = expensive_operation(state["input"])
+    print(f"Action took {time.time() - start:.2f}s")
+    return state.update(output=result)
+```
+
+3. **Check for unnecessary state copies:**
+```python
+# ❌ Slow - repeated state updates
+new_state = state
+for item in items:
+    new_state = new_state.update(item=process(item))
+
+# ✅ Faster - batch update
+processed_items = [process(item) for item in items]
+new_state = state.update(items=processed_items)
+```
+
+4. **Use async for I/O-bound operations:**
+```python
+# ❌ Slow - sequential I/O
+def fetch_all(state: State) -> State:
+    data1 = requests.get(url1).json()
+    data2 = requests.get(url2).json()
+    return state.update(data1=data1, data2=data2)
+
+# ✅ Fast - parallel async I/O
+async def fetch_all(state: State) -> State:
+    async with httpx.AsyncClient() as client:
+        data1, data2 = await asyncio.gather(
+            client.get(url1),
+            client.get(url2)
+        )
+    return state.update(data1=data1.json(), data2=data2.json())
+```
+
+## Testing Issues
+
+### Issue: Tests fail with tracking enabled
+
+**Problem:**
+```python
+# Tests create tracking data and clutter filesystem
+```
+
+**Solution:**
+
+Disable tracking in tests:
+```python
+def build_app(enable_tracking: bool = True):
+    builder = ApplicationBuilder().with_actions(...)
+
+    if enable_tracking:
+        builder = builder.with_tracker("local", project="my_app")
+
+    return builder.build()
+
+# In tests
+def test_my_app():
+    app = build_app(enable_tracking=False)
+    # Test without creating tracking data
+```
+
+Or use temporary directory:
+```python
+import tempfile
+
+def test_with_tracking():
+    with tempfile.TemporaryDirectory() as tmpdir:
+        app = (
+            ApplicationBuilder()
+            .with_tracker(
+                "local",
+                project="test",
+                params={"storage_dir": tmpdir}
+            )
+            .build()
+        )
+        # Test runs, tracking data cleaned up automatically
+```
+
+## Type Checking Issues
+
+### Issue: Type errors with State
+
+**Problem:**
+```python
+# Type checker complains about state access
+def my_function(state: State):
+    value: int = state["key"]  # Type checker error
+```
+
+**Solution:**
+
+State values are typed as `Any` by default. Use runtime checks or type 
assertions:
+```python
+def my_function(state: State):
+    value = state["key"]
+    assert isinstance(value, int)
+    # Now type checker knows it's int
+```
+
+Or use Pydantic integration for type safety:
+```python
+from burr.core import action
+from pydantic import BaseModel
+
+class MyState(BaseModel):
+    key: int
+
[email protected](reads=["key"], writes=["result"])
+def my_action(state: State, inputs: MyState) -> dict:
+    # inputs.key is typed as int
+    return {"result": inputs.key + 1}
+```
+
+## Getting Help
+
+If you're still stuck:
+
+1. **Check the documentation:** https://burr.apache.org/
+2. **Search GitHub issues:** https://github.com/apache/burr/issues
+3. **Ask on Discord:** https://discord.gg/6Zy2DwP4f3
+4. **Enable debug logging:**
+```python
+import logging
+logging.basicConfig(level=logging.DEBUG)
+```
+
+When asking for help, include:
+- Burr version: `pip show burr`
+- Python version: `python --version`
+- Minimal code example that reproduces the issue
+- Full error message and traceback
+- State machine visualization if relevant: `app.visualize()`
diff --git a/.rat-excludes b/.rat-excludes
index 7a51824e..b6ebab13 100644
--- a/.rat-excludes
+++ b/.rat-excludes
@@ -12,6 +12,9 @@
 # Jupyter notebooks (JSON format, cannot practically add headers)
 .*\.ipynb
 
+# Plugin manifests (JSON format, cannot practically add headers)
+.*plugin\.json
+
 # Data files (CSV - not source code)
 .*\.csv
 
diff --git a/CLAUDE_SKILL.md b/CLAUDE_SKILL.md
new file mode 100644
index 00000000..650c3da4
--- /dev/null
+++ b/CLAUDE_SKILL.md
@@ -0,0 +1,168 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Apache Burr Claude Code Skill
+
+This repository includes a Claude Code skill that makes Claude an expert 
assistant for building Apache Burr applications.
+
+## Quick Install
+
+### Option 1: Install from GitHub (Easiest)
+
+```bash
+# Install to personal skills directory
+claude skill install https://github.com/apache/burr/.claude/skills/burr
+
+# Or install to project
+cd your-project
+claude skill install https://github.com/apache/burr/.claude/skills/burr 
--project
+```
+
+### Option 2: Manual Install
+
+```bash
+# Clone the repository
+git clone https://github.com/apache/burr
+
+# Install to personal skills directory
+cp -r burr/.claude/skills/burr ~/.claude/skills/
+
+# Or install to your project
+cp -r burr/.claude/skills/burr .claude/skills/
+```
+
+## What You Get
+
+Once installed, Claude becomes an expert in:
+
+- **Building Burr applications** - Get help scaffolding state machines from 
scratch
+- **Writing actions** - Create properly structured action functions with 
correct decorators
+- **Defining transitions** - Set up conditional logic and state machine flows
+- **Adding observability** - Configure the Burr UI and tracking
+- **Debugging issues** - Troubleshoot common problems with state machines
+- **Following best practices** - Learn recommended patterns and anti-patterns
+- **Code review** - Get feedback on your Burr code
+
+## Usage
+
+### Automatic Activation
+
+Just mention Burr in your conversation:
+
+```
+"Help me build a chatbot with Burr"
+"Why isn't my state updating?"
+"Show me how to add retry logic"
+```
+
+### Manual Invocation
+
+Use the `/burr` command explicitly:
+
+```
+/burr How do I create a streaming action?
+/burr Review this code for best practices
+/burr Show me an example of parallel execution
+```
+
+## What's Included
+
+The skill contains comprehensive documentation:
+
+- **SKILL.md** - Main instructions for Claude
+- **api-reference.md** - Complete Burr API documentation
+- **examples.md** - Working code examples for common patterns
+- **patterns.md** - Best practices and design patterns
+- **troubleshooting.md** - Solutions to common issues
+- **README.md** - Installation and usage guide
+
+## Example Interactions
+
+**Building a new application:**
+```
+You: "Help me create a Burr application for document processing"
+Claude: I'll help you create a multi-stage pipeline...
+[Generates complete application with actions and transitions]
+```
+
+**Getting examples:**
+```
+You: "Show me how to implement retry logic in Burr"
+Claude: Here's a retry pattern with error recovery...
+[Provides working code example]
+```
+
+**Debugging:**
+```
+You: "My state machine is looping infinitely"
+Claude: Let me help you debug the transitions...
+[Analyzes and suggests fixes]
+```
+
+**Code review:**
+```
+You: "Review this Burr code for best practices"
+Claude: Let me check your application...
+[Provides detailed feedback]
+```
+
+## Documentation
+
+Full documentation available at:
+- **Online**: https://burr.apache.org/getting_started/claude-skill
+- **Local**: `docs/getting_started/claude-skill.rst` in this repository
+
+## Requirements
+
+- Claude Code CLI installed
+- No Burr installation required (Claude can help you install when needed)
+
+## Customization
+
+You can customize the skill for your team:
+
+1. Edit the files in `.claude/skills/burr/`
+2. Add your own examples to `examples.md`
+3. Update patterns in `patterns.md`
+4. Extend the API reference
+
+## Contributing
+
+Found a bug or want to improve the skill?
+
+- **Report issues**: https://github.com/apache/burr/issues
+- **Submit fixes**: Open a pull request with your improvements
+- **Suggest examples**: Share useful patterns you've discovered
+
+We welcome contributions of all sizes - from typo fixes to new examples!
+
+## Related Resources
+
+- **Burr Documentation**: https://burr.apache.org
+- **GitHub Repository**: https://github.com/apache/burr
+- **Example Applications**: `examples/` directory
+- **Discord Community**: https://discord.gg/6Zy2DwP4f3
+
+## License
+
+Apache License 2.0 - See LICENSE file in the root of the repository.
+
+---
+
+Built with ❤️ by the Apache Burr community.
diff --git a/README.md b/README.md
index c65c035e..bc86c7ec 100644
--- a/README.md
+++ b/README.md
@@ -144,6 +144,22 @@ including tooling to build a UI in streamlit and watch 
your state machine execut
 See the documentation for [getting 
started](https://burr.apache.org/getting_started/simple-example), and follow 
the example.
 Then read through some of the concepts and write your own application!
 
+### Using Claude Code?
+
+If you use [Claude Code](https://claude.com/claude-code), install the [Burr 
Claude skill](.claude/skills/burr/) to get expert assistance building Burr 
applications. The skill teaches Claude about Burr's APIs, best practices, and 
common patterns.
+
+```bash
+# Easy install from GitHub
+claude skill install https://github.com/apache/burr/.claude/skills/burr
+
+# Or manual install
+cp -r /path/to/burr/.claude/skills/burr ~/.claude/skills/
+```
+
+Then just ask Claude naturally: *"Help me build a Burr application"* or use 
`/burr` for specific help.
+
+See [CLAUDE_SKILL.md](CLAUDE_SKILL.md) for installation details and the [skill 
documentation](https://burr.apache.org/getting_started/claude-skill) for full 
usage guide.
+
 ## 📃 Comparison against common frameworks
 
 While Apache Burr is attempting something (somewhat) unique, there are a 
variety of tools that occupy similar spaces:
diff --git a/docs/getting_started/claude-skill.rst 
b/docs/getting_started/claude-skill.rst
new file mode 100644
index 00000000..02de3761
--- /dev/null
+++ b/docs/getting_started/claude-skill.rst
@@ -0,0 +1,439 @@
+..
+   Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+   distributed with this work for additional information
+   regarding copyright ownership.  The ASF licenses this file
+   to you under the Apache License, Version 2.0 (the
+   "License"); you may not use this file except in compliance
+   with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing,
+   software distributed under the License is distributed on an
+   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+   KIND, either express or implied.  See the License for the
+   specific language governing permissions and limitations
+   under the License.
+
+
+==================
+Claude Code Skill
+==================
+
+Apache Burr includes a comprehensive Claude Code skill that makes Claude an 
expert in helping you build Burr applications.
+
+What is the Claude Code Skill?
+===============================
+
+The Burr Claude skill is a plugin for `Claude Code 
<https://claude.com/claude-code>`_, Anthropic's official CLI tool. When active, 
it teaches Claude how to:
+
+* Build new Burr applications from scratch
+* Write properly structured actions and transitions
+* Follow best practices and design patterns
+* Debug common issues
+* Provide working code examples
+* Review your code for correctness
+
+Installation
+============
+
+Option 1: Install from GitHub (Easiest)
+----------------------------------------
+
+Use the Claude CLI to install directly from GitHub:
+
+.. code-block:: bash
+
+    # Install to personal skills directory
+    claude skill install https://github.com/apache/burr/.claude/skills/burr
+
+    # Or install to your current project
+    claude skill install https://github.com/apache/burr/.claude/skills/burr 
--project
+
+Option 2: Manual Personal Installation
+---------------------------------------
+
+Copy the skill to your personal Claude skills directory:
+
+.. code-block:: bash
+
+    # Clone the Burr repository
+    git clone https://github.com/apache/burr
+
+    # Copy skill to personal directory
+    cp -r burr/.claude/skills/burr ~/.claude/skills/
+
+Option 3: Manual Project Installation
+--------------------------------------
+
+For team projects, copy the skill to your project's ``.claude/skills/`` 
directory:
+
+.. code-block:: bash
+
+    # From your project root
+    cp -r /path/to/burr/.claude/skills/burr .claude/skills/
+
+Verify Installation
+-------------------
+
+Check that the skill is available:
+
+.. code-block:: bash
+
+    # In Claude Code, try:
+    /burr --help
+
+Or ask Claude naturally:
+
+    "Help me build a Burr application"
+
+Usage
+=====
+
+Manual Invocation
+-----------------
+
+Use the ``/burr`` command to explicitly invoke the skill:
+
+.. code-block:: text
+
+    /burr How do I create a streaming action?
+
+    /burr Review this action for best practices
+
+    /burr Show me an example of parallel execution
+
+Automatic Invocation
+--------------------
+
+Claude automatically loads the skill when it detects you're working with Burr:
+
+.. code-block:: text
+
+    "I'm building a chatbot with Burr"
+
+    "Why isn't my action updating the state?"
+
+    "Show me how to add persistence"
+
+What Can It Do?
+===============
+
+Code Generation
+---------------
+
+Ask Claude to generate complete Burr applications:
+
+**Example:**
+
+.. code-block:: text
+
+    "Create a Burr application for a RAG chatbot with document retrieval and 
reranking"
+
+Claude will generate:
+
+* Action functions with proper ``@action`` decorators
+* State machine transitions with conditions
+* Tracking configuration
+* Complete application setup
+
+Code Review
+-----------
+
+Get feedback on your Burr code:
+
+**Example:**
+
+.. code-block:: text
+
+    "Review this application for best practices"
+
+Claude will check:
+
+* Correct ``reads`` and ``writes`` declarations
+* State immutability
+* Transition coverage
+* Error handling
+* Performance considerations
+
+Learning & Examples
+-------------------
+
+Get working examples for common patterns:
+
+**Example:**
+
+.. code-block:: text
+
+    "Show me how to implement retry logic"
+
+Claude provides:
+
+* Complete working code
+* Explanation of the pattern
+* Best practices
+* References to documentation
+
+Debugging Help
+--------------
+
+Troubleshoot issues with Claude's help:
+
+**Example:**
+
+.. code-block:: text
+
+    "My state machine is looping infinitely"
+
+Claude will:
+
+* Analyze transition logic
+* Suggest using ``.visualize()``
+* Provide solutions
+* Reference troubleshooting docs
+
+Skill Contents
+==============
+
+The skill includes comprehensive documentation:
+
+API Reference
+-------------
+
+Complete documentation of Burr's API:
+
+* Actions and decorators
+* State management
+* ApplicationBuilder
+* Transitions and conditions
+* Persistence
+* Tracking and hooks
+
+Examples
+--------
+
+Working code examples for:
+
+* Basic chatbots
+* Streaming actions
+* Parallel execution
+* Error handling and retries
+* RAG patterns
+* State persistence
+* Testing
+
+Design Patterns
+---------------
+
+Best practices and architectural guidance:
+
+* Single responsibility actions
+* State immutability
+* Deterministic actions
+* Error recovery patterns
+* Multi-stage pipelines
+* Branching decision trees
+
+Troubleshooting
+---------------
+
+Solutions for common issues:
+
+* Installation problems
+* State machine loops
+* State not updating
+* Persistence issues
+* Performance optimization
+
+Common Use Cases
+================
+
+Building a Chatbot
+------------------
+
+.. code-block:: text
+
+    "Help me build a multi-modal chatbot with Burr"
+
+Claude will create a complete chatbot with:
+
+* User input action
+* LLM response action
+* State management for chat history
+* Transitions for conversation flow
+
+Adding Features
+---------------
+
+.. code-block:: text
+
+    "Add streaming responses to my chatbot"
+
+Claude will:
+
+* Show how to convert to a streaming action
+* Provide the generator pattern
+* Update the application setup
+
+Debugging
+---------
+
+.. code-block:: text
+
+    "My action isn't updating state, what's wrong?"
+
+Claude will:
+
+* Review your code
+* Identify the issue (likely missing ``return``)
+* Provide the fix
+* Explain why it matters
+
+Tips for Best Results
+======================
+
+1. **Be specific** - "Help me add retry logic to my fetch action" is better 
than "help with errors"
+
+2. **Show your code** - Claude works best when it can see what you're building
+
+3. **Ask for examples** - "Show me an example of..." gets working code
+
+4. **Reference the skill's docs** - Ask Claude to check the API reference or 
patterns guide
+
+5. **Use visualization** - Ask Claude to suggest using ``app.visualize()`` 
when debugging
+
+Example Conversation
+====================
+
+Here's a typical interaction:
+
+.. code-block:: text
+
+    You: I want to build a Burr application that processes documents through 
multiple stages
+
+    Claude: I'll help you create a multi-stage document processing pipeline 
with Burr.
+    Let me create actions for each stage...
+
+    [Claude generates code with actions for validation, transformation, 
enrichment, and output]
+
+    You: How do I add error handling?
+
+    Claude: I'll show you how to add error recovery with retries. Here's the 
pattern...
+
+    [Claude adds error handling actions and transitions]
+
+    You: Can you review this code?
+
+    Claude: Let me check your application for best practices...
+
+    [Claude reviews and provides feedback]
+
+Integration with Development
+=============================
+
+The skill integrates seamlessly with your development workflow:
+
+* **During design** - Get help planning your state machine architecture
+* **While coding** - Generate boilerplate and follow patterns
+* **When debugging** - Troubleshoot issues and understand errors
+* **In code review** - Verify best practices are followed
+
+Customizing the Skill
+======================
+
+You can customize the skill for your needs:
+
+1. Edit ``SKILL.md`` to change instructions
+2. Add your own examples to ``examples.md``
+3. Update ``patterns.md`` with team-specific practices
+4. Extend ``api-reference.md`` with custom actions
+
+Example customization:
+
+.. code-block:: bash
+
+    cd ~/.claude/skills/burr
+    # Edit the skill files
+    vim examples.md
+
+Updating the Skill
+==================
+
+To get the latest version:
+
+.. code-block:: bash
+
+    cd /path/to/burr
+    git pull
+    cp -r .claude/skills/burr ~/.claude/skills/
+
+Related Resources
+=================
+
+* `Claude Code Documentation <https://docs.claude.com/claude-code>`_
+* `Burr Examples <https://github.com/apache/burr/tree/main/examples>`_
+* `Burr Discord <https://discord.gg/6Zy2DwP4f3>`_
+
+FAQ
+===
+
+**Do I need Burr installed to use the skill?**
+
+No, but Claude can help you install it when needed.
+
+**Can I use this with other frameworks?**
+
+Yes! Burr integrates well with LangChain, LlamaIndex, Apache Hamilton, and 
others.
+
+**Will this work with older Burr versions?**
+
+The skill is designed for current Burr versions. Some APIs may differ in older 
releases.
+
+**How do I disable the skill?**
+
+Rename the skill directory:
+
+.. code-block:: bash
+
+    mv ~/.claude/skills/burr ~/.claude/skills/burr.disabled
+
+**Can I share my customizations?**
+
+Yes! Contribute improvements back to the project via pull request.
+
+Contributing
+============
+
+Found an issue or want to improve the skill? We welcome contributions!
+
+Reporting Issues
+----------------
+
+If you find a bug or have a suggestion:
+
+1. Check existing issues at https://github.com/apache/burr/issues
+2. Open a new issue with:
+
+   * Clear description of the problem or suggestion
+   * Steps to reproduce (for bugs)
+   * Expected vs actual behavior
+   * Burr version and environment details
+
+Contributing Improvements
+-------------------------
+
+We especially appreciate pull requests! To contribute:
+
+1. Fork the repository
+2. Edit the skill files in ``.claude/skills/burr/``
+3. Test your changes with Claude Code
+4. Submit a PR to https://github.com/apache/burr with:
+
+   * Clear description of what you changed and why
+   * Examples showing the improvement
+   * Any relevant issue references
+
+Small fixes like typos, improved examples, or clearer explanations are always 
welcome!
+
+The Burr community appreciates all contributions, big and small.
diff --git a/docs/getting_started/index.rst b/docs/getting_started/index.rst
index bd5d29c7..5594161d 100644
--- a/docs/getting_started/index.rst
+++ b/docs/getting_started/index.rst
@@ -31,4 +31,5 @@ The following section of the docs will walk you through Burr 
and how to integrat
     why-burr
     install
     simple-example
+    claude-skill
     up-next

Reply via email to