ACOP

ACOP Conformance Fixtures (v1.0)

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.

Fixture kinds

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_by

A document fixture names the schema it is judged against and declares enforced_by:

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 fixtures

A 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.

File naming

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 semantics

When kind is omitted, runners MUST treat it as subset.

Variables

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:

  1. Captured from an earlier fixture. A fixture’s 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.
  2. Supplied by the runner. Credentials and pre-seeded state cannot be produced by the suite. Every such variable is declared in runner-inputs.json with a description of what it must satisfy. A runner MUST provide all of them before executing the suite.

Two of the runner-supplied inputs deserve attention, because a runner that fakes them will report a false pass:

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.

Fixture inventory

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.

Orchestration extension

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.

Compliance extension

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.

Running the suite

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:

  1. work-item-create.success (seeds a work item)
  2. claim-grant.success (binds a claim)
  3. claim-renew.success
  4. claim-complete.success
  5. accept-handoff.success

Independent scenarios (conflict, unauthorized, expired, schema violation, etc.) MAY be run in any order against a freshly-seeded state.