AgentDeck is a declarative runtime harness for multi-agent systems and workflows (OpenAI Agents SDK, plus AgentDeck-native @tool/@workflow).
Core standards: docs/engineering/: the linked suite of binding engineering law:
docs/engineering/principles.md: Product philosophy, and the standard a feature is held to (Q1-Q7).docs/engineering/coding-standards.md: Binding front door for every code change.docs/engineering/coding-agents.md: Mandatory rules for coding agents.docs/engineering/documentation.md: Binding for every page underdocs-site/content/.- Specialized standards:
architecture.md,runtime-contracts.md,testing.md,dependencies.md,repository-policy.md,import-boundaries.md. docs/patterns/: the project's taste as real good/bad pairs. Read the file for your concern before writing; match the good side.
Every file under docs/ declares its status on line 3, from four words, so law is distinguishable from archaeology without opening it:
| Status | Means | Where |
|---|---|---|
| Binding | a change that violates it is wrong | all of docs/engineering/ and docs/patterns/, the 16 files above |
| Design of record | live rulings; amend it rather than contradicting it | docs/design/ including protocols/, and docs/delivery/, whose plans are cited by ruling number from tests and source |
| Reference | true, not law | docs/brand/README.md |
| History | a state the project has left. Usually it is deleted instead: git is the archive, and #734 deleted the eight that nothing read | nothing, currently |
A version in a History title is that document's own subject, not a stale claim about this repository.
You build the software. AgentDeck handles the agentic machinery. Simple on the outside, elegant on the inside.
Suspending a run on a human and resuming it days later on another machine is hard. So is cancelling a live run from another process, one conversation that spans turns and surfaces and restarts, and one run readable over HTTP, stdio and AG-UI at once. Our position is that we write those once, carefully, instead of every team writing them again under deadline. These subsystems are sophisticated inside and must not make application code sophisticated. When a design makes the inside simpler by making the outside harder, we have failed, and that is not a trade we are allowed to make quietly.
- User owns intent, AgentDeck owns machinery: Users define agents, tools, workflows, skills, and context. AgentDeck manages run identity, execution lifecycle, persistence, event streams, cancellation, and concurrency.
- Complexity grows slower than capability: Needing concurrency, a human, another agent or a protocol costs one call, not a redesign. An application is never restructured because its requirements got more serious.
- Abstractions must delete complexity: An abstraction is successful only when the caller needs to know less. Never leak internal plumbing (stores, resolvers, internal contexts, log keys) into public user APIs.
- One obvious path first: One clean, standard path for common tasks (
await deck.run(...)). Advanced knobs remain escape hatches, never obstacles. - We are not exempt: Finding that AgentDeck's own API has transferred complexity to the user is a defect, not a preference.
The standard. Every major feature answers Q1-Q7 in docs/engineering/principles.md, and a review cites them by key. Q2, can a developer who does not need it ignore it completely, kills most proposals. Q4, are we absorbing complexity or transferring it to the user, is the one we most often fail. A bugfix answers neither.
Deck is the single composition root: Deck(...) or Deck.from_project() (discovering ./.agentdeck/).
agentdeck/core/: Pure domain model, event schema, content blocks, ports, error taxonomy. Imports stdlib + pydantic only (enforced byimport-linter).agentdeck/runtime/: Execution orchestration, lifecycle state machine, event dispatch. Importscore/only.agentdeck/adapters/: Pluggable integrations (executors:openai_agents,native; stores:sqlite,postgres,redis;mcp;telemetry). Each adapter importscore/plus exactly one external technology. Adapters never import other adapters.agentdeck/authoring/: Declarations (Agent,@tool,@workflow,Skill) compiled to specs.agentdeck/bindings/: The binding SPI:Binding,Gateway,exposure, and thenative/agui/terminalentry points. ReachesDeckthrough its public surface only.agentdeck/skills/:Skills, the capability object aDeckcomposes for skill disclosure.
- Correctness before convenience: Strict internal invariants, forgiving external APIs. Make invalid states impossible to express.
- Errors are part of the API: Every error must state what happened, why it happened, and the exact code/action to resolve it.
- Good (real,
runtime/registry.py): "No {self.label} named {name!r}. Available: {sorted(plugins)}." - a lookup failure names the alternatives. - Bad: "invalid workflow state".
- Good (real,
- Typing: Python ≥3.12. Strict annotations everywhere. Pydantic v2 models at system boundaries;
@dataclass(frozen=True, slots=True)for internal immutable value objects. No unprincipledAny. - Zero unnecessary abstractions: YAGNI. Delete dead code aggressively. Do not add configuration for things that never change.
- Good (real,
deck.py): cancellation isRun.cancel(), a method on the existing handle. - Bad: a new
CancellationManagerclass for that same responsibility. Runuv run scripts/repomap.pybefore adding any public abstraction.
- Good (real,
- Comments: Extremely rare, max 1–2 lines explaining non-obvious why, never restating what the code does.
- Good (real,
core/control.py):# Before the raise, because the raise is what records the effect: an intent left pending behind an honored one would be honored a second time on the next resume. - Bad:
# Increment the retry countaboveretry_count += 1. APostToolUsehook (scripts/slopcheck.py) flags this per edit.
- Good (real,
- Conciseness is mandatory: No fluff, no sprawling prose. If one sentence suffices, write one sentence. Code, comments, docs, PR descriptions, and issue specs must be terse, precise, and dense with signal.
- No em dashes: Never use the
-character in any documentation, code, comments, or agent output. Use a regular hyphen-, colon:, or separate into distinct sentences.
- Gate:
make check(runs ruff, ty typecheck, import-linter, and pytest). Must be 100% green. - Settings & Env: Layered pydantic-settings; all environment variables use the
AGENTDECK_*prefix exclusively (except third-party SDK vars likeOPENAI_*). - Goldens & Snapshots:
tests/golden/andtests/core/snapshots/update only via intentionalmake goldenwith PR justification. - CHANGELOG: Add concise, user-facing entries under
## [Unreleased]for any user-visible change.
- Communication conciseness: Keep text between tool calls to ≤25 words. Keep final responses to ≤100 words unless more detail is required.
- Branches:
devis default; PRs targetdev.mainis release-only. - No attribution trailers: Never include an AI
Co-Authored-By,🤖 Generated with, or AI vendor signatures in commits, PRs, or issues. A trailer naming a person is collaboration, not attribution: GitHub writes one itself when a contributor accepts a maintainer's suggestion. - Draft PR workflow: Open a draft PR on your first commit (
gh pr create --draft) and push continuously as you work. Mark ready withgh pr readyonly whenmake checkis clean.