Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8c21036
docs: scaffold MkDocs site + AGENTS.md
chris-colinsky May 12, 2026
c4a8dee
examples: migrate openarmature-examples into the repo
chris-colinsky May 12, 2026
af7be1d
llm: promote classify_http_error + parse_retry_after to public
chris-colinsky May 12, 2026
e9cbad6
docs: Quickstart page + pytest-examples wiring
chris-colinsky May 12, 2026
94d89b8
docs: Provider authoring guide + skeleton
chris-colinsky May 12, 2026
b20fd6a
docs: API reference pages via mkdocstrings
chris-colinsky May 12, 2026
6b6c8bd
docs: wire llmstxt plugin + mike versioning config
chris-colinsky May 12, 2026
3abe9be
docs: landing page with hero + feature grid
chris-colinsky May 12, 2026
8cc6f83
docs: conceptual overview reorganized for first-touch readers
chris-colinsky May 12, 2026
c4e84d5
ci: install examples group so smoke test imports work
chris-colinsky May 12, 2026
63180a6
docs: rewrite concept pages against actual code + spec
chris-colinsky May 12, 2026
488dea9
docs: fix Provider authoring Usage skeleton + checklist wording
chris-colinsky May 12, 2026
98b85ff
docs: fix contributing/index.md dead RELEASING.md link
chris-colinsky May 12, 2026
65dc890
docs: drop the non-existent ``"all"`` phases shortcut
chris-colinsky May 12, 2026
54ae52a
docs: lead install instructions with uv, pip as alternative
chris-colinsky May 12, 2026
ca8ed2a
docs: move RELEASING.md out of the public repo
chris-colinsky May 12, 2026
27177ab
docs: scrub spec references from rendered docs + source docstrings
chris-colinsky May 12, 2026
19cf51c
docs: Model Providers section + light/dark theme polish
chris-colinsky May 12, 2026
7b0490e
docs: tighten spec breadcrumbs from spec review
chris-colinsky May 12, 2026
babb418
docs: PR #32 review fixes
chris-colinsky May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
# ``opentelemetry.*`` symbols come back Unknown and pyright
# produces hundreds of false-positive errors on
# ``tests/unit/test_observability_otel.py``.
run: uv sync --frozen --all-extras
run: uv sync --frozen --all-extras --group examples

- name: Lint (ruff check)
run: uv run ruff check .
Expand Down
69 changes: 69 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Docs

on:
pull_request:
branches: [main]
paths:
- "docs/**"
- "mkdocs.yml"
- "pyproject.toml"
- "src/**"
- ".github/workflows/docs.yml"
push:
branches: [main]
paths:
- "docs/**"
- "mkdocs.yml"
- "pyproject.toml"
- "src/**"
- ".github/workflows/docs.yml"

# Least-privilege: read code for the build; ``deployments: write`` lets
# the Cloudflare Pages action attach a deployment status to the commit.
permissions:
contents: read
deployments: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
# mkdocstrings imports the package source; the spec submodule
# is not referenced by docs but keep the conventional recursive
# checkout consistent with ``ci.yml``.
submodules: recursive

- name: Install uv
uses: astral-sh/setup-uv@v8.1.0

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Docs' step
Uses Step
uses 'astral-sh/setup-uv' with ref 'v8.1.0', not a pinned commit hash
Comment thread
chris-colinsky marked this conversation as resolved.
with:
enable-cache: true

- name: Sync deps (docs group)
# ``--group docs`` installs the project itself in editable mode
# plus the ``docs`` dependency group. mkdocstrings needs to
# import ``openarmature`` to introspect docstrings, which the
# editable install provides.
run: uv sync --frozen --group docs

- name: Build site (strict)
# ``--strict`` fails the build on any warning. Catches broken
# internal links, missing nav references, plugin misconfig
# early — before the deploy step.
run: uv run mkdocs build --strict

- name: Deploy to Cloudflare Pages
# Only deploy on pushes to main. PR builds prove the site
# builds but do not deploy.
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: cloudflare/wrangler-action@v3

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Docs' step
Uses Step
uses 'cloudflare/wrangler-action' with ref 'v3', not a pinned commit hash
Comment thread
chris-colinsky marked this conversation as resolved.
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
command: pages deploy site --project-name=openarmature-docs --branch=main
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ dist/
.mypy_cache/
.pyright/

# MkDocs build output + plugin cache
site/
.cache/

# Coverage
.coverage
.coverage.*
Expand Down
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ repos:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
# ``mkdocs.yml`` uses ``!!python/name:...`` tags (e.g. for
# Material's emoji extension) that safe_load can't parse.
# ``--unsafe`` skips constructor validation but still catches
# malformed YAML, which is the bug class this hook is for.
args: ["--unsafe"]
- id: check-toml
- id: check-merge-conflict
- id: check-added-large-files
Expand Down
141 changes: 141 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# AGENTS.md

Orientation for coding agents working in this repo. `README.md` covers what
the project is and how to use it; this file covers things that aren't
obvious from reading the code.

## Spec is the source of truth

This repo is a Python implementation of
[`openarmature-spec`](https://github.com/LunarCommand/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`. `main` may 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).

## Spec proposal lifecycle

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.

## Three places hold the spec version — keep them in sync

- `tool.openarmature.spec_version` in `pyproject.toml`
- `__spec_version__` in `src/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.

## Package layout

- `src/openarmature/graph/` — graph engine (State, GraphBuilder,
CompiledGraph, edges, projections, fan-out)
- `src/openarmature/llm/` — LLM Provider Protocol + OpenAIProvider; HTTP
error classification + retry helpers
- `src/openarmature/checkpoint/` — checkpointing protocol + in-memory
and filesystem backends
- `src/openarmature/observability/` — `[otel]` extra; OTel observer +
log bridge + correlation primitives
- `src/openarmature/middleware/` — pipeline-utility middleware

## Test layout

- `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.

## Tooling

- `uv` for everything. Don't use `pip` directly.
- Pyright **strict mode** is enforced (`pyproject.toml`). Annotations
are not optional.
- Ruff for lint + format. Pre-commit hook runs `ruff format`
automatically — the file you committed may not be the file in the
next diff.
- `pytest-asyncio` with `asyncio_mode = "auto"` — `async def test_...`
works with no decorator.

## Common commands

```bash
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 locally
```

## Branch + commit conventions

Branch names use `<type>/<kebab-case-description>` (3–5 words). Allowed
types:

- `feature/` — new functionality
- `fix/` — bug fixes
- `refactor/` — restructuring without behavior change
- `chore/` — tooling, deps, config, housekeeping
- `schema/` — 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*.

## Docs

User-facing docs live in `docs/` and build via MkDocs Material; the
deployed site is at `docs.openarmature.ai`. CI build + deploy is in
`.github/workflows/docs.yml`. Local preview: `uv run mkdocs serve`.

## Engine design notes that are easy to miss

- `State` is `frozen=True` AND `extra="forbid"`. Nodes that return an
undeclared field surface as a `state_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.
- `END` is a distinct sentinel object, not a reserved string. Use the
exported `END` constant.

## In scope / out of scope

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.
47 changes: 1 addition & 46 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,3 @@
# CLAUDE.md

Orientation for Claude Code sessions in this repo. The `README.md` covers what the project is and how to use it; this file covers things that aren't obvious from reading the code.

## Spec is the source of truth

This repo is a Python implementation of [`openarmature-spec`](https://github.com/LunarCommand/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.
- To bump the spec: `cd openarmature-spec && git checkout <tag>`, then bump the three places that track the spec version (see below).
- Behavior changes that aren't already in the spec require a proposal in the spec repo first, not a PR here.

## Three places hold the spec version — keep them in sync

- `tool.openarmature.spec_version` in `pyproject.toml`
- `__spec_version__` in `src/openarmature/__init__.py`
- The submodule commit (must match a released tag, e.g. `v0.1.1`)

`tests/test_smoke.py` asserts the first two match. The third is enforced by convention.

## Test layout

- `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.

## Tooling

- `uv` for everything. Don't use `pip` directly.
- Pyright **strict mode** is enforced (`pyproject.toml`). Annotations are not optional.
- Ruff for lint + format. Pre-commit hook runs `ruff format` automatically — the file you committed may not be the file in the next diff.
- `pytest-asyncio` with `asyncio_mode = "auto"` — `async def test_...` works with no decorator.

## Common commands

```bash
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
```

## Engine design notes that are easy to miss

- `State` is `frozen=True` AND `extra="forbid"`. Nodes that return an undeclared field surface as a `state_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.
- `END` is a distinct sentinel object, not a reserved string. Use the exported `END` constant.
See AGENTS.md.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ Python reference implementation of [OpenArmature](https://github.com/LunarComman
## Install

```bash
pip install openarmature # core
pip install 'openarmature[otel]' # with OpenTelemetry observability
uv add openarmature # core
uv add 'openarmature[otel]' # with OpenTelemetry observability
# or, with pip:
pip install openarmature
pip install 'openarmature[otel]'
```

Or for editable local development:
For editable local development:

```bash
uv add --editable /path/to/openarmature-python
Expand Down
Loading
Loading