diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0a29f4f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Something in agentfactory doesn't work as documented +title: '' +labels: bug +assignees: '' +--- + +## What happened + + + +## Reproduction + +```bash +# exact commands, starting from a known state +``` + +## Environment + +- OS: +- Go version (`go version`): +- Python version (`python3 --version`): +- tmux version (`tmux -V`): +- Install path: source build / quickstart.sh / quickdocker.sh + +## Relevant output + +``` +# af output, tmux capture, or hook logs +``` diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..55002d7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Propose a capability or improvement +title: '' +labels: enhancement +assignees: '' +--- + +## Problem + + + +## Proposed behavior + + + +## Alternatives considered + + diff --git a/.github/workflows/visibility-health.yml b/.github/workflows/visibility-health.yml new file mode 100644 index 0000000..bce3f38 --- /dev/null +++ b/.github/workflows/visibility-health.yml @@ -0,0 +1,64 @@ +# Monthly repo-health watchdog: verifies the discoverability surface (topics, +# description, release, README badges) hasn't regressed and opens a single +# issue when it has. Checks only — never commits, never touches content. +name: visibility-health + +on: + schedule: + - cron: '17 8 3 * *' + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + check: + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + steps: + - uses: actions/checkout@v4 + - name: Check discoverability surface + run: | + problems="" + + topics=$(gh api "repos/$REPO" --jq '.topics | join(" ")') + for t in claude-code ai-agents multi-agent-systems agentic-ai golang; do + case " $topics " in + *" $t "*) ;; + *) problems="$problems- topic \`$t\` is missing\n" ;; + esac + done + + desc=$(gh api "repos/$REPO" --jq '.description // ""') + [ -n "$desc" ] || problems="$problems- repo description is empty\n" + + if ! gh api "repos/$REPO/releases/latest" --jq .tag_name >/dev/null 2>&1; then + problems="$problems- no published release\n" + fi + + grep -q 'actions/workflows/test.yml/badge.svg' README.md \ + || problems="$problems- CI badge missing from README\n" + grep -q 'img.shields.io/github/license' README.md \ + || problems="$problems- license badge missing from README\n" + + if [ -z "$problems" ]; then + echo "All visibility checks passed." + exit 0 + fi + + echo "Regressions found:" + printf '%b' "$problems" + + title="Visibility health check: regressions found" + existing=$(gh issue list --repo "$REPO" --state open \ + --search "\"$title\" in:title" --json number --jq 'length') + if [ "$existing" -gt 0 ]; then + echo "Open issue already exists — not filing a duplicate." + exit 0 + fi + + body=$(printf 'The monthly visibility health check found regressions:\n\n%b\nRestore these — they are the repo'"'"'s discoverability surface (topics pages, search snippets, README badges).' "$problems") + gh issue create --repo "$REPO" --title "$title" --body "$body" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9f614be --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,62 @@ +# Changelog + +Notable changes to agentfactory. The project began 2026-05-01; snapshot tags `V001`–`V012` +mark pre-release checkpoints. `v0.1.0` is the first formal release. + +## v0.1.0 — 2026-07-11 + +First formal release, consolidating ten weeks of development. + +### Orchestration core + +- Formula system: declarative TOML workflows with steps, DAG dependencies, variables, and + gates; `af sling --formula` instantiation and `af prime`/`af done` step tracking (#2) +- Agent generation from formulas: `af formula agent-gen` creates workspace, role template, + and hook configuration with no manual file moves (#8) +- Skill-to-formula pipeline: `/formula-create` turns a `SKILL.md` into a runnable formula; + built-in skills embedded and extracted during `af install --init` (#16) +- Prime-before-done enforcement with velocity tracking, formula skill validation, and + per-agent model/endpoint configuration (#43, #81) +- Startup.json-driven `af up` with declarative agent subset selection, dispatcher + auto-start, and scoped watchdog (#58) + +### Reliability & recovery + +- Mandatory step execution block and fidelity hook corrections for agents drifting off + formula steps (#26, #28) +- Worktree isolation hardening: dispatched agents get independent worktrees; teardown gated + on session termination; branch-committed skills preserved (#30, #32, #61, #69) +- Unified reset semantics: `af sling --reset` and `af down --reset` perform identical full + cleanup — worktrees, open work items, runtime state, checkpoints (#40) +- Gate locks migrated to `.runtime` with stale-PID recovery (#43) +- Test/production tmux isolation with build-tag-gated constructor guard; compact-handoff + PreCompact hook for context-compression safety (#52) +- Agents made default-branch-agnostic; regen/lint CI gates (#63) + +### Multi-agent coordination + +- Inter-agent mail over the issue store, with broadcast groups and reply threading +- Autonomous dispatch: PR/issue label matching, multi-label AND semantics, dispatch cycle + locking, idle back-off, phase advancement, and issue→PR handoff (#36, #38, #79) +- MergePatrol PR-review agent with label-based discovery (#36) + +### Formula & agent library + +- rapid-implement, rapid-increment, ultra-review formulas (#65); web-design agent with + consensus gate (#68); minimalworker (#52); the fable agent family — fable-implement, + fable-increment, fable-review (#83) + +### Web console + +- Loopback-only web console: Floor view, task slinging, dispatch status, settings, design + prototypes; singleton-launch rendezvous; agent detail and operator mail (#72, #81, #83) +- Browser formula authoring (#83) + +### Platform & tooling + +- Containerized setup via `quickdocker.sh` + `quickstart.sh`; stack-agnostic customer repo + discovery (#56); iOS build-host configuration with ssh-agent forwarding (#48, #50) +- Server-wide tmux mouse/clipboard UX at session creation (#77) +- CI: unit, integration, template-regen, and supply-chain-lint jobs + +[Full commit history](https://github.com/stempeck/agentfactory/commits/main) diff --git a/README.md b/README.md index 1465aa3..f294169 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,70 @@ -# Agentfactory -**Why do I need Agentfactory?** -1. You can give LLM steps to follow and at times it will use a heuristic and improvise, skipping steps -2. Recency bias drives every prompt -3. Improvization and Recency bias lead to bugs the next LLM builds around, compounding false assumptions -4. No crash recovery for agents doing long-running or multi-agent operations +# agentfactory — multi-agent orchestration for Claude Code + +**Turn your `SKILL.md` files into an autonomous agent workforce.** agentfactory is a +multi-agent orchestration CLI that runs Claude Code agents through declarative TOML +workflows — with crash recovery, context re-injection, and inter-agent mail built in. + +[![CI](https://github.com/stempeck/agentfactory/actions/workflows/test.yml/badge.svg)](https://github.com/stempeck/agentfactory/actions/workflows/test.yml) +[![Go](https://img.shields.io/github/go-mod/go-version/stempeck/agentfactory)](go.mod) +[![License: AGPL-3.0](https://img.shields.io/github/license/stempeck/agentfactory)](LICENSE) +[![Release](https://img.shields.io/github/v/release/stempeck/agentfactory)](https://github.com/stempeck/agentfactory/releases) + +## Why agentfactory + +Long-running LLM agents fail in predictable ways. Give an agent a list of steps and at some +point it will improvise — skip a step, substitute a heuristic, and keep going. Recency bias +means whatever entered the context last dominates what the agent does next. When the context +window fills and gets compressed, the agent quietly loses its identity, its place in the +workflow, or both. And when a session crashes mid-task, there is usually nothing to recover: +the plan lived inside the conversation. + +Skills (`SKILL.md` files) capture *what you know*, but a skill alone can't hold an agent to a +workflow — the agent needs a harness. agentfactory separates the two concerns: **personas** +stay thin (identity, startup protocol, available commands), while **workflows live in +formulas** — declarative TOML files with steps, DAG dependencies, variables, and quality +gates. The `af` runtime bridges them: it instantiates a formula into trackable work items, +re-injects identity and the current step on every prompt (surviving context compression), +checkpoints progress so a crashed agent resumes where it stopped, and gives agents a mail +system to coordinate multi-agent work. -SKILLs aren't enough on their own to solve this. You need an Agent with a better harness. +## Architecture + +```mermaid +flowchart LR + S["SKILL.md
what you know"] -- "/formula-create" --> F["formula.toml
steps · DAG deps · vars · gates"] + F -- "af formula agent-gen" --> A["agent workspace
thin persona + hooks"] + A -- "af up" --> T["tmux session
running Claude Code"] + T -- "af prime
context re-injection" --> T + T -- "af done
step advancement" --> F + T <-- "af mail" --> M[("inter-agent
mail")] + T -- crash --> C["checkpoint
resume on restart"] --> T +``` -Agent Factory can easily harness your existing skills +Three layers: -**Vision:** -You have SKILLs, now turn your SKILL.md's into your autonomous workforce. +1. **Agent templates** (`.md.tmpl`) — thin persona shells: identity, startup protocol, commands +2. **Formulas** (`.formula.toml`) — declarative workflows: steps, DAG dependencies, variables, gates +3. **`af` runtime** — instantiates formulas as work items, injects context via `af prime`, tracks progress via `af done` -**Mission:** -Create an instruction set workflow (formula) with `/formula-create /path/to/your/SKILL.md` and generate an autonomous agentfactory agent from it with `af formula agent-gen name-of-your-formula` with simple steps or multi-agent coordination. +Agents don't need to know their full workflow. They run `af prime` to get the current step, +execute it, run `af done` to advance, and repeat. On context compression, `af prime` +re-injects identity and step context automatically. Deeper reading: +[formulas reference](docs/formulas.md) · [agent lifecycle](docs/agent-lifecycle.md) · +[recovery model](docs/recovery-model.md) · [architecture corpus](docs/architecture/overview.md). -**Multi-agent orchestration CLI for Claude Code.** -Turn your SKILL.md files into autonomous agents that execute structured workflows with context handoffs, inter-agent messaging, and crash recovery. +## How it compares + +| | Workflow definition | Execution substrate | Crash / context-loss recovery | +|---|---|---|---| +| **agentfactory** | Declarative TOML DAGs, separate from personas | Claude Code sessions in tmux — inspectable, attachable | First-class: checkpoints + `af prime` re-injection | +| **LangGraph** | Graphs built in Python/JS application code | Your app process calling model APIs | Checkpointing available; you build the harness | +| **CrewAI** | Role/task definitions in Python code | Your app process calling model APIs | Not a core concern; retries at task level | +| **Claude Code subagents** | Prompts inside one session | In-session fan-out | None — subagent state dies with the session | + +Honest scope: agentfactory orchestrates **Claude Code** specifically — it is not a +model-agnostic framework. If you want programmatic graphs inside a Python app, LangGraph is +the better fit. If you live in Claude Code and want autonomous, restartable, coordinated +agents driven by workflows you can diff and review, that's what this is for. ## Quick Start @@ -52,9 +100,14 @@ cd agentfactory ./quickdocker.sh ``` -This builds a container with all prerequisites, clones your target repo, and runs `quickstart.sh` inside it. When it finishes, the container is ready for `af up`. +This builds a container with all prerequisites, clones your target repo, and runs +`quickstart.sh` inside it. When it finishes, the container is ready for `af up`. -A clean install now also **reveals the web console before the shell**: when it finishes it prints the loopback URL `http://127.0.0.1:/` (and opens your browser on macOS) immediately before dropping you into the interactive shell — so you no longer have to run `--web` yourself just to first see it. Use `--web` only to **re-open** the console later. See the **Web Console (optional)** section below and [`web/README.md`](web/README.md) for details. +A clean install now also **reveals the web console before the shell**: when it finishes it +prints the loopback URL `http://127.0.0.1:/` (and opens your browser on macOS) +immediately before dropping you into the interactive shell — so you no longer have to run +`--web` yourself just to first see it. Use `--web` only to **re-open** the console later. See +the **Web Console (optional)** section below and [`web/README.md`](web/README.md) for details. #### Using quickstart.sh (inside the docker container @ docker exec -it -u dev "af_ghusername_repo" bash) @@ -65,7 +118,8 @@ cd ~/projects/agentfactory/ ### Authenticate Claude Code -After installation, run `claude` once to authenticate. Agents require an authenticated Claude Code session to function. +After installation, run `claude` once to authenticate. Agents require an authenticated Claude +Code session to function. ## Usage @@ -80,15 +134,8 @@ af install manager af install supervisor ``` -Add factory directories to `.gitignore`: - -```bash -cat >> .gitignore << 'EOF' -.agentfactory/ -.agentfactory/hooks/ -.agentfactory/store/ -EOF -``` +`af install --init` automatically excludes factory directories from git via +`.git/info/exclude` — no `.gitignore` changes needed. ### 2. Start agents @@ -136,9 +183,11 @@ claude # e.g. ./.claude/skills/rapid-implement/SKILL.md") ``` -This generates a `.formula.toml` file in `.agentfactory/store/formulas/`. Be patient. It can take some time. +This generates a `.formula.toml` file in `.agentfactory/store/formulas/`. Be patient. It can +take some time. -NOTICE: `.claude/skills/rapid-implement/SKILL.md` was provided in case you want to try creating your first coding agent. +NOTICE: `.claude/skills/rapid-implement/SKILL.md` was provided in case you want to try +creating your first coding agent. ### 2. Generate an agent from your formula (your-agent-name.formula.toml -> your-agent-name) @@ -146,7 +195,8 @@ NOTICE: `.claude/skills/rapid-implement/SKILL.md` was provided in case you want af formula agent-gen your-agent-name ``` -This creates the agent's workspace, CLAUDE.md template, hook configuration, and registers it in `agents.json`. +This creates the agent's workspace, CLAUDE.md template, hook configuration, and registers it +in `agents.json`. ### 3. Rebuild af with the new agent template @@ -154,7 +204,8 @@ This creates the agent's workspace, CLAUDE.md template, hook configuration, and make install ``` -Required because `af prime` reads templates from the compiled binary (`go:embed`). Without this, the agent falls back to the generic supervisor template on context compression. +Required because `af prime` reads templates from the compiled binary (`go:embed`). Without +this, the agent falls back to the generic supervisor template on context compression. ### 4. Start the agent @@ -170,7 +221,8 @@ af sling --agent your-agent-name "do the thing" ### Batch regeneration -To regenerate all specialist agents from formulas and re-bootstrap the factory, run the redeploy command from the main project checkout: +To regenerate all specialist agents from formulas and re-bootstrap the factory, run the +redeploy command from the main project checkout: ```bash af install --agents # regenerate all + rebuild, then bootstrap @@ -181,13 +233,15 @@ See `USING_AGENTFACTORY.md` for preconditions, data-safety, and `--no-build` not ## Included Formulas -| Formula | Purpose | -|---------|---------| -| `design-v3` | Structured design exploration with constraint verification | -| `design` | Basic design workflow | -| `factoryworker` | General-purpose factory worker | -| `gherkin-breakdown` | Break work into Gherkin scenarios | -| `mergepatrol` | PR review and merge workflow | +Nineteen formulas ship with the factory (see [docs/formulas.md](docs/formulas.md) for the format): + +| Family | Formulas | Purpose | +|---------|----------|---------| +| Implementation | `rapid-implement`, `rapid-increment`, `fable-implement`, `fable-increment` | Structured feature implementation with quality gates | +| Design | `design`, `design-v3`, `design-v7`, `design-plan-impl`, `rapid-soldesign-plan`, `web-design` | Design exploration with constraint verification | +| Review | `mergepatrol`, `ultra-review`, `fable-review` | PR review, merge workflow, deep multi-pass review | +| Root cause | `rootcause-all`, `investigate` | Failure investigation and verified root-cause analysis | +| Utility | `factoryworker`, `minimalworker`, `gherkin-breakdown`, `github-issue` | General workers, scenario breakdown, issue authoring | ## Included Skills @@ -254,17 +308,7 @@ When the bind is ever non-loopback (not the default), the console additionally r session token (printed at startup) as defense-in-depth — but that is **not** a license to publish the port; the socket stays loopback whether you reach it via the `--web` bridge or the SSH forward. -## Architecture - -Agentfactory has three layers: - -1. **Agent templates** (`.md.tmpl`) — thin persona shells that define identity, startup protocol, and available commands -2. **Formulas** (`.formula.toml`) — declarative workflow definitions with steps, DAG dependencies, variables, and gates -3. **`af` runtime** — bridges the two: instantiates formulas as beads, injects context via `af prime`, tracks progress via `af done` - -Agents don't need to know their full workflow. They run `af prime` to get the current step, execute it, run `af done` to advance, and repeat. On context compression, `af prime` re-injects identity and step context automatically. - -### Key directories +## Key directories ``` .agentfactory/ @@ -311,15 +355,35 @@ af prime # inject identity, get af done # complete and advance to next step (used by agents) ``` +## Roadmap + +- **Prebuilt release binaries** (GoReleaser) so `af` installs without a Go toolchain +- **Richer shipped formula library** — more turnkey specialist agents out of the box +- **Gate quality improvements** — reduce fidelity-gate false positives on passive steps ([#75](https://github.com/stempeck/agentfactory/issues/75)) +- **Default dispatch workflow** included with the factory ([#73](https://github.com/stempeck/agentfactory/issues/73)) +- **Web console growth** — deeper agent detail, formula authoring in the browser + +Have a use case these don't cover? [Open an issue](https://github.com/stempeck/agentfactory/issues/new/choose). + ## Contributing -See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines, CLA requirements, and development setup. +Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines, CLA +requirements, and development setup. Good entry points are labeled +[good first issue](https://github.com/stempeck/agentfactory/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). ## License AGPL-3.0. See [CONTRIBUTING.md](CONTRIBUTING.md) for commercial licensing inquiries. ### Disclaimer -The contributors to this project take no responsibility for your agent (or their respective LLMs) actions. -Good luck, and enjoy your Factory of Agents! \ No newline at end of file +The contributors to this project take no responsibility for your agent (or their respective +LLMs) actions. + +--- + +Built and maintained by **[Glenn Stempeck](https://github.com/stempeck)** · +[LinkedIn](https://www.linkedin.com/in/glenn-stempeck/) · +[Medium](https://medium.com/@glennstempeck) + +Good luck, and enjoy your Factory of Agents! diff --git a/docs/agent-lifecycle.md b/docs/agent-lifecycle.md new file mode 100644 index 0000000..ce8c11e --- /dev/null +++ b/docs/agent-lifecycle.md @@ -0,0 +1,112 @@ +# Agent lifecycle: how agentfactory runs autonomous Claude Code agents + +Every agentfactory agent is a Claude Code session running inside a tmux session, owned and +supervised by the `af` runtime. The lifecycle is deliberately boring: provision a workspace, +start a session, inject identity, loop over formula steps, stop. Each stage is a CLI command +you can run and inspect yourself, which is also what makes the system debuggable — attach to +any agent with `af attach` and watch it work. + +## Provisioning: `af install` + +```bash +af install --init # initialize the factory in your project root +af install manager # provision an agent workspace from a role template +af install supervisor +``` + +`--init` creates `.agentfactory/` (registry, config, formula store) and adds the +factory-managed paths to `.git/info/exclude` — your repository stays clean without manual +`.gitignore` edits. Each `af install ` creates a per-agent workspace under +`.agentfactory/agents//` containing the agent's `CLAUDE.md` role template and its +Claude Code hook configuration. + +Custom specialists come from formulas: `af formula agent-gen ` generates the workspace +and registers the agent in `agents.json` — see [Formulas](formulas.md). + +## Starting: `af up` + +```bash +af up # start all configured agents +af up manager supervisor # start specific agents +af up worker --model sonnet-profile # per-agent model override from models.json +``` + +For each agent, `af up`: + +1. Creates a git **worktree** owned by that agent (children dispatched from it via + `af sling --agent` share the same worktree). +2. Creates a tmux session named for the agent, with mouse and clipboard behavior configured. +3. Exports factory environment (`AF_ROOT`, …) into the session. +4. Launches Claude Code in the agent's workspace. + +**Zombie detection:** if the tmux session already exists, `af up` checks whether Claude is +actually running inside it. A live session with a dead Claude is a zombie — it is killed and +recreated rather than reported as "already running" (`internal/session/session.go`). + +## Identity injection: `af prime` + +Agents do not carry their instructions in a long-lived prompt. On every session start, a +Claude Code `SessionStart` hook runs: + +``` +af prime --hook && af mail check --inject +``` + +`af prime` outputs the agent's role template, session metadata, and — when a formula is +hooked — the current step's directives. This is the mechanism that makes agents recoverable: +identity and workflow state live on disk and in the issue store, not in the model's context +window. See [the recovery model](recovery-model.md) for what happens on context compression. + +## The work loop: `af done` + +An agent working a formula repeats one cycle: + +1. `af prime` hands it the current step (id, title, directives, resolved variables). +2. It executes the step's directives. +3. `af done` closes the step's bead and advances to the next ready step in the DAG. +4. Gate steps end the session instead: `af done --phase-complete --gate `. + +Quality enforcement happens at the loop boundary — `Stop` hooks run the quality and fidelity +gates, so a step that skipped its directives is caught before the workflow advances. + +## Messaging: `af mail` + +Agents coordinate through mail, not shared context: + +```bash +af mail send supervisor -s "Fix auth bug" -m "login.go is not checking token expiry" +af mail send @all -s "Status" -m "…" # broadcast +af mail inbox / read / reply -m "…" / delete +af mail check # exit 0/1 — is there new mail? +``` + +Delivery is automatic: the `UserPromptSubmit` hook injects unread mail into the agent's +context on every prompt, and `SessionStart` injects it at launch. A supervisor started with +`af up supervisor` needs no operator attention — it picks up mail and works. + +## Dispatch: `af sling` + +```bash +af sling --agent rapid-implement "https://github.com/org/repo/issues/42" +af sling --formula mergepatrol --var pr=123 --agent mergepatrol +af sling --agent worker "task" --reset # force-reset: close beads, remove worktree, clean state +``` + +`af sling` is how work enters the factory — from an operator, from the manager agent, or from +the GitHub-issue dispatcher (`af dispatch`). Sessions started by sling auto-terminate when +their formula completes. + +## Observing and stopping + +```bash +af attach # attach to the live tmux session (watch or intervene) +af agents list --json # machine-readable status of every configured agent +af down worker # stop specific agents +af down --all # stop everything +``` + +## Further reading + +- [Formulas](formulas.md) — the workflow definitions agents execute +- [Recovery model](recovery-model.md) — crash and context-compression survival +- [README](../README.md) · [Using Agentfactory](../USING_AGENTFACTORY.md) · [Architecture overview](architecture/overview.md) diff --git a/docs/formulas.md b/docs/formulas.md new file mode 100644 index 0000000..4190d2b --- /dev/null +++ b/docs/formulas.md @@ -0,0 +1,122 @@ +# Formulas: declarative TOML workflows for Claude Code agents + +A **formula** is a declarative workflow definition in TOML. It describes the steps of a job — +their order, their dependencies, their inputs, and the gates that block completion — separately +from the agent that executes them. The agent doesn't memorize its workflow; the `af` runtime +feeds it one step at a time via [`af prime`](agent-lifecycle.md) and advances via `af done`. +That separation is what makes agent behavior repeatable: the workflow lives in a file you can +review and version, not inside a persona prompt that drifts. + +## Where formulas live + +``` +.agentfactory/store/formulas/ # project-local (per repository) +~/.agentfactory/store/formulas/ # global (shared across projects) +``` + +Files are named `.formula.toml`. Agentfactory ships with formulas for +implementation, review, design, and issue workflows (see `internal/cmd/install_formulas/`). + +## Formula types + +| Type | Execution model | +|------|-----------------| +| `workflow` | Sequential steps with explicit dependencies (the common case) | +| `convoy` | Parallel legs, then a synthesis step that depends on all of them | +| `expansion` | Template-based step generation | +| `aspect` | Multi-aspect parallel analysis | + +## Anatomy of a workflow formula + +```toml +formula = "my-workflow" +description = "What this workflow does and how it progresses." +version = 1 + +[inputs] +[inputs.issue_url] +description = "GitHub issue to implement" +type = "string" +required = true + +[vars] +[vars.branch_prefix] +description = "Branch naming prefix" +source = "cli" # cli | env | literal | hook_bead | bead_title | bead_description +default = "feature" + +[[steps]] +id = "investigate" +title = "Investigate the issue" +description = """ +What the agent must do in this step, written as directives. +Reference variables as {{issue_url}}. +""" + +[[steps]] +id = "implement" +title = "Implement the fix" +needs = ["investigate"] # DAG dependency — this step is not ready until investigate closes +``` + +Key fields: + +- **`[[steps]]`** — each has an `id`, `title`, `description` (the actual instructions), optional + `agent` (per-step owner overriding the formula-level `agent`), optional `needs` (dependencies), + and an optional `gate`. +- **`needs`** — forms a directed acyclic graph. Validation rejects unknown IDs and cycles; + execution order is a topological sort (Kahn's algorithm, `internal/formula/sort.go`). +- **`[inputs]`** — parameters callers must supply at instantiation (`--var key=val`). +- **`[vars]`** — resolved at instantiation from the declared `source`: the command line, the + environment, a literal default, or the hooked work item. +- **`gate`** — an optional blocking gate on a step (`internal/formula/types.go`); a step with + a gate cannot be closed until the gate resolves. Gate steps complete with + `af done --phase-complete --gate `, which registers the session as a gate waiter. + +Inspect any formula's inputs and vars without opening the file: + +```bash +af formula show --json +``` + +## Creating a formula from a SKILL.md + +The `/formula-create` skill (run inside Claude Code) converts a SKILL.md — a procedural +skill document — into a formula: + +``` +/formula-create /path/to/your/SKILL.md +``` + +It preserves the skill's phase gates as separate formula steps and writes the result to +`.agentfactory/store/formulas/.formula.toml`. + +## Turning a formula into an agent + +```bash +af formula agent-gen # generate workspace, CLAUDE.md template, hooks, registry entry +af formula agent-gen -o # dry run — print the rendered CLAUDE.md to stdout +af formula agent-gen --delete +make install # required: af embeds role templates in the binary (go:embed) +``` + +Without the rebuild, the new agent falls back to the generic supervisor template after +context compression — see [the recovery model](recovery-model.md). + +## Running a formula + +```bash +af sling --formula --var key=val --agent # instantiate on an agent +af sling --agent "task description" # simple task dispatch (no formula) +af sling --formula --agent --no-launch # create step beads only, don't launch +``` + +Instantiation creates one work item ("bead") per step with the DAG encoded as dependencies, +resolves variables, and (unless `--no-launch`) starts the agent's tmux session. The agent then +loops: `af prime` → execute step → `af done` → next ready step. + +## Further reading + +- [Agent lifecycle](agent-lifecycle.md) — how sessions start, work, and stop +- [Recovery model](recovery-model.md) — how formulas survive crashes and context compression +- [README](../README.md) · [Using Agentfactory](../USING_AGENTFACTORY.md) · [Architecture overview](architecture/overview.md) diff --git a/docs/recovery-model.md b/docs/recovery-model.md new file mode 100644 index 0000000..2eefd8a --- /dev/null +++ b/docs/recovery-model.md @@ -0,0 +1,102 @@ +# Recovery model: how agents survive crashes and context compression + +Long-running LLM agents fail in ways ordinary programs don't. The context window fills up and +gets compressed, silently deleting the instructions the agent was following. Sessions crash +mid-step. An agent that "remembers" its workflow only in conversation history loses it at +exactly the wrong moment. Agentfactory's answer is to keep identity and workflow state +**outside** the model — on disk and in the issue store — and re-inject it at every session +boundary, so a recovered agent picks up the current step instead of improvising. + +## What breaks, concretely + +- **Context compression** — Claude Code summarizes the conversation when the window fills. + Step directives, variable values, and role framing can be lost or distorted in the summary. +- **Crashes and disconnects** — the Claude process dies but the tmux session survives (a + zombie), or the whole session is gone and uncommitted work sits in the worktree. +- **Recency bias** — after enough turns, early instructions stop steering behavior even + without compression. Improvised shortcuts compound into wrong work. + +## The recovery chain + +### 1. State lives outside the session + +A formula instantiation creates one bead (work item) per step, with DAG dependencies, in the +issue store. The agent's hook (`.runtime/hooked_formula`) records which formula instance it +is working. None of this is in the context window, so none of it can be compressed away. + +### 2. `af prime` re-injects identity on every session start + +The `SessionStart` hook runs `af prime --hook`. Prime outputs the agent's role template +(embedded in the `af` binary via `go:embed`), session metadata, and the current step's full +directives, computed from the ready-step frontier in the issue store. A fresh session — +whether after a crash, a restart, or a compaction recycle — starts with the same authoritative +context as the first one. This is why `make install` matters after `af formula agent-gen`: +the template a recovered agent gets is the one compiled into the binary. + +### 3. Compaction boundaries are intercepted + +The `PreCompact` hook runs `af compact-handoff`, which handles the compaction boundary +(including preventing thinking-block corruption) and hands the session off cleanly. The +checkpoint records `compaction_handoff: true` and the compaction time, so the next session +knows a recycle happened. + +### 4. Checkpoints leave breadcrumbs + +Whenever a session ends, `.agent-checkpoint.json` in the agent directory captures +(`internal/checkpoint/`): + +| Field | Contents | +|---|---| +| `formula_id`, `current_step`, `step_title` | What was being worked | +| `hooked_bead` | The work item on the agent's hook | +| `modified_files`, `last_commit`, `branch` | Git state at checkpoint time | +| `timestamp`, `session_id`, `notes` | Provenance and free-form context | + +Checkpoints are deliberately **informational** — breadcrumbs for the next session to read, +not a database that drives recovery. Recovery decisions come from the issue store's ready +steps and the hooked formula; the checkpoint tells the recovered agent what the previous +session had in flight (e.g. "3 modified files on branch X, step `implement` open"). + +### 5. Identity locks prevent split-brain + +Each agent workspace holds a PID-based lock at `.runtime/agent.lock` +(`internal/lock/`). A second session claiming the same identity gets `ErrLocked` — unless the +recorded PID is dead, in which case the lock is **stale** and is cleaned up automatically. +Crash recovery therefore never requires manually deleting lock files. + +### 6. Zombie sessions are killed, not trusted + +`af up` distinguishes "tmux session exists" from "Claude is running in it." A tmux session +whose Claude process died is killed and recreated. See +[agent lifecycle](agent-lifecycle.md). + +## The operator's view + +Recovery is invisible in the normal case: restart the agent and it resumes. + +```bash +af up worker # zombie or fresh — either way, a working session +af attach worker # watch it prime itself and continue the open step +af agents list --json # confirm status +``` + +If a formula instance is wedged beyond repair, reset it explicitly rather than hand-editing +state: + +```bash +af sling --agent worker "…" --reset # close beads, remove worktree, clean runtime state +``` + +## Design intent + +The invariant behind all of this: **the context window is a cache, never the source of +truth.** Everything an agent needs to continue — who it is, what step it is on, what the +step requires — must be reconstructible from disk. See +[docs/architecture/overview.md](architecture/overview.md) for how this fits the wider +system design. + +## Further reading + +- [Formulas](formulas.md) — where workflow state comes from +- [Agent lifecycle](agent-lifecycle.md) — session start, work loop, shutdown +- [README](../README.md) · [Using Agentfactory](../USING_AGENTFACTORY.md)