From 262d26b149c7b5a44358a28288c554f61bbf6587 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:18:05 -0400 Subject: [PATCH 01/18] docs: specify cross-runtime execution contract v1 --- ...cross-runtime-execution-contract-design.md | 283 ++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-14-cross-runtime-execution-contract-design.md diff --git a/docs/superpowers/specs/2026-09-14-cross-runtime-execution-contract-design.md b/docs/superpowers/specs/2026-09-14-cross-runtime-execution-contract-design.md new file mode 100644 index 0000000..4e4ece4 --- /dev/null +++ b/docs/superpowers/specs/2026-09-14-cross-runtime-execution-contract-design.md @@ -0,0 +1,283 @@ +# Cross-Runtime Execution Contract v1 — Design + +**Status:** Design candidate for implementation in `agent-control-plane`. This document does not establish contract acceptance, portability, production readiness, DGAF authorization, PDMAL empirical validity, or efficacy. + +## Purpose + +Add a versioned, framework-neutral execution/trace contract alongside the existing Agent Control Plane kernel so heterogeneous runtimes can emit and consume the same execution identity, trace, event, artifact, policy, and evidence metadata without making any runtime framework authoritative over the core ontology. + +The change is additive. Existing `Task`, `ControlPlane`, cooperative budget behavior, and `agent-control-plane.provenance.v1` remain compatible during this slice. + +## Architecture boundary + +- **DGAF** owns governance, authorization, evidence-state transitions, and fail-closed policy authority. +- **agent-control-plane (ACP)** is the preferred implementation host for the framework-neutral execution contract and future runtime adapters. +- **PDMAL** is a governed empirical workload/research consumer. It does not define the ACP contract and is not a prerequisite parent of ACP. +- **Metasymphony** may name a composed execution profile using the contract. It is not a separate framework or control plane by default. +- **Evaluators** such as Axiom Lens or provenance-regression tooling attach through explicit bounded interfaces. They do not mutate the core schema merely because they exist. + +## Design principles + +1. **Framework neutrality:** LangGraph, Microsoft Agent Framework, MCP, A2A, OpenAI runtimes, Hermes, PDMAL-specific runtimes, and future systems are adapters or consumers, not schema authorities. +2. **Additive compatibility:** do not rewrite the existing kernel to introduce the contract. Existing behavior remains valid while the new contract is introduced and mapped. +3. **Typed identity before adapter work:** establish execution, trace, span, actor/component, runtime/adapter, artifact, and schema identity before building external adapters. +4. **Deterministic serialization:** contract objects must serialize deterministically for fixtures, hashing, comparison, and evidence binding. +5. **Fail closed on malformed identity:** blank or structurally invalid required identifiers and unsupported schema versions are rejected rather than normalized silently. +6. **Evidence separation:** contract conformance is engineering evidence. It is not runtime portability, governance authorization, empirical efficacy, or scientific validation. +7. **No hidden ontology fork:** runtime adapters may populate contract fields but may not silently redefine required semantics. + +## Scope of this implementation slice + +### In scope + +- a versioned contract module/package; +- execution identity; +- trace/span context; +- actor/component and runtime/adapter identity; +- artifact references with optional version/hash metadata; +- versioned execution-event objects; +- UTC timestamp plus monotonic ordering/timing field semantics; +- execution status/event classification; +- optional policy-decision reference; +- deterministic `to_dict` / JSON-ready serialization; +- strict construction/validation for required fields; +- mapping from current ACP provenance events into the new contract without removing `agent-control-plane.provenance.v1`; +- deterministic contract/conformance fixtures and tests; +- documentation of evidence boundaries and unsupported claims. + +### Explicitly out of scope + +- LangGraph, Microsoft Agent Framework, MCP, A2A, OpenAI, Hermes, or PDMAL adapter implementation; +- two-runtime portability claims; +- durable persistence or distributed trace storage; +- cryptographic/tamper-evident provenance; +- provider-accurate metering; +- hard deadline/preemption; +- checkpoint/resume; +- delegated-budget conservation; +- authentication or authorization infrastructure; +- empirical DGAF/PDMAL runs; +- semantic/embedding evaluators; +- changes to DGAF governance state. + +## Contract structure + +The first implementation should use a focused module or package under `src/agent_control_plane/contract/`. Exact internal file decomposition may be adjusted to fit repository conventions, but public concepts and semantics below are normative for this slice. + +### `ExecutionIdentity` + +Required fields: + +- `execution_id: str` — stable identifier for one logical execution; +- `run_id: str` — ACP/run-level identity; +- `schema_version: str` — exact contract version, initially `agent-control-plane.execution.v1`. + +Validation: + +- all required identifiers must be non-empty after trimming; +- unsupported schema versions fail closed; +- identifiers are preserved exactly after validation; they are not silently regenerated. + +`execution_id` is intentionally distinct from existing `task_id` and `run_id`: a task is a kernel object, a run scopes ACP provenance, and an execution is the cross-runtime unit represented by this contract. + +### `TraceContext` + +Required fields: + +- `trace_id: str`; +- `span_id: str`. + +Optional field: + +- `parent_span_id: str | None`. + +Validation: + +- `trace_id` and `span_id` must be non-empty; +- when present, `parent_span_id` must be non-empty; +- `span_id` must not equal `parent_span_id`; +- this slice does not prescribe UUID, W3C Trace Context, or OpenTelemetry formatting. Format standardization may be added later through an explicit compatibility decision. + +### `ComponentIdentity` + +Required fields: + +- `component_id: str` — stable actor/tool/agent/service/component identifier; +- `component_type: str` — bounded descriptive type such as `kernel`, `agent`, `tool`, `runtime`, or `evaluator`; +- `runtime_id: str` — runtime/execution environment identity; +- `adapter_id: str` — adapter identity, including `native-acp` for the current kernel mapping. + +Optional fields: + +- `version: str | None`; +- `source_ref: str | None` — commit/build/package/deployment identity where material. + +No named persona is required by the contract. Persona presentation or historical actor lineage belongs outside this primitive identity layer unless represented as ordinary metadata by a higher-level profile. + +### `ArtifactRef` + +Required fields: + +- `artifact_id: str`; +- `kind: str`. + +Optional fields: + +- `uri: str | None`; +- `version: str | None`; +- `sha256: str | None`. + +Validation: + +- required fields must be non-empty; +- if `sha256` is present it must be 64 lowercase hexadecimal characters; +- the contract does not claim that a hash proves custody, attestation, or independence. + +### `ExecutionEvent` + +Required fields: + +- `event_type: str`; +- `identity: ExecutionIdentity`; +- `trace: TraceContext`; +- `component: ComponentIdentity`; +- `task_id: str`; +- `status: str`; +- `utc_timestamp: str`; +- `monotonic_ns: int`. + +Optional fields: + +- `capability: str | None`; +- `policy_decision_ref: str | None`; +- `input_artifacts: tuple[ArtifactRef, ...]`; +- `output_artifacts: tuple[ArtifactRef, ...]`; +- `detail: str | None`. + +Validation: + +- required strings must be non-empty; +- `monotonic_ns` must be an integer >= 0; +- `utc_timestamp` must be timezone-aware ISO-8601 UTC and normalized to a `Z` or `+00:00` representation chosen consistently by implementation; +- artifact arrays preserve caller order; +- serialization emits stable field names and deterministic nested ordering. + +## Event mapping from current ACP provenance + +The current `ProvenanceEvent` remains supported. The additive mapper converts an existing provenance event to `ExecutionEvent` when the caller supplies the cross-runtime context not present in v1 provenance: + +- `ExecutionIdentity`; +- `TraceContext`; +- `ComponentIdentity`; +- optional artifact references and policy-decision reference. + +Mapping rules: + +- existing `ProvenanceEvent.event` -> `ExecutionEvent.event_type`; +- existing `task_id` -> `task_id`; +- existing `run_id` must equal `ExecutionIdentity.run_id`; mismatch fails closed; +- existing `capability` -> `capability`; +- existing `state` -> `status`; when state is absent the mapper uses an explicit non-success placeholder such as `unspecified`, never inferred success; +- existing `detail` -> `detail`; +- existing UTC timestamp -> `utc_timestamp` after strict validation; +- `monotonic_ns` must be supplied by the mapping call or event-emission boundary; it is not reconstructed from wall-clock time. + +The mapper must not fabricate trace IDs, component IDs, source refs, artifacts, policy decisions, or successful status from missing historical data. + +## Policy lifecycle boundary + +This slice records a `policy_decision_ref` but does not implement new in-execution or post-execution policy engines. + +The current ACP policy hook remains a pre-execution allow/deny mechanism. Future policy phases must be separately designed and tested. A recorded policy reference means only that an identified policy decision is associated with an event; it does not prove authorization unless DGAF or another governing authority establishes that relationship. + +## Serialization and conformance + +Contract objects expose deterministic JSON-ready dictionaries. The implementation must not include process addresses, unordered set output, generated timestamps at serialization time, or other nondeterministic fields. + +A conformance fixture should serialize a fully populated event to an exact expected dictionary. A second round-trip test should reconstruct or validate the same contract data without semantic drift. + +This first suite establishes **schema/serialization conformance for ACP-native events only**. It is not a cross-runtime portability suite until a materially different adapter is implemented and executes the same fixtures. + +## Error handling + +Construction or conversion must raise explicit validation errors for: + +- blank required identity fields; +- unsupported schema version; +- blank parent span when provided; +- span self-parenting; +- malformed artifact SHA-256; +- negative monotonic value; +- non-UTC or naive event timestamps; +- `run_id` mismatch during provenance mapping. + +No invalid required value may be replaced automatically with a generated identifier. + +## Compatibility + +The existing public imports and current kernel behavior remain unchanged in this slice. + +`agent-control-plane.provenance.v1` remains exportable. The new contract is an additional representation and does not silently change the manifest schema. + +A later explicit migration may version the provenance manifest to include execution/trace data after conformance evidence exists. That migration is outside this design. + +## Test strategy + +Use TDD for each behavior. The minimum tests are: + +1. valid execution identity construction and deterministic serialization; +2. blank/unsupported identity rejection; +3. valid trace construction; +4. blank trace ID/span ID and self-parent rejection; +5. component identity serialization without persona dependence; +6. artifact hash validation; +7. deterministic full event serialization; +8. rejection of negative monotonic values; +9. rejection of naive/non-UTC timestamps; +10. provenance-to-contract mapping preserves event/task/run/capability/state/detail; +11. provenance mapping rejects run mismatch; +12. provenance mapping does not infer success when source state is absent; +13. existing ACP test suite remains green without public-behavior changes. + +Tests must assert real behavior, not implementation-specific mocks. + +## Acceptance criteria for this slice + +This design slice may be described as implemented only when: + +- the contract types and validation rules exist in source; +- required TDD red/green evidence has been observed during implementation; +- deterministic serialization tests pass; +- current provenance can be mapped without modifying the existing manifest schema; +- the full existing test suite passes; +- CI passes on the exact implementation head; +- documentation states that portability and empirical efficacy remain not established. + +Even after those criteria pass, the ADR remains a candidate until its broader acceptance gate is met, including materially different adapters using the same core schema without fork. + +## Follow-on sequence + +After this slice is accepted: + +1. define a minimal adapter protocol that consumes/emits the stable contract; +2. implement one ACP-native reference adapter if a distinct adapter surface is still needed; +3. implement one materially different external/runtime adapter; +4. run the same conformance fixtures across both; +5. only then evaluate a portability claim; +6. separately design in-execution/post-execution policy phases, evaluator attachment, persistence, and advanced bounded-execution capabilities as evidence justifies them. + +## Evidence ceiling + +The strongest claim this slice can support is: + +> ACP implements and tests a versioned framework-neutral execution/trace contract for its native kernel representation, with deterministic serialization and fail-closed validation under the tested conditions. + +It cannot establish: + +- general runtime portability; +- DGAF authorization; +- PDMAL efficacy; +- secure or tamper-evident provenance; +- distributed reliability; +- production readiness; +- superiority to existing orchestration standards or frameworks. From 326ee7fa514e5f23c8b3a356d2d785589322c414 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:18:42 -0400 Subject: [PATCH 02/18] docs: remove contract design ambiguities --- ...cross-runtime-execution-contract-design.md | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/superpowers/specs/2026-09-14-cross-runtime-execution-contract-design.md b/docs/superpowers/specs/2026-09-14-cross-runtime-execution-contract-design.md index 4e4ece4..4138c85 100644 --- a/docs/superpowers/specs/2026-09-14-cross-runtime-execution-contract-design.md +++ b/docs/superpowers/specs/2026-09-14-cross-runtime-execution-contract-design.md @@ -62,7 +62,9 @@ The change is additive. Existing `Task`, `ControlPlane`, cooperative budget beha ## Contract structure -The first implementation should use a focused module or package under `src/agent_control_plane/contract/`. Exact internal file decomposition may be adjusted to fit repository conventions, but public concepts and semantics below are normative for this slice. +The implementation should use a focused package under `src/agent_control_plane/contract/`, with public exports collected in `src/agent_control_plane/contract/__init__.py`. Internal decomposition should keep validation/model and provenance-mapping responsibilities separate. + +The package exports `ContractValidationError`, a subclass of `ValueError`, for contract construction and mapping failures. Callers may catch `ValueError` broadly without losing compatibility with ordinary Python validation conventions. ### `ExecutionIdentity` @@ -75,7 +77,7 @@ Required fields: Validation: - all required identifiers must be non-empty after trimming; -- unsupported schema versions fail closed; +- unsupported schema versions fail closed with `ContractValidationError`; - identifiers are preserved exactly after validation; they are not silently regenerated. `execution_id` is intentionally distinct from existing `task_id` and `run_id`: a task is a kernel object, a run scopes ACP provenance, and an execution is the cross-runtime unit represented by this contract. @@ -158,7 +160,8 @@ Validation: - required strings must be non-empty; - `monotonic_ns` must be an integer >= 0; -- `utc_timestamp` must be timezone-aware ISO-8601 UTC and normalized to a `Z` or `+00:00` representation chosen consistently by implementation; +- `utc_timestamp` must parse as timezone-aware ISO-8601 UTC; +- serialization normalizes UTC timestamps to the canonical `YYYY-MM-DDTHH:MM:SS[.ffffff]Z` form, using `Z` rather than `+00:00`; - artifact arrays preserve caller order; - serialization emits stable field names and deterministic nested ordering. @@ -177,9 +180,9 @@ Mapping rules: - existing `task_id` -> `task_id`; - existing `run_id` must equal `ExecutionIdentity.run_id`; mismatch fails closed; - existing `capability` -> `capability`; -- existing `state` -> `status`; when state is absent the mapper uses an explicit non-success placeholder such as `unspecified`, never inferred success; +- existing `state` -> `status`; when state is absent the mapper uses the exact literal `unspecified`, never inferred success; - existing `detail` -> `detail`; -- existing UTC timestamp -> `utc_timestamp` after strict validation; +- existing UTC timestamp -> validated and canonically serialized `utc_timestamp`; - `monotonic_ns` must be supplied by the mapping call or event-emission boundary; it is not reconstructed from wall-clock time. The mapper must not fabricate trace IDs, component IDs, source refs, artifacts, policy decisions, or successful status from missing historical data. @@ -194,13 +197,13 @@ The current ACP policy hook remains a pre-execution allow/deny mechanism. Future Contract objects expose deterministic JSON-ready dictionaries. The implementation must not include process addresses, unordered set output, generated timestamps at serialization time, or other nondeterministic fields. -A conformance fixture should serialize a fully populated event to an exact expected dictionary. A second round-trip test should reconstruct or validate the same contract data without semantic drift. +A conformance fixture should serialize a fully populated event to an exact expected dictionary. A second round-trip validation test should construct an equivalent event from the serialized dictionary and confirm semantic equality without field loss or reinterpretation. This first suite establishes **schema/serialization conformance for ACP-native events only**. It is not a cross-runtime portability suite until a materially different adapter is implemented and executes the same fixtures. ## Error handling -Construction or conversion must raise explicit validation errors for: +Construction or conversion must raise `ContractValidationError` for: - blank required identity fields; - unsupported schema version; @@ -231,13 +234,14 @@ Use TDD for each behavior. The minimum tests are: 4. blank trace ID/span ID and self-parent rejection; 5. component identity serialization without persona dependence; 6. artifact hash validation; -7. deterministic full event serialization; +7. deterministic full event serialization with canonical `Z` timestamp; 8. rejection of negative monotonic values; 9. rejection of naive/non-UTC timestamps; 10. provenance-to-contract mapping preserves event/task/run/capability/state/detail; 11. provenance mapping rejects run mismatch; -12. provenance mapping does not infer success when source state is absent; -13. existing ACP test suite remains green without public-behavior changes. +12. provenance mapping maps absent source state exactly to `unspecified`; +13. serialized-dictionary round-trip preserves semantic equality; +14. existing ACP test suite remains green without public-behavior changes. Tests must assert real behavior, not implementation-specific mocks. From 5437cbe1e84b65ac71214d7e6407c28b6e895729 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:21:43 -0400 Subject: [PATCH 03/18] docs: add cross-runtime contract implementation plan --- ...4-cross-runtime-execution-contract-plan.md | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-14-cross-runtime-execution-contract-plan.md diff --git a/docs/superpowers/plans/2026-09-14-cross-runtime-execution-contract-plan.md b/docs/superpowers/plans/2026-09-14-cross-runtime-execution-contract-plan.md new file mode 100644 index 0000000..d04f2f4 --- /dev/null +++ b/docs/superpowers/plans/2026-09-14-cross-runtime-execution-contract-plan.md @@ -0,0 +1,97 @@ +# Cross-Runtime Execution Contract v1 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a versioned framework-neutral execution/trace contract to ACP without changing existing kernel behavior or the `agent-control-plane.provenance.v1` manifest. + +**Architecture:** Introduce focused immutable contract dataclasses plus strict validators in a new `agent_control_plane.contract` package. Keep the existing kernel/provenance model intact, then add an explicit mapper from legacy `ProvenanceEvent` into the new contract when the caller supplies trace/component context that legacy events do not contain. + +**Tech Stack:** Python >=3.10, stdlib dataclasses/datetime/re, pytest, existing GitHub Actions matrix Python 3.10–3.14. + +**Spec:** `docs/superpowers/specs/2026-09-14-cross-runtime-execution-contract-design.md` + +## Global Constraints + +- Contract schema version is exactly `agent-control-plane.execution.v1`. +- Canonical serialized UTC timestamps end in `Z`. +- Legacy provenance with absent state maps exactly to `unspecified`. +- Invalid required identity, unsupported schema version, malformed SHA-256, non-UTC timestamp, negative monotonic value, span self-parenting, and provenance/identity run mismatch fail closed with `ContractValidationError`. +- No new external dependencies. +- Existing `Task`, `ControlPlane`, budgets, public imports, and `agent-control-plane.provenance.v1` behavior remain compatible. +- This slice establishes ACP-native schema/serialization conformance only; portability remains NOT ESTABLISHED. + +--- + +### Task 1: Core identities and validation + +**Files:** +- Create: `tests/test_contract.py` +- Create: `src/agent_control_plane/contract/__init__.py` +- Create: `src/agent_control_plane/contract/model.py` + +**Interfaces:** +- Produces: `ContractValidationError`, `ExecutionIdentity`, `TraceContext`, `ComponentIdentity`, `ArtifactRef`, `SCHEMA_VERSION`. + +- [ ] **Step 1: Write failing tests** for valid deterministic serialization and each required rejection path: blank identity, unsupported schema version, blank trace/span, blank optional parent, self-parent, blank component fields, and malformed artifact SHA-256. +- [ ] **Step 2: Push tests only and verify RED in GitHub Actions.** Expected failures are import/definition failures for `agent_control_plane.contract` or missing required contract classes, not syntax/configuration failures. +- [ ] **Step 3: Implement minimal immutable dataclasses and validators** in `model.py`; export them through `contract/__init__.py`. +- [ ] **Step 4: Push implementation and verify GREEN** for the full Python 3.10–3.14 matrix. + +### Task 2: Execution event and canonical time serialization + +**Files:** +- Modify: `tests/test_contract.py` +- Modify: `src/agent_control_plane/contract/model.py` +- Modify: `src/agent_control_plane/contract/__init__.py` + +**Interfaces:** +- Produces: `ExecutionEvent` with `to_dict()` and strict UTC/monotonic validation. +- Consumes: identities and artifact refs from Task 1. + +- [ ] **Step 1: Write failing tests** for exact full-event dictionary serialization, canonical `Z` timestamp output, artifact order preservation, negative monotonic rejection, naive timestamp rejection, and non-UTC offset rejection. +- [ ] **Step 2: Push tests only and verify RED** for missing `ExecutionEvent`/event validation behavior. +- [ ] **Step 3: Implement the minimum event type and timestamp canonicalizer** needed to satisfy the tests; no generated timestamps during serialization. +- [ ] **Step 4: Push and verify GREEN** across the matrix. + +### Task 3: Legacy provenance mapper + +**Files:** +- Create: `src/agent_control_plane/contract/mapping.py` +- Modify: `src/agent_control_plane/contract/__init__.py` +- Modify: `tests/test_contract.py` + +**Interfaces:** +- Produces: `map_provenance_event(event, *, identity, trace, component, monotonic_ns, input_artifacts=(), output_artifacts=(), policy_decision_ref=None) -> ExecutionEvent`. +- Consumes: existing `agent_control_plane.provenance.ProvenanceEvent` plus Task 1/2 contract types. + +- [ ] **Step 1: Write failing tests** proving legacy event/task/run/capability/state/detail preservation, exact `unspecified` mapping for absent state, canonical UTC conversion, and fail-closed run mismatch. +- [ ] **Step 2: Push tests only and verify RED** for missing mapper behavior. +- [ ] **Step 3: Implement the mapper without fabricating trace/component/artifact/policy data.** Require supplied `monotonic_ns`; reject run mismatch with `ContractValidationError`. +- [ ] **Step 4: Push and verify GREEN** across the matrix. + +### Task 4: Contract round-trip and evidence-boundary documentation + +**Files:** +- Modify: `tests/test_contract.py` +- Modify: `src/agent_control_plane/contract/model.py` +- Modify: `docs/CONTROL_PLANE_KERNEL_SPEC.md` +- Modify: `README.md` + +**Interfaces:** +- Produces: `ExecutionEvent.from_dict()` (or equivalent explicit validator) that reconstructs the exact semantic contract from serialized data. + +- [ ] **Step 1: Write failing round-trip test** from a fully populated serialized event back to an equal contract object; reject unsupported schema/version data during reconstruction. +- [ ] **Step 2: Push tests only and verify RED** for missing round-trip reconstruction. +- [ ] **Step 3: Implement minimal reconstruction/validation** reusing the same constructors rather than duplicating validation. +- [ ] **Step 4: Update README/kernel spec** to distinguish legacy provenance from the new execution contract and explicitly state that runtime portability, DGAF authorization, PDMAL efficacy, durability, attestation, and production readiness remain unestablished. +- [ ] **Step 5: Push and verify GREEN** across the complete matrix. + +### Task 5: Final branch verification and review gate + +**Files:** no new production scope. + +- [ ] **Step 1: Compare branch against base `dbab7c1afafec524ce7c18157de2089cafe79c87`** and verify only planned contract/tests/docs changed. +- [ ] **Step 2: Verify exact-head GitHub Actions** across Python 3.10–3.14 with no failures. +- [ ] **Step 3: Re-read the design acceptance criteria** and classify each as VERIFIED, NOT VERIFIED, or NOT APPLICABLE from branch evidence. +- [ ] **Step 4: Open a PR only after exact-head verification is green.** PR text must preserve the evidence ceiling and state that portability remains NOT ESTABLISHED. +- [ ] **Step 5: Do not merge until PR-head checks are complete and no evidence-boundary regression is found.** From 761e8dd745b904cbfa33c5c74e32b3462a5687b4 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:21:56 -0400 Subject: [PATCH 04/18] test: define cross-runtime contract identity behavior --- tests/test_contract.py | 129 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 tests/test_contract.py diff --git a/tests/test_contract.py b/tests/test_contract.py new file mode 100644 index 0000000..cbf22dc --- /dev/null +++ b/tests/test_contract.py @@ -0,0 +1,129 @@ +import pytest + +from agent_control_plane.contract import ( + SCHEMA_VERSION, + ArtifactRef, + ComponentIdentity, + ContractValidationError, + ExecutionIdentity, + TraceContext, +) + + +def test_execution_identity_serializes_deterministically(): + identity = ExecutionIdentity(execution_id="exec-1", run_id="run-1") + + assert identity.to_dict() == { + "execution_id": "exec-1", + "run_id": "run-1", + "schema_version": SCHEMA_VERSION, + } + + +@pytest.mark.parametrize("field", ["execution_id", "run_id"]) +def test_execution_identity_rejects_blank_required_fields(field): + values = {"execution_id": "exec-1", "run_id": "run-1"} + values[field] = " " + + with pytest.raises(ContractValidationError): + ExecutionIdentity(**values) + + +def test_execution_identity_rejects_unsupported_schema_version(): + with pytest.raises(ContractValidationError): + ExecutionIdentity( + execution_id="exec-1", + run_id="run-1", + schema_version="agent-control-plane.execution.v999", + ) + + +def test_trace_context_serializes_with_optional_parent(): + trace = TraceContext(trace_id="trace-1", span_id="span-1", parent_span_id="span-0") + + assert trace.to_dict() == { + "trace_id": "trace-1", + "span_id": "span-1", + "parent_span_id": "span-0", + } + + +@pytest.mark.parametrize( + "kwargs", + [ + {"trace_id": "", "span_id": "span-1"}, + {"trace_id": "trace-1", "span_id": " "}, + {"trace_id": "trace-1", "span_id": "span-1", "parent_span_id": " "}, + {"trace_id": "trace-1", "span_id": "span-1", "parent_span_id": "span-1"}, + ], +) +def test_trace_context_rejects_invalid_identity(kwargs): + with pytest.raises(ContractValidationError): + TraceContext(**kwargs) + + +def test_component_identity_serializes_without_persona_dependency(): + component = ComponentIdentity( + component_id="kernel", + component_type="kernel", + runtime_id="python", + adapter_id="native-acp", + version="0.1.0", + source_ref="dbab7c1", + ) + + assert component.to_dict() == { + "component_id": "kernel", + "component_type": "kernel", + "runtime_id": "python", + "adapter_id": "native-acp", + "version": "0.1.0", + "source_ref": "dbab7c1", + } + + +@pytest.mark.parametrize("field", ["component_id", "component_type", "runtime_id", "adapter_id"]) +def test_component_identity_rejects_blank_required_fields(field): + values = { + "component_id": "kernel", + "component_type": "kernel", + "runtime_id": "python", + "adapter_id": "native-acp", + } + values[field] = "" + + with pytest.raises(ContractValidationError): + ComponentIdentity(**values) + + +def test_artifact_ref_serializes_valid_sha256(): + artifact = ArtifactRef( + artifact_id="artifact-1", + kind="input", + uri="memory://artifact-1", + version="v1", + sha256="a" * 64, + ) + + assert artifact.to_dict() == { + "artifact_id": "artifact-1", + "kind": "input", + "uri": "memory://artifact-1", + "version": "v1", + "sha256": "a" * 64, + } + + +@pytest.mark.parametrize("sha256", ["A" * 64, "a" * 63, "g" * 64, "not-a-hash"]) +def test_artifact_ref_rejects_malformed_sha256(sha256): + with pytest.raises(ContractValidationError): + ArtifactRef(artifact_id="artifact-1", kind="input", sha256=sha256) + + +@pytest.mark.parametrize("field", ["artifact_id", "kind"]) +def test_artifact_ref_rejects_blank_required_fields(field): + values = {"artifact_id": "artifact-1", "kind": "input"} + values[field] = " " + + with pytest.raises(ContractValidationError): + ArtifactRef(**values) From ad5168361f7fa7d3a47b2234c29e859d8b85cac5 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:22:27 -0400 Subject: [PATCH 05/18] feat: add core execution contract identities --- src/agent_control_plane/contract/model.py | 89 +++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/agent_control_plane/contract/model.py diff --git a/src/agent_control_plane/contract/model.py b/src/agent_control_plane/contract/model.py new file mode 100644 index 0000000..299c91e --- /dev/null +++ b/src/agent_control_plane/contract/model.py @@ -0,0 +1,89 @@ +"""Framework-neutral execution contract primitives.""" + +from dataclasses import asdict, dataclass +import re +from typing import Any, Dict, Optional + +SCHEMA_VERSION = "agent-control-plane.execution.v1" +_SHA256_RE = re.compile(r"^[0-9a-f]{64}$") + + +class ContractValidationError(ValueError): + """Raised when required execution-contract data is invalid.""" + + +def _required(value: str, field_name: str) -> str: + if not isinstance(value, str) or not value.strip(): + raise ContractValidationError(f"{field_name} must not be blank") + return value + + +@dataclass(frozen=True) +class ExecutionIdentity: + execution_id: str + run_id: str + schema_version: str = SCHEMA_VERSION + + def __post_init__(self) -> None: + _required(self.execution_id, "execution_id") + _required(self.run_id, "run_id") + if self.schema_version != SCHEMA_VERSION: + raise ContractValidationError(f"unsupported schema_version: {self.schema_version}") + + def to_dict(self) -> Dict[str, Any]: + return asdict(self) + + +@dataclass(frozen=True) +class TraceContext: + trace_id: str + span_id: str + parent_span_id: Optional[str] = None + + def __post_init__(self) -> None: + _required(self.trace_id, "trace_id") + _required(self.span_id, "span_id") + if self.parent_span_id is not None: + _required(self.parent_span_id, "parent_span_id") + if self.parent_span_id == self.span_id: + raise ContractValidationError("span_id must not equal parent_span_id") + + def to_dict(self) -> Dict[str, Any]: + return asdict(self) + + +@dataclass(frozen=True) +class ComponentIdentity: + component_id: str + component_type: str + runtime_id: str + adapter_id: str + version: Optional[str] = None + source_ref: Optional[str] = None + + def __post_init__(self) -> None: + _required(self.component_id, "component_id") + _required(self.component_type, "component_type") + _required(self.runtime_id, "runtime_id") + _required(self.adapter_id, "adapter_id") + + def to_dict(self) -> Dict[str, Any]: + return asdict(self) + + +@dataclass(frozen=True) +class ArtifactRef: + artifact_id: str + kind: str + uri: Optional[str] = None + version: Optional[str] = None + sha256: Optional[str] = None + + def __post_init__(self) -> None: + _required(self.artifact_id, "artifact_id") + _required(self.kind, "kind") + if self.sha256 is not None and _SHA256_RE.fullmatch(self.sha256) is None: + raise ContractValidationError("sha256 must be 64 lowercase hexadecimal characters") + + def to_dict(self) -> Dict[str, Any]: + return asdict(self) From ee3315fc0e508d3bf9e574e302a828c1fa6535d1 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:22:31 -0400 Subject: [PATCH 06/18] feat: export execution contract primitives --- src/agent_control_plane/contract/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/agent_control_plane/contract/__init__.py diff --git a/src/agent_control_plane/contract/__init__.py b/src/agent_control_plane/contract/__init__.py new file mode 100644 index 0000000..8f019ff --- /dev/null +++ b/src/agent_control_plane/contract/__init__.py @@ -0,0 +1,19 @@ +"""Public execution-contract API.""" + +from .model import ( + SCHEMA_VERSION, + ArtifactRef, + ComponentIdentity, + ContractValidationError, + ExecutionIdentity, + TraceContext, +) + +__all__ = [ + "SCHEMA_VERSION", + "ArtifactRef", + "ComponentIdentity", + "ContractValidationError", + "ExecutionIdentity", + "TraceContext", +] From 95c03ba1973b63f6d962c4ca578a49f1a48e04ee Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:23:15 -0400 Subject: [PATCH 07/18] test: define execution event contract behavior --- tests/test_contract.py | 106 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/tests/test_contract.py b/tests/test_contract.py index cbf22dc..5cb3f0c 100644 --- a/tests/test_contract.py +++ b/tests/test_contract.py @@ -5,6 +5,7 @@ ArtifactRef, ComponentIdentity, ContractValidationError, + ExecutionEvent, ExecutionIdentity, TraceContext, ) @@ -127,3 +128,108 @@ def test_artifact_ref_rejects_blank_required_fields(field): with pytest.raises(ContractValidationError): ArtifactRef(**values) + + +def _event_kwargs(): + return { + "event_type": "task.completed", + "identity": ExecutionIdentity(execution_id="exec-1", run_id="run-1"), + "trace": TraceContext(trace_id="trace-1", span_id="span-1"), + "component": ComponentIdentity( + component_id="kernel", + component_type="kernel", + runtime_id="python", + adapter_id="native-acp", + ), + "task_id": "task-1", + "status": "completed", + "utc_timestamp": "2026-09-14T23:00:00+00:00", + "monotonic_ns": 42, + } + + +def test_execution_event_serializes_deterministically_with_canonical_utc(): + input_artifacts = ( + ArtifactRef(artifact_id="input-1", kind="input"), + ArtifactRef(artifact_id="input-2", kind="input"), + ) + output_artifacts = (ArtifactRef(artifact_id="output-1", kind="output"),) + event = ExecutionEvent( + **_event_kwargs(), + capability="echo", + policy_decision_ref="policy-1", + input_artifacts=input_artifacts, + output_artifacts=output_artifacts, + detail="steps=1", + ) + + assert event.to_dict() == { + "event_type": "task.completed", + "identity": { + "execution_id": "exec-1", + "run_id": "run-1", + "schema_version": SCHEMA_VERSION, + }, + "trace": { + "trace_id": "trace-1", + "span_id": "span-1", + "parent_span_id": None, + }, + "component": { + "component_id": "kernel", + "component_type": "kernel", + "runtime_id": "python", + "adapter_id": "native-acp", + "version": None, + "source_ref": None, + }, + "task_id": "task-1", + "status": "completed", + "utc_timestamp": "2026-09-14T23:00:00Z", + "monotonic_ns": 42, + "capability": "echo", + "policy_decision_ref": "policy-1", + "input_artifacts": [ + {"artifact_id": "input-1", "kind": "input", "uri": None, "version": None, "sha256": None}, + {"artifact_id": "input-2", "kind": "input", "uri": None, "version": None, "sha256": None}, + ], + "output_artifacts": [ + {"artifact_id": "output-1", "kind": "output", "uri": None, "version": None, "sha256": None} + ], + "detail": "steps=1", + } + + +def test_execution_event_preserves_artifact_order(): + event = ExecutionEvent( + **_event_kwargs(), + input_artifacts=( + ArtifactRef(artifact_id="first", kind="input"), + ArtifactRef(artifact_id="second", kind="input"), + ), + ) + + assert [item["artifact_id"] for item in event.to_dict()["input_artifacts"]] == ["first", "second"] + + +def test_execution_event_rejects_negative_monotonic_value(): + values = _event_kwargs() + values["monotonic_ns"] = -1 + + with pytest.raises(ContractValidationError): + ExecutionEvent(**values) + + +@pytest.mark.parametrize( + "timestamp", + [ + "2026-09-14T23:00:00", + "2026-09-15T00:00:00+01:00", + ], +) +def test_execution_event_rejects_naive_or_non_utc_timestamp(timestamp): + values = _event_kwargs() + values["utc_timestamp"] = timestamp + + with pytest.raises(ContractValidationError): + ExecutionEvent(**values) From 8bb2d0fee339590a77241ca6cccc70a49001463e Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:24:02 -0400 Subject: [PATCH 08/18] feat: add execution event contract --- src/agent_control_plane/contract/model.py | 61 ++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/agent_control_plane/contract/model.py b/src/agent_control_plane/contract/model.py index 299c91e..2e2f273 100644 --- a/src/agent_control_plane/contract/model.py +++ b/src/agent_control_plane/contract/model.py @@ -1,8 +1,9 @@ """Framework-neutral execution contract primitives.""" from dataclasses import asdict, dataclass +from datetime import datetime, timedelta, timezone import re -from typing import Any, Dict, Optional +from typing import Any, Dict, Optional, Tuple SCHEMA_VERSION = "agent-control-plane.execution.v1" _SHA256_RE = re.compile(r"^[0-9a-f]{64}$") @@ -18,6 +19,20 @@ def _required(value: str, field_name: str) -> str: return value +def _canonical_utc_timestamp(value: str) -> str: + _required(value, "utc_timestamp") + candidate = value[:-1] + "+00:00" if value.endswith("Z") else value + try: + parsed = datetime.fromisoformat(candidate) + except ValueError as exc: + raise ContractValidationError("utc_timestamp must be valid ISO-8601") from exc + if parsed.tzinfo is None or parsed.utcoffset() is None: + raise ContractValidationError("utc_timestamp must be timezone-aware UTC") + if parsed.utcoffset() != timedelta(0): + raise ContractValidationError("utc_timestamp must use UTC") + return parsed.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") + + @dataclass(frozen=True) class ExecutionIdentity: execution_id: str @@ -87,3 +102,47 @@ def __post_init__(self) -> None: def to_dict(self) -> Dict[str, Any]: return asdict(self) + + +@dataclass(frozen=True) +class ExecutionEvent: + event_type: str + identity: ExecutionIdentity + trace: TraceContext + component: ComponentIdentity + task_id: str + status: str + utc_timestamp: str + monotonic_ns: int + capability: Optional[str] = None + policy_decision_ref: Optional[str] = None + input_artifacts: Tuple[ArtifactRef, ...] = () + output_artifacts: Tuple[ArtifactRef, ...] = () + detail: Optional[str] = None + + def __post_init__(self) -> None: + _required(self.event_type, "event_type") + _required(self.task_id, "task_id") + _required(self.status, "status") + if isinstance(self.monotonic_ns, bool) or not isinstance(self.monotonic_ns, int): + raise ContractValidationError("monotonic_ns must be an integer >= 0") + if self.monotonic_ns < 0: + raise ContractValidationError("monotonic_ns must be an integer >= 0") + object.__setattr__(self, "utc_timestamp", _canonical_utc_timestamp(self.utc_timestamp)) + + def to_dict(self) -> Dict[str, Any]: + return { + "event_type": self.event_type, + "identity": self.identity.to_dict(), + "trace": self.trace.to_dict(), + "component": self.component.to_dict(), + "task_id": self.task_id, + "status": self.status, + "utc_timestamp": self.utc_timestamp, + "monotonic_ns": self.monotonic_ns, + "capability": self.capability, + "policy_decision_ref": self.policy_decision_ref, + "input_artifacts": [artifact.to_dict() for artifact in self.input_artifacts], + "output_artifacts": [artifact.to_dict() for artifact in self.output_artifacts], + "detail": self.detail, + } From 9fb67b5d1b93e20e0f9c3ea4310db048143d3f40 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:24:07 -0400 Subject: [PATCH 09/18] feat: export execution event contract --- src/agent_control_plane/contract/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/agent_control_plane/contract/__init__.py b/src/agent_control_plane/contract/__init__.py index 8f019ff..0af3c23 100644 --- a/src/agent_control_plane/contract/__init__.py +++ b/src/agent_control_plane/contract/__init__.py @@ -5,6 +5,7 @@ ArtifactRef, ComponentIdentity, ContractValidationError, + ExecutionEvent, ExecutionIdentity, TraceContext, ) @@ -14,6 +15,7 @@ "ArtifactRef", "ComponentIdentity", "ContractValidationError", + "ExecutionEvent", "ExecutionIdentity", "TraceContext", ] From 8d03155c88957eb9600a84a57626681a5116f4cd Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:25:19 -0400 Subject: [PATCH 10/18] test: define legacy provenance mapping behavior --- tests/test_contract.py | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/test_contract.py b/tests/test_contract.py index 5cb3f0c..41d5b58 100644 --- a/tests/test_contract.py +++ b/tests/test_contract.py @@ -8,7 +8,9 @@ ExecutionEvent, ExecutionIdentity, TraceContext, + map_provenance_event, ) +from agent_control_plane.provenance import ProvenanceEvent def test_execution_identity_serializes_deterministically(): @@ -233,3 +235,74 @@ def test_execution_event_rejects_naive_or_non_utc_timestamp(timestamp): with pytest.raises(ContractValidationError): ExecutionEvent(**values) + + +def _mapping_context(run_id="run-1"): + return { + "identity": ExecutionIdentity(execution_id="exec-1", run_id=run_id), + "trace": TraceContext(trace_id="trace-1", span_id="span-1"), + "component": ComponentIdentity( + component_id="kernel", + component_type="kernel", + runtime_id="python", + adapter_id="native-acp", + ), + "monotonic_ns": 77, + } + + +def test_map_provenance_event_preserves_legacy_fields_and_context(): + source = ProvenanceEvent( + event="task.completed", + task_id="task-1", + run_id="run-1", + capability="echo", + state="completed", + detail="steps=1", + timestamp="2026-09-14T23:00:00+00:00", + ) + input_artifact = ArtifactRef(artifact_id="input-1", kind="input") + + mapped = map_provenance_event( + source, + **_mapping_context(), + input_artifacts=(input_artifact,), + policy_decision_ref="policy-1", + ) + + assert mapped.event_type == "task.completed" + assert mapped.task_id == "task-1" + assert mapped.identity.run_id == "run-1" + assert mapped.capability == "echo" + assert mapped.status == "completed" + assert mapped.detail == "steps=1" + assert mapped.utc_timestamp == "2026-09-14T23:00:00Z" + assert mapped.monotonic_ns == 77 + assert mapped.input_artifacts == (input_artifact,) + assert mapped.policy_decision_ref == "policy-1" + + +def test_map_provenance_event_maps_absent_state_to_unspecified(): + source = ProvenanceEvent( + event="task.rejected", + task_id="task-1", + run_id="run-1", + timestamp="2026-09-14T23:00:00Z", + ) + + mapped = map_provenance_event(source, **_mapping_context()) + + assert mapped.status == "unspecified" + + +def test_map_provenance_event_rejects_run_mismatch(): + source = ProvenanceEvent( + event="task.started", + task_id="task-1", + run_id="run-source", + state="running", + timestamp="2026-09-14T23:00:00Z", + ) + + with pytest.raises(ContractValidationError): + map_provenance_event(source, **_mapping_context(run_id="run-contract")) From 10fd1255afcc9ddacf83ab8832dde6de3e285bb7 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:25:47 -0400 Subject: [PATCH 11/18] feat: map legacy provenance into execution contract --- src/agent_control_plane/contract/mapping.py | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/agent_control_plane/contract/mapping.py diff --git a/src/agent_control_plane/contract/mapping.py b/src/agent_control_plane/contract/mapping.py new file mode 100644 index 0000000..38febd7 --- /dev/null +++ b/src/agent_control_plane/contract/mapping.py @@ -0,0 +1,47 @@ +"""Explicit mapping from legacy ACP provenance into the execution contract.""" + +from typing import Iterable, Optional + +from ..provenance import ProvenanceEvent +from .model import ( + ArtifactRef, + ComponentIdentity, + ContractValidationError, + ExecutionEvent, + ExecutionIdentity, + TraceContext, +) + + +def map_provenance_event( + event: ProvenanceEvent, + *, + identity: ExecutionIdentity, + trace: TraceContext, + component: ComponentIdentity, + monotonic_ns: int, + input_artifacts: Iterable[ArtifactRef] = (), + output_artifacts: Iterable[ArtifactRef] = (), + policy_decision_ref: Optional[str] = None, +) -> ExecutionEvent: + """Convert one legacy provenance event without fabricating missing context.""" + if event.run_id != identity.run_id: + raise ContractValidationError( + f"provenance run_id {event.run_id!r} does not match contract run_id {identity.run_id!r}" + ) + + return ExecutionEvent( + event_type=event.event, + identity=identity, + trace=trace, + component=component, + task_id=event.task_id, + status=event.state if event.state is not None else "unspecified", + utc_timestamp=event.timestamp, + monotonic_ns=monotonic_ns, + capability=event.capability, + policy_decision_ref=policy_decision_ref, + input_artifacts=tuple(input_artifacts), + output_artifacts=tuple(output_artifacts), + detail=event.detail, + ) From 069474395708fe1402802154f847b00a666e0c71 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:26:03 -0400 Subject: [PATCH 12/18] feat: export legacy provenance mapper --- src/agent_control_plane/contract/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/agent_control_plane/contract/__init__.py b/src/agent_control_plane/contract/__init__.py index 0af3c23..76187ce 100644 --- a/src/agent_control_plane/contract/__init__.py +++ b/src/agent_control_plane/contract/__init__.py @@ -1,5 +1,6 @@ """Public execution-contract API.""" +from .mapping import map_provenance_event from .model import ( SCHEMA_VERSION, ArtifactRef, @@ -18,4 +19,5 @@ "ExecutionEvent", "ExecutionIdentity", "TraceContext", + "map_provenance_event", ] From 0e1b63a0bc4251093108bb73e42cd5273cd83387 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:27:29 -0400 Subject: [PATCH 13/18] test: define execution contract round-trip behavior --- tests/test_contract_roundtrip.py | 69 ++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/test_contract_roundtrip.py diff --git a/tests/test_contract_roundtrip.py b/tests/test_contract_roundtrip.py new file mode 100644 index 0000000..a96e22d --- /dev/null +++ b/tests/test_contract_roundtrip.py @@ -0,0 +1,69 @@ +import pytest + +from agent_control_plane.contract import ( + SCHEMA_VERSION, + ArtifactRef, + ComponentIdentity, + ContractValidationError, + ExecutionEvent, + ExecutionIdentity, + TraceContext, +) + + +def _full_event(): + return ExecutionEvent( + event_type="task.completed", + identity=ExecutionIdentity(execution_id="exec-1", run_id="run-1"), + trace=TraceContext(trace_id="trace-1", span_id="span-1", parent_span_id="span-0"), + component=ComponentIdentity( + component_id="kernel", + component_type="kernel", + runtime_id="python", + adapter_id="native-acp", + version="0.1.0", + source_ref="commit:dbab7c1", + ), + task_id="task-1", + status="completed", + utc_timestamp="2026-09-14T23:00:00Z", + monotonic_ns=123, + capability="echo", + policy_decision_ref="policy-1", + input_artifacts=( + ArtifactRef( + artifact_id="input-1", + kind="input", + uri="memory://input-1", + version="v1", + sha256="a" * 64, + ), + ), + output_artifacts=(ArtifactRef(artifact_id="output-1", kind="output"),), + detail="steps=1", + ) + + +def test_execution_event_round_trip_preserves_semantics(): + event = _full_event() + + reconstructed = ExecutionEvent.from_dict(event.to_dict()) + + assert reconstructed == event + assert reconstructed.to_dict() == event.to_dict() + + +def test_execution_event_from_dict_rejects_unsupported_schema_version(): + payload = _full_event().to_dict() + payload["identity"]["schema_version"] = "agent-control-plane.execution.v999" + + with pytest.raises(ContractValidationError): + ExecutionEvent.from_dict(payload) + + +def test_execution_event_from_dict_rejects_malformed_structure_fail_closed(): + payload = _full_event().to_dict() + del payload["trace"]["span_id"] + + with pytest.raises(ContractValidationError): + ExecutionEvent.from_dict(payload) From 79608046c2490023e8849d37b274a7b2bdbdf694 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:28:15 -0400 Subject: [PATCH 14/18] feat: add execution contract round-trip validation --- src/agent_control_plane/contract/model.py | 44 ++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/agent_control_plane/contract/model.py b/src/agent_control_plane/contract/model.py index 2e2f273..c15bce2 100644 --- a/src/agent_control_plane/contract/model.py +++ b/src/agent_control_plane/contract/model.py @@ -3,7 +3,7 @@ from dataclasses import asdict, dataclass from datetime import datetime, timedelta, timezone import re -from typing import Any, Dict, Optional, Tuple +from typing import Any, Dict, Mapping, Optional, Tuple SCHEMA_VERSION = "agent-control-plane.execution.v1" _SHA256_RE = re.compile(r"^[0-9a-f]{64}$") @@ -146,3 +146,45 @@ def to_dict(self) -> Dict[str, Any]: "output_artifacts": [artifact.to_dict() for artifact in self.output_artifacts], "detail": self.detail, } + + @classmethod + def from_dict(cls, data: Mapping[str, Any]) -> "ExecutionEvent": + """Reconstruct a serialized event through the same fail-closed validators.""" + if not isinstance(data, Mapping): + raise ContractValidationError("execution event payload must be a mapping") + try: + identity_data = data["identity"] + trace_data = data["trace"] + component_data = data["component"] + input_artifacts_data = data.get("input_artifacts", ()) + output_artifacts_data = data.get("output_artifacts", ()) + if not isinstance(identity_data, Mapping): + raise ContractValidationError("identity must be a mapping") + if not isinstance(trace_data, Mapping): + raise ContractValidationError("trace must be a mapping") + if not isinstance(component_data, Mapping): + raise ContractValidationError("component must be a mapping") + if not isinstance(input_artifacts_data, (list, tuple)): + raise ContractValidationError("input_artifacts must be a sequence") + if not isinstance(output_artifacts_data, (list, tuple)): + raise ContractValidationError("output_artifacts must be a sequence") + + return cls( + event_type=data["event_type"], + identity=ExecutionIdentity(**dict(identity_data)), + trace=TraceContext(**dict(trace_data)), + component=ComponentIdentity(**dict(component_data)), + task_id=data["task_id"], + status=data["status"], + utc_timestamp=data["utc_timestamp"], + monotonic_ns=data["monotonic_ns"], + capability=data.get("capability"), + policy_decision_ref=data.get("policy_decision_ref"), + input_artifacts=tuple(ArtifactRef(**dict(item)) for item in input_artifacts_data), + output_artifacts=tuple(ArtifactRef(**dict(item)) for item in output_artifacts_data), + detail=data.get("detail"), + ) + except ContractValidationError: + raise + except (KeyError, TypeError, ValueError) as exc: + raise ContractValidationError("malformed execution event payload") from exc From f100f17f188e31fef6c9e1067767303757616049 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:30:03 -0400 Subject: [PATCH 15/18] docs: describe execution contract evidence boundary --- docs/CONTROL_PLANE_KERNEL_SPEC.md | 57 ++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/docs/CONTROL_PLANE_KERNEL_SPEC.md b/docs/CONTROL_PLANE_KERNEL_SPEC.md index 52e4436..0dad4ce 100644 --- a/docs/CONTROL_PLANE_KERNEL_SPEC.md +++ b/docs/CONTROL_PLANE_KERNEL_SPEC.md @@ -53,32 +53,79 @@ This is local deterministic routing. It is **not** distributed scheduling, load Policy evaluation is represented as an explicit allow/deny decision with an optional reason. Policy is intentionally separated from execution so higher-level governance systems can supply policies without coupling them to the kernel. +The current kernel policy hook is a **pre-execution** allow/deny mechanism. The cross-runtime execution contract described below can carry an optional `policy_decision_ref`, but that field only associates an identified decision with an execution event. It does not add in-execution or post-execution policy engines and does not itself establish authorization. + ## Provenance -Each execution transition emits a structured `ProvenanceEvent` containing: +Each kernel execution transition emits a structured `ProvenanceEvent` containing: - event type; - task identifier; +- run identifier; - capability when applicable; - resulting state when applicable; - optional failure or usage detail; - UTC timestamp. -The event list is currently process-local and non-durable. +`ControlPlane.provenance_manifest()` continues to export the existing `agent-control-plane.provenance.v1` representation. That manifest remains process-local and non-durable. + +## Cross-runtime execution contract + +ACP additionally defines a versioned framework-neutral execution/trace contract with schema identity: + +`agent-control-plane.execution.v1` + +The contract is additive to the existing kernel provenance representation. It currently provides immutable typed records for: + +- `ExecutionIdentity` — explicit execution ID, run ID, and schema version; +- `TraceContext` — trace ID, span ID, and optional parent span ID; +- `ComponentIdentity` — component, component type, runtime, adapter, and optional source/version identity; +- `ArtifactRef` — artifact identity/type plus optional URI, version, and SHA-256 reference; +- `ExecutionEvent` — event type, execution/trace/component context, task/status, UTC timestamp, monotonic ordering value, optional capability/policy reference/artifacts/detail. + +Required identity fields fail closed when blank. Unsupported schema versions, span self-parenting, malformed SHA-256 values, negative/non-integer monotonic values, and naive or non-UTC event timestamps are rejected with `ContractValidationError`. + +Event timestamps serialize canonically in UTC with a trailing `Z`. Contract serialization is deterministic and `ExecutionEvent.from_dict()` reconstructs serialized events through the same validation paths, so malformed nested data does not bypass validation. + +### Legacy provenance mapping + +`map_provenance_event(...)` converts an existing `ProvenanceEvent` into an `ExecutionEvent` only when the caller supplies execution, trace, component, and monotonic context that the legacy event does not contain. + +The mapper: + +- preserves the legacy event type, task ID, capability, state, detail, and UTC timestamp; +- requires the legacy `run_id` to equal the supplied contract `run_id` and fails closed on mismatch; +- maps an absent legacy state to the explicit non-success placeholder `unspecified`; +- does not fabricate trace IDs, component IDs, artifacts, policy decisions, or successful status. + +This mapper does not change `agent-control-plane.provenance.v1` and does not imply that historical events contained trace/span data they did not record. + +### Current conformance boundary + +The present contract tests establish ACP-native schema construction, validation, deterministic serialization, round-trip reconstruction, and legacy-provenance mapping under the tested Python environments. + +They do **not** establish cross-runtime portability. No materially different external runtime adapter is implemented in this slice, and there is not yet a two-runtime conformance result using the same core schema without fork. ## Evidence boundary -The kernel and tests demonstrate local deterministic behavior only. The budget tests establish the cooperative count/cost accounting and fail-closed exhaustion properties exercised by those tests. They do not establish: +The kernel and tests demonstrate local deterministic behavior only. The budget tests establish the cooperative count/cost accounting and fail-closed exhaustion properties exercised by those tests. The execution-contract tests establish only the ACP-native contract properties exercised by those tests. + +They do not establish: +- general cross-runtime portability; - production reliability; - distributed correctness; - security authorization; +- DGAF authorization or governance effectiveness; +- PDMAL scientific validity or efficacy; - persistence guarantees; +- durable or tamper-evident provenance; +- cryptographic attestation or custody independence; - hard execution-time enforcement; - provider-accurate token/cost metering; - retry/checkpoint/delegation correctness; - model quality; - multi-agent coordination quality; -- governance effectiveness. +- superiority to existing orchestration standards or frameworks. -Those claims require separate implementation and empirical validation. +Those claims require separate implementation and evidence. From acf4d076bf8457b975022c71b508e81e8555988d Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:30:24 -0400 Subject: [PATCH 16/18] docs: document framework-neutral execution contract --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 835009f..469ccdb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **Agent Control Plane (ACP)** is an experimental control-plane kernel for coordinating, constraining, and observing AI-agent/workflow execution. -> **Epistemic status:** Experimental engineering. The repository contains an executable deterministic kernel with policy hooks, cooperative task budgets, and run-scoped provenance. It is not a complete autonomous control plane, security boundary, or production-ready orchestration platform. +> **Epistemic status:** Experimental engineering. The repository contains an executable deterministic kernel with policy hooks, cooperative task budgets, run-scoped provenance, and an additive versioned framework-neutral execution/trace contract. It is not a complete autonomous control plane, security boundary, proven cross-runtime portability layer, or production-ready orchestration platform. ## Current implementation @@ -20,9 +20,15 @@ The kernel currently provides: - run-scoped provenance events carrying both task ID and run ID; - terminal provenance containing reported resource usage; - a portable `agent-control-plane.provenance.v1` manifest for the current in-memory run; -- tests covering successful dispatch, handler failure, cancellation, policy decisions, adversarial/invariant cases, duplicate registration, unknown-capability evidence, provenance binding, exact-limit budget use, atomic overrun handling, and suppressed-exception fail-closure; +- additive `agent-control-plane.execution.v1` contract types for execution, trace/span, component/runtime/adapter, artifact, and event identity; +- strict fail-closed validation of required identities, schema version, trace parentage, SHA-256 references, monotonic values, and UTC timestamps; +- deterministic execution-event serialization with canonical UTC `Z` timestamps and validated round-trip reconstruction; +- an explicit mapper from legacy `ProvenanceEvent` records into the new execution contract when the caller supplies context absent from legacy provenance; +- tests covering successful dispatch, handler failure, cancellation, policy decisions, adversarial/invariant cases, duplicate registration, unknown-capability evidence, provenance binding, exact-limit budget use, atomic overrun handling, suppressed-exception fail-closure, execution-contract validation/serialization, round-trip reconstruction, and provenance mapping; - GitHub Actions CI for the Python suite. +The legacy provenance manifest and the execution contract are distinct representations. `agent-control-plane.provenance.v1` remains unchanged and process-local. Mapping a legacy event into `agent-control-plane.execution.v1` requires caller-supplied execution/trace/component/monotonic context; ACP does not fabricate missing historical trace or identity data. + The provenance manifest is **an in-memory/exportable execution record**, not durable storage, tamper-evident attestation, or an external audit log. Resource accounting is **cooperative**: handlers or runtime/tool adapters must report usage with `Task.consume(...)`; the kernel does not infer provider token counts, tool usage, elapsed time, or cost automatically. ## Example @@ -51,6 +57,38 @@ manifest = plane.provenance_manifest() assert manifest["run_id"] == "example-run" ``` +The versioned execution contract is available from the additive subpackage: + +```python +from agent_control_plane.contract import ( + ComponentIdentity, + ExecutionEvent, + ExecutionIdentity, + TraceContext, +) + +identity = ExecutionIdentity(execution_id="exec-1", run_id="run-1") +trace = TraceContext(trace_id="trace-1", span_id="span-1") +component = ComponentIdentity( + component_id="kernel", + component_type="kernel", + runtime_id="python", + adapter_id="native-acp", +) + +event = ExecutionEvent( + event_type="task.completed", + identity=identity, + trace=trace, + component=component, + task_id="task-1", + status="completed", + utc_timestamp="2026-09-14T23:00:00Z", + monotonic_ns=1, +) +assert ExecutionEvent.from_dict(event.to_dict()) == event +``` + Run verification with: ```bash @@ -60,7 +98,7 @@ python -m pytest ## Fail-closed boundaries -The current kernel deliberately rejects or records several ambiguous states: +The current kernel and contract deliberately reject or record several ambiguous states: - an empty capability cannot be registered; - an already registered capability cannot be silently replaced; @@ -68,17 +106,27 @@ The current kernel deliberately rejects or records several ambiguous states: - an unknown capability produces a provenance rejection event and raises `KeyError`; - a handler exception becomes a recorded `FAILED` task state; - a policy denial is recorded rather than treated as successful execution; -- an attempted cooperative resource-budget overrun becomes `BUDGET_EXHAUSTED` and cannot be converted into successful completion by catching the budget exception inside the handler. - -These properties are local software invariants. They do not establish distributed reliability or system security. +- an attempted cooperative resource-budget overrun becomes `BUDGET_EXHAUSTED` and cannot be converted into successful completion by catching the budget exception inside the handler; +- blank required execution/trace/component/artifact identity is rejected; +- unsupported execution-contract schema versions are rejected; +- trace span self-parenting is rejected; +- malformed optional SHA-256 references are rejected; +- negative or non-integer monotonic values are rejected; +- naive or non-UTC event timestamps are rejected; +- legacy provenance cannot be mapped across a mismatched run identity; +- absent legacy state maps to explicit `unspecified`, never inferred success. + +These properties are local software invariants under the tested conditions. They do not establish distributed reliability, system security, governance efficacy, or cross-runtime portability. ## Not yet implemented / established Unless added and independently verified later, ACP does **not** currently provide: +- materially different external runtime adapters conforming to `agent-control-plane.execution.v1`; +- two-runtime portability evidence without core-schema fork; - model/provider integrations; - automatic provider token/cost/tool-call metering; -- durable event or budget persistence; +- durable event, trace, or budget persistence; - cryptographic/tamper-evident provenance; - distributed execution; - authentication or authorization infrastructure; @@ -96,15 +144,17 @@ Claims in this repository should distinguish: `DEFINED → IMPLEMENTED → COMPUTED → VERIFIED → ATTESTED → HISTORICAL → HYPOTHESIS → METAPHOR → UNSUPPORTED → DEPRECATED` -A passing unit test establishes only the tested property under that test environment. An exported provenance manifest is not an attestation. Cooperative resource accounting is not proof of externally measured consumption. Cross-repository use does not transfer validation. +A passing unit test establishes only the tested property under that test environment. An exported provenance manifest is not an attestation. Cooperative resource accounting is not proof of externally measured consumption. ACP-native execution-contract conformance is not proof of cross-runtime portability. Cross-repository use does not transfer validation. ## Ecosystem relationship -ACP may provide reusable primitives to other `ndrorchestration` projects. `DGAF-Framework`, PDMAL, Orbit-Driftwatch, Sentinel, and other repositories maintain separate evidence and governance boundaries. Integration should be demonstrated through explicit interfaces and tests. +ACP is the preferred experimental implementation host for a framework-neutral execution contract. DGAF retains governance, authorization, provenance/evidence-discipline, and evidence-state authority. PDMAL is a governed empirical workload/research consumer rather than a prerequisite parent of ACP. Other `ndrorchestration` repositories maintain separate evidence boundaries. + +A framework/runtime adapter may populate the ACP execution contract, but adapter readiness, CI success, deployment health, or successful execution cannot self-promote DGAF authorization, PDMAL scientific validity, empirical efficacy, or general portability. ## Current status -**Experimental / development track — executable kernel with run-scoped provenance, fail-closed dispatch invariants, and cooperative task-budget accounting.** +**Experimental / development track — executable kernel with run-scoped provenance, fail-closed dispatch invariants, cooperative task-budget accounting, and an ACP-native versioned execution/trace contract with deterministic serialization and validation. Cross-runtime portability remains NOT ESTABLISHED.** ## Provenance From 00538b772356f0b4c2884a281015696696e2db13 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:32:07 -0400 Subject: [PATCH 17/18] test: enforce contract nested type validation --- tests/test_contract.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/test_contract.py b/tests/test_contract.py index 41d5b58..bb50552 100644 --- a/tests/test_contract.py +++ b/tests/test_contract.py @@ -117,7 +117,7 @@ def test_artifact_ref_serializes_valid_sha256(): } -@pytest.mark.parametrize("sha256", ["A" * 64, "a" * 63, "g" * 64, "not-a-hash"]) +@pytest.mark.parametrize("sha256", ["A" * 64, "a" * 63, "g" * 64, "not-a-hash", 123]) def test_artifact_ref_rejects_malformed_sha256(sha256): with pytest.raises(ContractValidationError): ArtifactRef(artifact_id="artifact-1", kind="input", sha256=sha256) @@ -237,6 +237,24 @@ def test_execution_event_rejects_naive_or_non_utc_timestamp(timestamp): ExecutionEvent(**values) +@pytest.mark.parametrize( + ("field", "invalid_value"), + [ + ("identity", "not-an-identity"), + ("trace", "not-a-trace"), + ("component", "not-a-component"), + ("input_artifacts", ("not-an-artifact",)), + ("output_artifacts", ("not-an-artifact",)), + ], +) +def test_execution_event_rejects_invalid_nested_contract_types(field, invalid_value): + values = _event_kwargs() + values[field] = invalid_value + + with pytest.raises(ContractValidationError): + ExecutionEvent(**values) + + def _mapping_context(run_id="run-1"): return { "identity": ExecutionIdentity(execution_id="exec-1", run_id=run_id), From 07a09698ca66e8837d04e6ec05b4de3448eced04 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:33:03 -0400 Subject: [PATCH 18/18] fix: fail closed on malformed nested contract types --- src/agent_control_plane/contract/model.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/agent_control_plane/contract/model.py b/src/agent_control_plane/contract/model.py index c15bce2..7d61d62 100644 --- a/src/agent_control_plane/contract/model.py +++ b/src/agent_control_plane/contract/model.py @@ -97,8 +97,9 @@ class ArtifactRef: def __post_init__(self) -> None: _required(self.artifact_id, "artifact_id") _required(self.kind, "kind") - if self.sha256 is not None and _SHA256_RE.fullmatch(self.sha256) is None: - raise ContractValidationError("sha256 must be 64 lowercase hexadecimal characters") + if self.sha256 is not None: + if not isinstance(self.sha256, str) or _SHA256_RE.fullmatch(self.sha256) is None: + raise ContractValidationError("sha256 must be 64 lowercase hexadecimal characters") def to_dict(self) -> Dict[str, Any]: return asdict(self) @@ -124,6 +125,20 @@ def __post_init__(self) -> None: _required(self.event_type, "event_type") _required(self.task_id, "task_id") _required(self.status, "status") + if not isinstance(self.identity, ExecutionIdentity): + raise ContractValidationError("identity must be ExecutionIdentity") + if not isinstance(self.trace, TraceContext): + raise ContractValidationError("trace must be TraceContext") + if not isinstance(self.component, ComponentIdentity): + raise ContractValidationError("component must be ComponentIdentity") + if not isinstance(self.input_artifacts, tuple) or not all( + isinstance(artifact, ArtifactRef) for artifact in self.input_artifacts + ): + raise ContractValidationError("input_artifacts must be a tuple of ArtifactRef") + if not isinstance(self.output_artifacts, tuple) or not all( + isinstance(artifact, ArtifactRef) for artifact in self.output_artifacts + ): + raise ContractValidationError("output_artifacts must be a tuple of ArtifactRef") if isinstance(self.monotonic_ns, bool) or not isinstance(self.monotonic_ns, int): raise ContractValidationError("monotonic_ns must be an integer >= 0") if self.monotonic_ns < 0: