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.pyandagentco/policy.pyare 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 operator declarations they read (AGENTCO_HUMANS, AGENTCO_PROTECTED_TAGS). 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 ofcheck(one command) orchecks(an ordered, staged list; a one-elementchecksnormalises intocheck). - clock (the plane's park clock) —
max_park_seconds,on_timeout,escalate_to(only withon_timeout: escalate),verifier(required forhuman, refused fordeterministic, optional forjudged). Declared as a whole or not at all — a gate carrying some of these fields but not bothmax_park_secondsandon_timeoutis always refused, regardless of whether a caller requires the clock at all. - execution (the Harness's runtime hints) —
cwd,timeout_s,rubric(judgedonly),judge_route(judgedonly).
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.
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.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| asop_spec-0.2.0.tar.gz | 89.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| asop_spec-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 166.7 kB
Release files / asop_spec-0.2.0.tar.gz
| Download URL | asop_spec-0.2.0.tar.gz |
|---|---|
| Size | 89.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
572464f4a3f98f8f3591cd0d2de3e42231ab7d830ab495edcc347cd5f89eaac4
|
|
BLAKE2b-256 checksum How to use checksums |
80dfb66c83e82a5252cdd2913026a7168e6dcd156d650d6d49b742c86b65721e
|
| 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.2.0-py3-none-any.whl
| Download URL | asop_spec-0.2.0-py3-none-any.whl |
|---|---|
| Size | 77.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f1cdf50f2c6a357629996f7ef78d631a07478294bb969d3c82af8ba756e77c76
|
|
BLAKE2b-256 checksum How to use checksums |
cdcc6336cae7c18dd09b54e5533e9cdaef06a532b68760f6e42fd913c66b6b9f
|
| 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}
|