# Attractor

> A local-first, DOT-graph workflow engine for unattended AI software work.

**Status:** v0.1 dogfood-ready. The Python implementation ships the §16
Definition-of-Done feature set (DOT parser, engine + checkpoint/resume,
human gates, tool/agent nodes, Claude provider, seven CLI subcommands).
See [SPEC.md](./SPEC.md) for the design and `handoff.md` for current
priorities.

---

## What this is

In dynamical systems, an *attractor* is a state a system evolves toward.
Workflows here define the attractors — completion nodes and human gates —
and a run is the trajectory an AI coding agent follows to reach them. The
framing borrows from lights-out ("dark factory") manufacturing, where the
production line keeps running without humans on the floor: a human
intervenes only at explicit gates.

Workflows are Graphviz DOT files — diffable, reviewable, version-controlled.
All run state lives in local git refs and is never pushed to a remote by
the engine. No cloud sandboxes, no hosted service. The engine's only
outbound network traffic is LLM API calls; workflows themselves can do
whatever their authors wire up.

---

## Prerequisites

- **Python 3.13** — the project pins to 3.13 (`requires-python` in
  `pyproject.toml`). [UV](https://docs.astral.sh/uv/) will provision an
  interpreter if the system one doesn't match.
- **[UV](https://docs.astral.sh/uv/)** — the only supported package
  manager. `uv sync` resolves and installs all deps into `.venv`; `uv
  run <cmd>` runs commands inside it.
- **`git` ≥ 2.30** — the engine shells out for worktree management and
  checkpoint commits, and `pygit2` (libgit2) handles ref/tree IO.
- **`pi` on PATH** *(required for agent nodes only)* — Attractor's
  agent layer is the [pi](https://pi.dev/) harness
  (`@earendil-works/pi-coding-agent`), driven over `pi --mode rpc`. pi
  provides the conversation loop, tools, skills, and **multi-provider
  routing** (Anthropic, OpenAI/Codex, Gemini, Copilot, ...). Tool-only
  workflows (parallelogram + human-gate nodes, no `box` agent nodes)
  run without it. Install with `npm i -g @earendil-works/pi-coding-agent`.
- **A provider credential for pi** *(required for agent nodes only)* —
  authenticate pi once (`pi` then `/login` for a Claude Pro/Max or
  ChatGPT Plus/Pro Codex subscription, or set a provider env var like
  `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `GEMINI_API_KEY`). pi owns
  credential resolution (SPEC §9.4); the engine never reads or writes
  credentials, and the tool-node executor scrubs provider keys from
  tool-script child processes (SPEC §6.8).
- **`pwsh` (PowerShell 7) on Windows** — SPEC §6.8 mandates `pwsh
  -NoProfile -Command` for tool nodes on Windows. PowerShell 5.1
  (`powershell.exe`, built into Windows) is *not* supported and there
  is no silent fallback — see SPEC §15 for the workflow-portability
  rationale. Install PowerShell 7 from the
  [Microsoft docs](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell).
  Unix-likes use `sh -c` and require nothing extra.

---

## Install

The repo vendors reference implementations as git submodules. First clone:

```bash
git clone --recurse-submodules https://github.com/BigscreenVR/attractor
cd attractor
```

If you already cloned without `--recurse-submodules`:

```bash
git submodule update --init --recursive
```

Then install the project and its dev dependencies into `.venv`:

```bash
uv sync
```

Sanity-check the install:

```bash
uv run attractor --version
uv run pytest -q          # full test suite (the live-LLM test is skipped unless ATTRACTOR_LIVE_LLM=1)
```

---

## Quickstart

Run a tool-only workflow that doesn't need the LLM at all:

```bash
uv run attractor validate workflows/tool-only.dot
uv run attractor run workflows/tool-only.dot
```

The run streams events to stderr (including the prompt and accepted
choices at each human gate). When a gate fires, the process exits with
a `Run paused at gate` banner and the run id; resume by submitting one
of the choices. For `tool-only.dot`'s release gate the choices are
`ship` and `reject`:

```bash
uv run attractor run respond <run-id> ship
uv run attractor run resume <run-id>
```

Inspect what happened:

```bash
uv run attractor run list                 # active runs with worktrees
uv run attractor run show <run-id>        # journal, history, token totals
```

Run state lives under `refs/heads/attractor/run/<run-id>/...`. Browse
the journal with ordinary git tooling:

```bash
git log refs/heads/attractor/run/<run-id>/state
git diff refs/heads/attractor/run/<run-id>/worktree main
```

`workflows/` carries a handful of example graphs (`tool-only.dot`,
`triage.dot`, `implement.dot`, `multi-gate.dot`, `verify`/`fixup` in
`preflight.dot`, etc.) — use them as starting points.

---

## Reading order

1. **[SPEC.md](./SPEC.md)** — what we're building, why, and the open questions.
2. **[handoff.md](./handoff.md)** — current state, gotchas, and what's next.
3. **[AGENTS.md](./AGENTS.md)** — conventions for working in this repo
   (requirements tracing, commit style, gates).
4. `references/attractor/attractor-spec.md` — orchestrator concepts.
5. `references/attractor/coding-agent-loop-spec.md` — agent loop concepts.
6. `references/fabro/lib/crates/fabro-checkpoint/` — local checkpoint design,
   the cleanest thing to mine from fabro.

---

## References

The `references/` directory is study material, not a build dependency.

- **[references/fabro](./references/fabro)** — StrongDM's Rust implementation
  of an Attractor-style system. Source for design ideas, especially the
  `fabro-checkpoint` crate's git-as-key-value-store pattern.
- **[references/attractor](./references/attractor)** — open NLSpecs (natural-
  language specs) defining the orchestrator, agent loop, and unified LLM
  client patterns we're building on.

Both are pinned to specific commits and are not compiled into the build.
