Orientation for coding agents working in this repo — i.e., agents
contributing to openarmature itself. README.md covers what the project
is and how to use it; this file covers things that aren't obvious from
reading the code.
Two AGENTS.md files in this project. Different audiences.
- This file (
./AGENTS.md, at the repo root) — for agents working on the openarmature codebase. Package layout, test layout, tooling, spec-submodule discipline, commit conventions.src/openarmature/AGENTS.md(shipped in the wheel) — for agents working in user codebases that depend on openarmature. Capability contracts, common patterns, non-obvious shapes, example index. Generated byscripts/build_agents_md.pyfrom canonical sources; committed and CI-drift-checked.
This repo is a Python implementation of
openarmature-spec.
Behavior is defined by the spec; this repo executes it.
- The spec lives at
openarmature-spec/as a git submodule pinned to a released tag. Don't edit files in the submodule. - The pin tracks the latest Accepted spec version, not the tip of
openarmature-spec/main.mainmay contain Draft proposals merged into the spec text; only Accepted releases ship normative behavior, and the implementation conforms to the pinned Accepted release. - Behavior changes that aren't already in the Accepted spec require a proposal in the spec repo first, not a PR here.
- To bump the spec submodule after a new Accepted release:
cd openarmature-spec && git checkout <tag>, then bump the three places that track the version (below).
Proposals travel Draft → Accepted in the spec repo (see
openarmature-spec/GOVERNANCE.md for the format and flow). Proposals
live in openarmature-spec/proposals/; canonical spec text in
openarmature-spec/spec/<capability>/.
When implementing a feature, read the relevant Accepted proposal first — don't infer behavior from existing impl alone. Draft proposals don't ship; their text may change before acceptance.
tool.openarmature.spec_versioninpyproject.toml__spec_version__insrc/openarmature/__init__.py- The submodule commit (must match a released spec tag, e.g.
v0.10.0)
tests/test_smoke.py asserts the first two match. The third is enforced
by convention.
src/openarmature/graph/— graph engine (State, GraphBuilder, CompiledGraph, edges, projections, fan-out)src/openarmature/llm/— LLM Provider Protocol + OpenAIProvider; HTTP error classification + retry helperssrc/openarmature/checkpoint/— checkpointing protocol + in-memory and filesystem backendssrc/openarmature/observability/—[otel]extra; OTel observer + log bridge + correlation primitivessrc/openarmature/middleware/— pipeline-utility middleware
tests/conformance/— runs the spec's YAML fixtures against the engine via an adapter. Drives most of the behavior coverage.tests/unit/— fills coverage gaps the conformance suite doesn't reach:edge_exception,reducer_error,state_validation_error,SubgraphNode.run, projection variants, frozen-state mutation, etc.tests/test_smoke.py— version sync.
uvfor everything. Don't usepipdirectly.- Pyright strict mode is enforced (
pyproject.toml). Annotations are not optional. - Ruff for lint + format. Pre-commit hook runs
ruff formatautomatically — the file you committed may not be the file in the next diff. pytest-asynciowithasyncio_mode = "auto"—async def test_...works with no decorator.
uv run pytest -q # all tests
uv run pytest tests/conformance/ -v # spec conformance only
uv run ruff check . && uv run ruff format # lint + format
uv run pyright src/ tests/ # type check
uv run mkdocs serve # preview the docs site locallyBranch names use <type>/<kebab-case-description> (3–5 words). Allowed
types:
feature/— new functionalityfix/— bug fixesrefactor/— restructuring without behavior changechore/— tooling, deps, config, housekeepingschema/— data model changes
For ticketed work, embed the ID:
feature/PROJ-123-short-description.
Commit subjects follow the 50/72 rule — subject ≤ 50 chars (hard cap 72), imperative mood, capitalized, no trailing period. Body wrapped at 72 columns. Body explains what and why, not how.
User-facing docs live in docs/ and build via MkDocs Material; the
deployed site is at openarmature.ai. CI build + deploy is in
.github/workflows/docs.yml. Local preview: uv run mkdocs serve.
Stateisfrozen=TrueANDextra="forbid". Nodes that return an undeclared field surface as astate_validation_error, not a silent drop.- Conditional edges over-approximate at compile time (a conditional from node X is treated as reaching every node), so the unreachable-node check is sound but not tight.
- Each node has exactly one outgoing edge. Branching is via conditional edges, not multiple statics.
ENDis a distinct sentinel object, not a reserved string. Use the exportedENDconstant.
In scope:
- Graph engine + the spec's runtime contract.
- Pipeline utilities (rate limiting, structured-output retry helpers).
- Observability via OTel observer (under
[otel]extra). - Checkpointing (in-memory + filesystem backends).
- LLM Provider Protocol + the canonical OpenAI implementation.
Out of scope, deferred to sibling packages at v1.0:
openarmature-otel— eventual extraction of the OTel observer for projects that don't want it in core.openarmature-eval— evaluation framework.
Behavior changes outside the Accepted spec require a spec proposal first.