This directory contains fixtures covering ACOP core, the HTTP+JSON binding, and both extensions. They are intended to be executable against any ACOP implementation as a baseline conformance suite.
A fixture declares its kind. When kind is absent it is http, which
keeps the original core fixtures unchanged.
kind |
Asserts | Covers |
|---|---|---|
http |
A request produces an expected response. | acop-http-binding.md |
document |
A document is accepted or rejected. | acop-orchestration.md, acop-compliance.md |
transition |
A state transition is permitted or forbidden. | every normative state machine |
document fixtures and enforced_byA document fixture names the schema it is judged against and declares
enforced_by:
schema — JSON Schema validation decides the outcome. A rejection here
is one any validator catches.spec — the rule is a cross-object or lifecycle constraint that JSON
Schema cannot express. The document validates against the schema and
is still non-conforming.That distinction is the point of these fixtures. Rules like “an exception
against a critical requirement MUST carry an expiry” span two objects —
severity lives on the Requirement, the constraint applies to the
ExceptionJustification referencing it — so no schema will catch them. An
implementation that validates documents and stops is not conformant, and
the spec fixtures are what demonstrate the gap.
The repository validator executes every document fixture and enforces the
labelling in both directions: a schema fixture expecting rejection whose
document actually validates is a broken test, and a spec fixture whose
document the schema already rejects is mislabelled.
transition fixturesA transition fixture names a state_field such as
acop-compliance.schema.json#exception_status, a from and to state, and
whether the transition is permitted. The validator checks both states
exist in that enum and that no two fixtures make opposite claims about the
same edge.
Some transitions depend on state beyond the two status values — whether an
approval was recorded, whether a covering exception is still live. Those
carry a precondition string. A runner that ignores precondition will
report false passes on those fixtures.
Each http fixture is a single JSON object with the shape:
{
"name": "claim-grant.success",
"verb": "claim_work_item",
"request": {
"method": "POST",
"path": "claims",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer <test-token>"
},
"body": { "...": "..." }
},
"expected_response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"body_match": {
"kind": "subset",
"value": { "...": "..." }
}
}
}
body_match semanticskind: "subset" — the actual response body MUST be a JSON object that
contains every key from value with equal values (recursively).
Implementations MAY return additional fields.kind: "exact" — the actual response body MUST equal value exactly.kind: "schema" — the actual response body MUST validate against the
JSON Schema in value.When kind is omitted, runners MUST treat it as subset.
Some fixtures reference values that are not known until run time. These
appear as ${variable_name} strings anywhere in the fixture — in a path,
a header, or a body — and MUST be substituted by the runner before the
request is sent.
A variable resolves from one of two places:
captures block maps
variable names to JSONPath expressions over its own response body. For
example claim-grant.success captures claim_id from
$.claim.claim_id, and later fixtures reference it as ${claim_id}.
The producing fixture MUST have executed successfully first; use
depends_on to declare that ordering.Two of the runner-supplied inputs deserve attention, because a runner that fakes them will report a false pass:
worker_other_token MUST authenticate a genuinely different principal
from worker_http_token. The conflict and unauthorized-renewal fixtures
exist to prove that authorization is enforced against identity; reusing
one token for both makes those fixtures vacuous.expired_claim_id MUST reference a claim whose lease has actually
expired. The suite cannot produce one without waiting out a TTL, so the
runner must seed it directly or advance the implementation’s clock.The validator in this repository checks that every ${variable} a fixture
uses is either captured by another fixture or declared in
runner-inputs.json, and that the manifest contains no unused entries.
| File | Verb | Scenario |
|---|---|---|
claim-grant.success.json |
claim_work_item | First grant succeeds. |
claim-grant.conflict.json |
claim_work_item | Second worker collides on an already-claimed work item. |
claim-renew.success.json |
renew | Owner extends the lease. |
claim-renew.unauthorized.json |
renew | Non-owner attempts to renew. |
claim-renew.expired.json |
renew | Expired claim cannot be renewed. |
claim-release.success.json |
release | Owner releases the claim. |
claim-release.idempotent.json |
release | Release on an already-released claim returns 200. |
claim-complete.success.json |
complete | Completion with required evidence. |
claim-complete.no-evidence.json |
complete | Completion missing required evidence is rejected. |
work-item-create.success.json |
create_work_item | New work item inserted. |
work-item-create.idempotent.json |
create_work_item | Re-submitting the same work item returns 200. |
work-item-create.schema-violation.json |
create_work_item | Rejected with schema_violation. |
accept-handoff.success.json |
accept_handoff | Downstream consumer accepts. |
accept-handoff.not-found.json |
accept_handoff | Unknown work item returns 404. |
blackboard-post.success.json |
post_blackboard | New entry appended. |
blackboard-post.with-supersede.json |
post_blackboard | Entry supersedes a prior one. |
protocol-version.rejected.json |
any | Unsupported protocol_version is rejected. |
auth.unauthenticated.json |
any | Missing credential is rejected with 401. |
| File | Kind | Scenario |
|---|---|---|
orchestration-flow.success.json |
document | Well-formed flow: two lanes feeding an integration gate. |
orchestration-flow.profile-version-rejected.json |
document | Superseded acop-orchestration/0.1 profile version is rejected. |
orchestration-flow.dangling-edge.json |
document | Flow edge naming an undeclared gate. Validates against the schema; MUST still be rejected. |
orchestration-flow.vacuous-release-condition.json |
document | Release condition with no upstream targets — a gate that gates nothing. |
orchestration-acceptance.pending-to-accepted.json |
transition | Accepting work that never completed. |
orchestration-acceptance.released-reopen.json |
transition | released is terminal. |
orchestration-acceptance.rejected-to-accepted.json |
transition | Reversing a rejection without resubmission. |
orchestration-acceptance.rejected-rework.json |
transition | The permitted rework path. |
| File | Kind | Scenario |
|---|---|---|
compliance-matrix.success.json |
document | Requirement satisfied by evidence, second requirement excepted under a live exception. |
compliance-exception.missing-expiry.json |
document | Permanent waiver on a critical requirement. |
compliance-exception.missing-identity.json |
document | Severe waiver approved by a symbolic role with no identity binding. |
compliance-matrix.excepted-without-approval.json |
document | Entry held excepted behind an expired exception. |
compliance-exception.expired-renewal.json |
transition | Reactivating an expired exception in place. |
compliance-completeness.satisfied-to-excepted.json |
transition | Waiving an already-satisfied requirement. |
compliance-completeness.excepted-to-blocked.json |
transition | The required transition when a covering exception dies. |
compliance-gate.failed-to-waived.json |
transition | Waiving a failed gate with no recorded approval. |
runner-inputs.json is a manifest, not a fixture, and is not executed.
A reference runner is out of scope for v1.0 (it lives outside the spec repo). Implementations are encouraged to ship their own runner that walks the inventory above in dependency order:
work-item-create.success (seeds a work item)claim-grant.success (binds a claim)claim-renew.successclaim-complete.successaccept-handoff.successIndependent scenarios (conflict, unauthorized, expired, schema violation, etc.) MAY be run in any order against a freshly-seeded state.