Skip to main content

asop

The ASOP contract — a versioned, verified, self-revising procedure standard for agentic work.

This repository is the normative specification (ASOP.md) and its reference implementation. It is deliberately standalone: it depends on nothing, and nothing in it imports a coordination plane or a runtime.

Two implementations speak it today — the AgentCo Hub (coordination) and the AgentCo Harness (execution). They are independent of each other, and both are mine: the standard has not yet been implemented by anyone who did not also write it, which is the honest status and the thing I would most like to change.

What exists to make that possible: a normative schema as JSON Schema in YAML, and 39 conformance vectors as data, so an implementation in any language can prove it conforms without depending on this package or reading a line of Python.

The ASOP contract, as code. A procedure that is versioned, verified, and self-revising is only useful if two independently-owned pieces of software — a coordination plane and the harness actually running the work — agree on what a gate, an attestation, and a procedure record are, without either importing the other. This package is that agreement.

Who imports this

  • The AgentCo Hub (agentco/gates.py, agentco/errors.py, agentco/sop.py and agentco/policy.py are thin shims over this package, keeping every existing caller and test unchanged).
  • The AgentCo Harness, the standalone execution runtime, from its P2 (adopting this schema in place of its own VERIFY_KEYS/validate_verify).
  • Anyone else building a plane or a harness that wants to speak the same gate shape as either, without depending on the code that runs either one.

What's here

Module Contract
asop.errors Refusal — the one exception type: a stable machine code, a human message, and a remediation sentence. Every refusal in this package (and, by re-export, in the plane) is one of these.
asop.gates The unified gate schema. validate_gate(payload, *, require=(), max_park_seconds_ceiling=None) normalises a deterministic / judged / human gate — merging the plane's park-clock fields with the Harness's staged-check and runtime-hint fields into one shape — or refuses. validate_attestation, attestation_passes, retry_decision cover the evidence side.
asop.sop The SOP record contract: SopStatus, the SOP and ASOP dataclasses, validate_fields / validate_asop / validate_step. Not the store — keeping versions, locking a file, and filing runs from a template is plane- or harness-side machinery, layered on top of this record shape.
asop.revision The revision policy of ASOP.md §6.4: the four rules an agent reviser is bound by — protected tags, the human ratchet (absolute on a first activation), no-undoing-a-human, and the human_only verbs — plus the four operator declarations they read (ASOP_HUMANS, ASOP_PROTECTED_TAGS, ASOP_VERIFIERS, ASOP_ADJUDICATORS), each still falling back to its pre-split AGENTCO_* name for the two implementations that predate the split; whether that fallback is a permanent courtesy or gets a deprecation date is still open. Shared rather than plane-side because a rule that held on the plane and not in the runtime executing its work would be a rule with a door beside it. Which verb calls which rule stays each side's own wiring.
asop.refusals The refusal-code vocabulary — every machine-readable code a Refusal carries across the reference plane, named once with a one-line meaning, for a reader scanning codes rather than call sites.

The gate schema

One canonical field decides the shape: kind (deterministic \| judged \| human). class is accepted as a read alias — the Harness's historical name for the same field — and normalised output always uses kind. Sending both, disagreeing, is refused.

Three field groups:

  • core (always required) — kind, and exactly one of check (one command) or checks (an ordered, staged list; a one-element checks normalises into check).
  • clock (the plane's park clock) — max_park_seconds, on_timeout, escalate_to (only with on_timeout: escalate), verifier (required for human, refused for deterministic, optional for judged). Declared as a whole or not at all — a gate carrying some of these fields but not both max_park_seconds and on_timeout is always refused, regardless of whether a caller requires the clock at all.
  • execution (the Harness's runtime hints) — cwd, timeout_s, rubric (judged only), judge_route (judged only).

Whether the clock group is mandatory is the one thing that differs by caller, and the one knob validate_gate exposes: require=("clock",) is the plane's convention today; require=() is a caller with no park-clock concept, such as the Harness before it adopts one. The 30-day park-clock ceiling is not a package constant — it is the plane's own decision about its own registry, passed in as max_park_seconds_ceiling. A caller with no ceiling passes None.

Output is always fully normalised: every known field present (None where absent), plus "schema_version": 1.

Versioning

asop.SCHEMA_VERSION covers the gate schema's normalised shape. It bumps only on a breaking change — a new required field, a renamed one, a changed type. An additive optional field or a new refusal code does not bump it, because existing readers already tolerate both. See asop/__init__.py.

Install

pip install asop-spec      # or: uv add asop-spec

The distribution is asop-spec; the import is asop:

from asop import validate_gate, Refusal

(The bare asop name on PyPI was taken by an unrelated placeholder before this package existed.)

Standard library only, and that is a rule rather than a coincidence. This package is imported by every side of the contract, so a dependency taken here is forced on everyone who speaks it. A standard that drags a dependency tree behind it is a framework wearing a standard's clothes.

Testing

uv run --extra dev pytest -q                                         # 150 tests
uv run --with pyyaml python conformance/runner/python.py --verbose   # 61 vectors

pytest is a dev dependency, not a default one — a bare uv run pytest fails with "Failed to spawn: pytest" because nothing is installed to spawn; --extra dev is what pulls it in. The conformance runner takes PyYAML the same way, inline rather than as a dependency, for the reason given above: asop itself has none. See conformance/README.md for what a vector is, how counts are recomputed rather than typed by hand, and what the suite does not yet prove.

Licence

Apache License 2.0. The patent grant is the point: this package is the contract other people's harnesses are asked to depend on, and a contract adopted across organisations needs the grant stated in the file rather than implied.

Copyright 2026 Marcelo Bidoli Fernandes and the ASOP Authors.

Metadata

Release files for asop-spec 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for asop-spec 0.5.0
File Size Uploaded
asop_spec-0.5.0.tar.gz 155.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for asop-spec 0.5.0
File Interpreter ABI Platform
asop_spec-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 267.7 kB

Release files / asop_spec-0.5.0.tar.gz

Download URL asop_spec-0.5.0.tar.gz
Size 155.0 kB
Tags Source
SHA-256 checksum
How to use checksums
5b883f919d705b057fa9cfcb1dcf04ab4d409537635b73939db3e7b89f17d446
BLAKE2b-256 checksum
How to use checksums
911cbfdbe2e15fcb0d4d28ae13b14fb2167404e2c28fc1baee0500f85a189aee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / asop_spec-0.5.0-py3-none-any.whl

Download URL asop_spec-0.5.0-py3-none-any.whl
Size 112.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0de6300042928a31b0e7206d58408ba0c399fbfde4368dbf27e1435b3474f665
BLAKE2b-256 checksum
How to use checksums
55ff39ff6ae05ee80da9e44a294303dc38ed2081932bf79ccce3aa910e70bc2f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page