# AGENTS.md

## What this is

Attractor: a local-first, DOT-graph workflow engine for unattended AI
software work. Currently in **Python implementation phase**, built on
the [Claude Agent SDK](https://docs.anthropic.com/en/docs/agents/agent-sdk-overview)
and managed by [UV](https://docs.astral.sh/uv/). A Rust prototype is
archived at the `rust-v0.1` git tag — useful for cross-checking
design decisions but not part of the active build.

[SPEC.md](./SPEC.md) is the source of truth. Check it before non-trivial
decisions; if you disagree, propose an edit rather than diverging silently.
§15 lists open questions — if your work touches one, surface it to the
user, don't decide unilaterally.

## Load-bearing constraints

These are the project's reason for existing, not preferences:

1. **Local-first.** The **engine's** bookkeeping never leaves the machine:
   Attractor's own refs (everything under `refs/heads/attractor/`) are
   never pushed, and engine code never calls a hosted service or assumes
   cloud infrastructure. The engine's only outbound network destination
   is LLM provider APIs (Anthropic, OpenAI, Gemini). Workflows themselves
   are free to push, fetch, or call any service their authors wire up
   from a tool or agent node — that's the workflow's security domain,
   not the engine's. See SPEC §1 and §7.4.
2. **Single user, single machine.** No multi-tenant assumptions, no team-
   collaboration features.
3. **Workflows are versioned graph artifacts users can diff.** Anything
   that can't be expressed in a DOT file probably doesn't belong in a
   workflow.

If a task seems to violate one of these, stop and ask.

## `references/` is read-only

`references/fabro` and `references/attractor` are git submodules — study
material, not part of the build.

- Don't edit files inside `references/`.
- Don't bump submodule pins as part of unrelated work — treat that as its
  own change.
- Cite reference files by path (e.g.
  `references/fabro/lib/crates/fabro-checkpoint/src/branch.rs`) so the user
  can navigate.

## Repo-local agent skills

Reusable guidance for agents lives under `.agents/skills/`. These skills are
versioned with the repo so Codex, Claude Code, and other agents can share the
same workflow.

- Use `.agents/skills/attractor-workflow-author/SKILL.md` when authoring,
  reviewing, or repairing Attractor DOT workflows.

## Commits

- Conventional commit prefixes when categorizable (`feat:`, `fix:`, `docs:`,
  `chore:`, `refactor:`).
- Body explains *why*, not *what*.
- Include `Co-Authored-By:` for AI-generated commits.
- Never force-push `main`. Only push to this repo's `origin`.

## Python conventions

- **Package manager: UV.** `uv sync` resolves and installs all deps
  into the project's `.venv`. `uv add <package>` adds a runtime dep.
  `uv add --dev <package>` adds a dev dep. NEVER edit
  `pyproject.toml`'s `dependencies` list directly — let UV manage it
  so `uv.lock` stays consistent.
- **Run commands via UV.** `uv run pytest`, `uv run ruff check src
  tests`, `uv run pyright`, `uv run attractor <subcommand>`. UV
  guarantees the right venv + dep versions.
- **Linter: ruff.** Style and the obvious bug catches; configured in
  `pyproject.toml` under `[tool.ruff]`.
- **Type checker: pyright.** Strict mode for `src/attractor/`.
  Configured in `pyproject.toml` under `[tool.pyright]`.
- **Test framework: pytest.** Tests live under `tests/`, mirroring
  the module layout. Async tests use `pytest-asyncio` and the
  `@pytest.mark.asyncio` marker.
- **Python version: 3.13.** Pinned in `pyproject.toml`'s
  `requires-python`; UV provisions an interpreter if the system one
  doesn't match.
- **No conda, no system pip.** UV is the only supported entry point.

## When code arrives

The package layout in SPEC.md §12 is the plan. New modules land under
`src/attractor/<name>/`. The `checkpoint` module should remain
independently usable — minimal dependencies (`pygit2` only), no
upstream `attractor.*` imports.

Definition of Done for v0.1 is in SPEC.md §16. Match it.

## Requirements tracing

This repo uses [`traceable-reqs`](https://github.com/BigscreenVR/traceable-reqs)
to prove that every requirement has evidence in the tree. The manifest
`traceable-reqs.toml` lists the requirement IDs and their required
stages (`doc`, `impl`, `unit`, `int`). The manifest is the contract.

**Tags are mandatory, not advisory.** Requirement-bearing code without a
tag is considered untraced and the work is not done.

### What "requirement-bearing" means

If a function, type, test, doc section, or workflow exists *because of*
a specific requirement in `traceable-reqs.toml`, it carries a tag.
That covers nearly everything you'll write — handlers, parsers,
checkpoint primitives, agent tools, CLI subcommands, integration
tests, and the SPEC sections that describe them. Generic utility code
that isn't called out by a requirement doesn't need a tag, but if
you're writing such code you should be asking why.

### Required stages per requirement

`traceable-reqs.toml` declares `required_stages` per requirement.
Defaults:

- Most reqs: `doc`, `impl`, `unit`
- Architecture / build-graph reqs (e.g. `REQ-ARCH-THREE-LAYERS`): `doc`, `impl`
- v0.1 Definition-of-Done reqs (`REQ-DOD-*`): `doc`, `impl`, `int`

If you ship an `[impl->REQ-X]` tag, you must also ship the matching
`[unit->REQ-X]` (or `[int->REQ-X]` for DoD requirements) in the same
change. Half-tagged code is incomplete.

### Tag format and placement

- Format: an opening bracket, the stage name, a literal
  hyphen-greater-than (`->`), the REQ-ID, then a closing bracket.
  Canonical example: `[impl->REQ-X]` — `REQ-X` is a reserved
  placeholder declared in `traceable-reqs.toml` so example tags in
  this doc don't trip the scanner.
- Place tags **on or directly above** the function, test, doc
  section, or workflow step they prove. In markdown wrap as
  `<!-- [impl->REQ-X] -->`; in Python use line comments
  (`# [impl->REQ-X]`); in shell use the same `# [impl->REQ-X]`.
- **Tags at the top of a file are noisy and wrong.** A file-top tag
  claims the whole file proves the requirement, which is almost
  never true and breaks `traceable-reqs`' line-precision reporting.
- IDs must match `REQ-[A-Z0-9-]+` (uppercase, digits, hyphens). New
  IDs go in `traceable-reqs.toml` **before** they appear in tags —
  otherwise the scan flags them as unknown.

### Definition of Done

Before reporting a requirement-bearing change complete:

1. The change has `[impl->REQ-X]` and `[unit->REQ-X]` (or
   `[int->REQ-X]`)-shaped tags for every REQ it advances.
2. `traceable-reqs check --json` reports no `missing_stage` for the
   affected IDs.
3. Your return summary lists the REQ IDs the change touched, broken
   down by stage.

If `traceable-reqs` isn't installed on your host, invoke the
[`traceable-reqs` skill](https://github.com/BigscreenVR/traceable-reqs)
to install it before claiming the gate is satisfied.

### When there's no requirement for what you're building

Stop and ask. Either the requirement is missing from the manifest
(add it first, with the user's approval), or the work shouldn't be
done in this repo. Requirements are the source of truth.
