Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
76 changes: 76 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Docs

on:
pull_request:
branches: [main]
paths:
- "docs/**"
- "spec/**"
- "proposals/**"
- "GOVERNANCE.md"
- "CHANGELOG.md"
- "mkdocs.yml"
- "pyproject.toml"
- ".github/workflows/docs.yml"
push:
branches: [main]
paths:
- "docs/**"
- "spec/**"
- "proposals/**"
- "GOVERNANCE.md"
- "CHANGELOG.md"
- "mkdocs.yml"
- "pyproject.toml"
- ".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
# Bind the secret-presence check to a job-level env var so the
# deploy step's ``if:`` can gate on it. Protects against a missing
# or rotated CF secret turning the main branch's status red.
env:
HAS_CF_SECRETS: ${{ secrets.CF_API_TOKEN != '' && secrets.CF_ACCOUNT_ID != '' }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
enable-cache: true

- name: Sync deps (docs group)
# ``--frozen`` requires the lockfile to be up-to-date and refuses
# to update it during sync — guarantees reproducible CI builds.
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, and only when the CF secrets
# are configured. PR builds prove the site builds but do not
# deploy. If secrets are missing, skip rather than fail — the
# build step above is the source of truth for "does the site
# compile cleanly."
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && env.HAS_CF_SECRETS == 'true'
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
command: pages deploy site --project-name=openarmature-spec-docs --branch=main
Comment thread
chris-colinsky marked this conversation as resolved.
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
.claude/
.claude/

# MkDocs build output (rendered docs site).
site/

# Python tooling for the docs build (uv venv).
.venv/
__pycache__/
74 changes: 36 additions & 38 deletions CHANGELOG.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/capabilities/graph-engine.md
1 change: 1 addition & 0 deletions docs/capabilities/llm-provider.md
1 change: 1 addition & 0 deletions docs/capabilities/observability.md
1 change: 1 addition & 0 deletions docs/capabilities/pipeline-utilities.md
1 change: 1 addition & 0 deletions docs/capabilities/prompt-management.md
1 change: 1 addition & 0 deletions docs/changelog.md
1 change: 1 addition & 0 deletions docs/governance.md
157 changes: 157 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
hide:
- toc
---

# OpenArmature

**A workflow framework for LLM pipelines and tool-calling agents — defined as
a language-agnostic specification.** Implementations conform to the same
behavior via canonical fixtures, so the same workload runs the same way
regardless of language or runtime.

[Read the charter](openarmature.md){ .md-button .md-button--primary }
[View on GitHub](https://github.com/LunarCommand/openarmature-spec){ .md-button target="_blank" rel="noopener" }

---

## Pipelines and agents — one substrate

Most frameworks pick a side: deterministic LLM pipelines or autonomous
tool-calling agents. OpenArmature treats both as applications of the same
primitives — typed graphs, conditional edges, middleware, checkpointing.
A multi-stage content-extraction pipeline and a tool-loop agent are both
just compiled graphs.

<div class="grid cards" markdown>

- :material-pipe-leak:{ .lg .middle } &nbsp; __Deterministic LLM pipelines__

---

Topology pinned at compile time. Each node does one thing — LLM call,
parse, validate, persist. Per-node retries with explicit budgets;
observability captures every step.

*Use cases: content extraction, classification cascades, multi-stage
analysis, document refinery.*

- :material-robot:{ .lg .middle } &nbsp; __Tool-calling agents__

---

Same primitives, different shape: LLM node + tool-dispatch node +
conditional edge back to the LLM. Tool-call envelope is normalized;
multi-turn agents resume mid-conversation via checkpoint/resume.

*Use cases: research agents, code-generation loops, data-extraction
bots, multi-turn assistants.*

</div>

---

## What sets it apart

<div class="grid cards two-col" markdown>

- :material-eye-check:{ .lg .middle } &nbsp; __Transparency over abstraction__

---

Provider responses surface verbatim alongside normalized fields.
Internal events are observable via hook points at every node
boundary. The framework adds structure; it never hides what the
underlying tools returned.

- :material-shield-check:{ .lg .middle } &nbsp; __Compile-time safety__

---

Bad graph shapes fail at compile, not at run. Reducer conflicts,
dangling edges, multiple outgoing edges from a non-conditional
node — all caught before the first LLM call.

- :material-puzzle:{ .lg .middle } &nbsp; __Composable, not prescriptive__

---

Middleware is a primitive. Retry and timing ship canonical;
everything else composes from the same protocol. No magic
decorators, no global state — just explicit composition.

- :material-radar:{ .lg .middle } &nbsp; __Observability built in__

---

OpenTelemetry mapping is normative, not bolt-on. Span hierarchy
mirrors graph structure; cross-backend correlation IDs flow with
every invocation; detached trace mode keeps high-volume fan-outs
readable.

</div>

---

## Reference implementation

!!! abstract "openarmature-python — currently in active development"

The canonical reference implementation. Ships the full API for graph
engine, pipeline utilities, LLM provider, observability, and prompt
management — all driven by the conformance fixtures defined in this
repo.

[Visit the repo on GitHub :octicons-link-external-16:](https://github.com/LunarCommand/openarmature-python){ .md-button target="_blank" rel="noopener" }

A TypeScript implementation is on the roadmap. Both will pin behavior to
this spec.

---

## Why a spec, not a library?

LLM workflow frameworks usually ship as opinionated libraries. Pick the
wrong one and you're rewriting your pipeline; ship in two languages and
you're maintaining two divergent codebases that drift over time.
OpenArmature flips the model: the contract lives here as a spec with
conformance fixtures, and reference implementations in each language port
to the same behavior. Same workload, multiple runtimes, no behavioral
drift.

<div class="grid cards" markdown>

- :material-check-decagram:{ .lg .middle } &nbsp; __Behavior pinned by fixtures__

---

118 conformance fixtures across five capabilities. Implementations run
them; if they pass, behavior matches every other conforming runtime.
No "implementation-defined" footguns.

- :material-cube-unfolded:{ .lg .middle } &nbsp; __One contract, many runtimes__

---

Reference implementations in Python (active) and TypeScript (planned).
The same pipeline definition, the same observable trace shape, the
same retry semantics — across languages.

- :material-arrow-decision:{ .lg .middle } &nbsp; __Open evolution__

---

New behavior lands through numbered RFC-style proposals reviewed in
the open. Once accepted, a proposal's text is immutable; superseding
proposals link the chain forward. No silent drift between releases.

</div>

---

## How it evolves

OpenArmature is governed by a numbered proposal system: every behavioral
change starts as a Draft RFC, is reviewed in the open, lands with a
SemVer bump, and is frozen in proposal text once accepted. The capability
specs are the source of truth; proposals are the change history.
17 changes: 17 additions & 0 deletions docs/javascripts/header-link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Wrap the header site title in a link to the site root. Material's
// default markup leaves the title as plain text; only the small logo
// icon to the left is clickable. Wrapping it as an <a> matches the
// behavior most readers expect.
document$.subscribe(function () {
var topic = document.querySelector(".md-header__title .md-ellipsis");
if (!topic || topic.querySelector("a")) return;
var logo = document.querySelector(".md-header__button.md-logo");
var href = (logo && logo.getAttribute("href")) || ".";
var text = topic.textContent.trim();
var link = document.createElement("a");
link.href = href;
link.textContent = text;
link.className = "md-header__title-link";
topic.innerHTML = "";
topic.appendChild(link);
});
6 changes: 6 additions & 0 deletions docs/javascripts/tablesort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
document$.subscribe(function () {
var tables = document.querySelectorAll("article table:not([class])");
tables.forEach(function (table) {
new Tablesort(table);
});
});
6 changes: 6 additions & 0 deletions docs/javascripts/tablesort.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 0 additions & 34 deletions docs/openarmature.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,6 @@ framework for both deterministic LLM pipelines and tool-calling agents.

---

## Table of Contents

- [1. Thesis](#1-thesis)
- [1.1 The Gap: LLM Pipelines Have No Home](#11-the-gap-llm-pipelines-have-no-home)
- [1.2 The Insight: Pipelines and Agents Share Primitives](#12-the-insight-pipelines-and-agents-share-primitives)
- [1.3 What OpenArmature Proposes](#13-what-openarmature-proposes)
- [2. Evidence](#2-evidence)
- [2.1 Projects Behind the Design](#21-projects-behind-the-design)
- [2.2 Distilled Patterns](#22-distilled-patterns)
- [3. Architecture](#3-architecture)
- [3.1 Design Principles](#31-design-principles)
- [3.2 Package Structure](#32-package-structure)
- [3.3 Architecture Diagram](#33-architecture-diagram)
- [4. Module Specifications](#4-module-specifications)
- [4.1 Graph Engine](#41-graph-engine)
- [4.2 Pipeline Utilities](#42-pipeline-utilities)
- [4.3 LLM Provider Abstraction](#43-llm-provider-abstraction)
- [4.4 Tool System and MCP](#44-tool-system-and-mcp)
- [4.5 Prompt Management](#45-prompt-management)
- [4.6 Observability](#46-observability)
- [4.7 Evaluation](#47-evaluation)
- [4.8 Logging](#48-logging)
- [5. Canonical Examples](#5-canonical-examples)
- [5.1 LLM Pipeline (Content Analysis)](#51-llm-pipeline-content-analysis)
- [5.2 Tool-Calling Agent](#52-tool-calling-agent)
- [5.3 Hybrid (Pipeline with Agent Step)](#53-hybrid-pipeline-with-agent-step)
- [6. Multi-Language Strategy](#6-multi-language-strategy)
- [7. Implementation Plan](#7-implementation-plan)
- [7.1 Phasing](#71-phasing)
- [7.2 Risks](#72-risks)
- [7.3 What Is Hard](#73-what-is-hard)

---

## 1. Thesis

### 1.1 The Gap: LLM Pipelines Have No Home
Expand Down
28 changes: 28 additions & 0 deletions docs/proposals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Proposals

OpenArmature evolves through numbered RFC-style proposals. Each proposal targets a capability spec and
moves through a `Draft → Accepted` lifecycle. Once `Accepted`, a proposal's text is **immutable** — further
changes happen via new proposals that supersede the prior. See [Governance](governance.md) for the full
lifecycle and the proposal template.

| # | Title | Capability | Status |
|------|---------------------------------------------|---------------------|----------|
| [0001](proposals/0001-graph-engine-foundation.md) | Foundation | graph-engine | Accepted |
| [0002](proposals/0002-subgraph-explicit-mapping.md) | Subgraph explicit I/O mapping | graph-engine | Accepted |
| [0003](proposals/0003-node-boundary-observer-hooks.md) | Node-boundary observer hooks | graph-engine | Accepted |
| [0004](proposals/0004-pipeline-utilities-middleware.md) | Middleware | pipeline-utilities | Accepted |
| [0005](proposals/0005-pipeline-utilities-parallel-fan-out.md) | Parallel fan-out | pipeline-utilities | Accepted |
| [0006](proposals/0006-llm-provider-core.md) | Core abstraction (OpenAI-compatible) | llm-provider | Accepted |
| [0007](proposals/0007-observability-otel-span-mapping.md) | OpenTelemetry span mapping | observability | Accepted |
| [0008](proposals/0008-pipeline-utilities-checkpointing.md) | Checkpointing | pipeline-utilities | Accepted |
| [0009](proposals/0009-pipeline-utilities-per-instance-fan-out-resume.md) | Per-instance fan-out resume | pipeline-utilities | Draft |
| [0010](proposals/0010-drain-timeout.md) | Bounded drain — configurable timeout | graph-engine | Draft |
| [0011](proposals/0011-pipeline-utilities-parallel-branches.md) | Parallel branches | pipeline-utilities | Accepted |
| [0012](proposals/0012-graph-engine-completed-event-after-edges.md) | Completed event fires after edge evaluation | graph-engine | Accepted |
| [0013](proposals/0013-fan-out-config-on-node-event.md) | Fan-out config on node event | graph-engine | Accepted |
| [0014](proposals/0014-pipeline-utilities-state-migration.md) | State migration hooks for checkpoints | pipeline-utilities | Accepted |
| [0015](proposals/0015-llm-provider-multimodal-images.md) | Image content blocks for user messages | llm-provider | Accepted |
| [0016](proposals/0016-llm-provider-structured-output.md) | Structured output | llm-provider | Accepted |
| [0017](proposals/0017-prompt-management-core.md) | Prompt management core | prompt-management | Accepted |

Click any column header to sort.
1 change: 1 addition & 0 deletions docs/proposals/0001-graph-engine-foundation.md
1 change: 1 addition & 0 deletions docs/proposals/0002-subgraph-explicit-mapping.md
1 change: 1 addition & 0 deletions docs/proposals/0003-node-boundary-observer-hooks.md
1 change: 1 addition & 0 deletions docs/proposals/0004-pipeline-utilities-middleware.md
1 change: 1 addition & 0 deletions docs/proposals/0005-pipeline-utilities-parallel-fan-out.md
1 change: 1 addition & 0 deletions docs/proposals/0006-llm-provider-core.md
1 change: 1 addition & 0 deletions docs/proposals/0007-observability-otel-span-mapping.md
1 change: 1 addition & 0 deletions docs/proposals/0008-pipeline-utilities-checkpointing.md
Loading
Loading