From b736d203152f73f320d5e9eb359a6dc41c3414e9 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:06:00 -0400 Subject: [PATCH 01/21] docs: specify GSAE-E0 Stage-A conformance harness --- ...e-e0-stage-a-conformance-harness-design.md | 273 ++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-16-gsae-e0-stage-a-conformance-harness-design.md diff --git a/docs/superpowers/specs/2026-09-16-gsae-e0-stage-a-conformance-harness-design.md b/docs/superpowers/specs/2026-09-16-gsae-e0-stage-a-conformance-harness-design.md new file mode 100644 index 0000000..f1b54d2 --- /dev/null +++ b/docs/superpowers/specs/2026-09-16-gsae-e0-stage-a-conformance-harness-design.md @@ -0,0 +1,273 @@ +# GSAE-E0 Stage-A Conformance Harness — Design + +Date: 2026-09-16 +Status: APPROVED DESIGN / SPEC REVIEW PENDING +Repository: `ndrorchestration/agent-control-plane` +Source-under-test lineage: PR #5 exact head `07a09698ca66e8837d04e6ec05b4de3448eced04` +Candidate schema: `agent-control-plane.execution.v1` + +## 1. Purpose + +Build a research-scoped, deterministic conformance harness for `GSAE-E0` that tests whether the current ACP execution contract can represent frozen ordinary and consequential governance states without semantic overload, runtime-specific reinterpretation, or a core-schema fork. + +This apparatus is evidence-producing research infrastructure only. It must not change the ACP contract under test, authorize GSAE-E0 execution, establish portability, establish DGAF/GSAE efficacy, or imply production/security readiness. + +## 2. Design principle + +The harness measures the contract before attempting to improve it. Stage A imports the exact bound contract implementation and treats missing or ambiguous semantics as results. It must not encode missing governance meanings into free-form `detail`, opaque artifact references, or runtime-specific strings and then count those encodings as structured coverage. + +## 3. Scope + +In scope: + +- deterministic fixture definitions; +- exact source/schema/fixture identity binding; +- native ACP conformance fixtures; +- governance-authority semantic probes `AUTH-01` through `AUTH-10`; +- negative controls for malformed or mismatched inputs; +- explicit exception classification; +- deterministic result-matrix generation; +- machine-readable evidence manifests; +- fail-closed fixture/result validation; +- tests for determinism, identity binding, classification, and negative controls. + +Out of scope: + +- changing `agent-control-plane.execution.v1` during Stage A; +- adding a second runtime or claiming portability; +- provider/network integrations; +- DGAF/GSAE authorization or efficacy conclusions; +- production/security certification; +- durable or tamper-evident provenance claims; +- autonomous/self-improving runtime behavior; +- schema redesign before the Stage-A evidence package is frozen. + +## 4. Repository layout + +Use a research-isolated package: + +```text +experiments/gsae_e0/ + __init__.py + README.md + schema.py + fixtures.py + classify.py + runner.py + manifest.py + fixtures/ + stage_a_v1.json + tests/ + test_fixture_schema.py + test_classification.py + test_runner.py + test_manifest.py +``` + +The package may import public ACP contract types from `agent_control_plane.contract`. ACP production modules must not import from `experiments.gsae_e0`. + +## 5. Fixed source binding + +Every Stage-A evidence bundle must bind repository identity, exact source-under-test SHA, schema version, fixture-set version, fixture-manifest SHA-256, harness commit SHA, test command, and run/result identity. + +A source-under-test SHA change invalidates the previous bundle for current-state claims and requires explicit rebind/re-run. Historical results remain provenance. + +Initial source under test: + +`07a09698ca66e8837d04e6ec05b4de3448eced04` + +Its open PR status is metadata only and is not evidence that the implementation is accepted on `main`. + +## 6. Fixture model + +Each fixture is immutable by identifier within a fixture-set version and contains at minimum: + +- `fixture_id`; +- `family`; +- `title`; +- `purpose`; +- `criticality` (`critical` or `noncritical`); +- `required_semantics`; +- `input_spec`; +- `expected_classification_domain`; +- optional expected exception class for negative controls. + +Malformed fixture definitions fail closed before execution and produce no conformance result. + +### Native conformance fixtures + +1. allowed ordinary completion; +2. policy denial; +3. unknown-capability rejection; +4. handler/runtime failure; +5. cancellation; +6. cooperative budget exhaustion; +7. input/output artifact linkage; +8. trace parent/child linkage; +9. policy-decision reference binding; +10. legacy provenance to execution-contract mapping; +11. provenance/contract run-ID mismatch rejection; +12. malformed schema/identity/timestamp/hash/nested-contract rejection. + +### Governance-authority probes + +- `AUTH-01`: principal / acting identity; +- `AUTH-02`: requested capability; +- `AUTH-03`: resource / target scope; +- `AUTH-04`: operation / action semantics; +- `AUTH-05`: policy identity plus version/hash binding; +- `AUTH-06`: decision identity and explicit allow/deny/conditional semantics; +- `AUTH-07`: lease / authority expiry; +- `AUTH-08`: delegation chain / delegator scope; +- `AUTH-09`: permitted/forbidden actions or authority conditions; +- `AUTH-10`: reason codes plus decision-to-execution provenance linkage. + +## 7. Structured-coverage rule + +A semantic requirement counts as `STRUCTURED_COVERAGE` only when it has a stable, machine-addressable field or typed composition in the contract under test with a meaning that does not depend on free-form prose or runtime-specific reinterpretation. + +The following do not count as structured coverage by themselves: + +- `detail` strings; +- arbitrary artifact payload contents; +- overloaded `status` values; +- runtime-specific conventions embedded in identifiers; +- external documentation not represented in the contract instance. + +This prevents the harness from manufacturing coverage through generic escape hatches. + +## 8. Exception taxonomy + +Every non-covered or failed fixture receives exactly one primary class: + +- `MISSING_CORE_SEMANTIC`; +- `AMBIGUOUS_SEMANTIC`; +- `RUNTIME_SPECIFIC`; +- `ADAPTER_COMPLEXITY`; +- `NONCRITICAL_EXTENSION`; +- `MALFORMED_INPUT`; +- `IMPLEMENTATION_DEFECT`; +- `PROVENANCE_GAP`. + +Explanatory notes may accompany the result, but the primary class remains machine-readable. + +## 9. Result model + +Each fixture result records result identity, fixture identity, source/schema/fixture binding, disposition, primary exception class when applicable, structured fields used, deterministic evidence summary, error information for rejected/malformed cases, and immutable retry linkage. + +A retry receives a new result identity and must never overwrite prior evidence. Timestamps may be retained as provenance metadata but are not semantic ordering authority. + +## 10. Aggregate disposition + +Stage A may produce only: + +- `FEASIBLE_FOR_FROZEN_SCOPE`; +- `CONDITIONALLY_FEASIBLE_NARROW`; +- `NOT_FEASIBLE_FOR_FROZEN_SCOPE`; +- `NOT_ESTABLISHED`. + +Rules: + +- missing required evidence yields `NOT_ESTABLISHED`; +- a critical `MISSING_CORE_SEMANTIC` or `AMBIGUOUS_SEMANTIC` cannot be overridden by a high coverage percentage; +- `FEASIBLE_FOR_FROZEN_SCOPE` requires structured coverage for every critical frozen semantic plus passing required negative controls; +- `CONDITIONALLY_FEASIBLE_NARROW` requires uncovered items to be noncritical to a clearly stated narrower scope; +- `NOT_FEASIBLE_FOR_FROZEN_SCOPE` applies when one or more critical frozen semantics cannot be represented without violating the structured-coverage rule. + +The aggregate is a contract-feasibility result only, never an authorization, safety, efficacy, portability, or production-readiness conclusion. + +## 11. Runner behavior + +The runner must: + +1. validate the fixture manifest; +2. verify exact source/schema binding; +3. compute the fixture-manifest hash; +4. execute native conformance fixtures deterministically; +5. evaluate authority-semantic probes against the typed contract surface; +6. classify each fixture without mutating the contract implementation; +7. emit a stable machine-readable result matrix; +8. derive the bounded aggregate disposition; +9. emit an evidence manifest linking component identities and hashes. + +No network access or provider dependency is permitted in Stage A. + +## 12. Evidence bundle + +The canonical Stage-A evidence bundle consists of: + +- frozen fixture manifest; +- fixture-manifest SHA-256; +- source-under-test identity; +- harness identity; +- result matrix; +- aggregate disposition; +- test-output summary; +- evidence manifest linking component identities/hashes. + +The bundle format must be deterministic apart from explicitly excluded runtime metadata. Timestamps, if present, must not affect semantic equality of result content. + +## 13. Fail-closed rules + +- malformed fixture manifest: reject run as `NOT_ESTABLISHED`; +- source/schema mismatch: reject run as `NOT_ESTABLISHED`; +- unexpected fixture execution exception: `IMPLEMENTATION_DEFECT` unless evidence establishes another class; +- missing required result field: reject evidence bundle; +- invalid result classification: reject evidence bundle; +- unknown fixture ID in output: reject evidence bundle; +- duplicate result identity: reject evidence bundle; +- missing critical fixture: aggregate `NOT_ESTABLISHED`; +- attempted free-form escape-hatch substitution: classify the semantic `AMBIGUOUS_SEMANTIC` or `MISSING_CORE_SEMANTIC`, never PASS. + +## 14. Testing strategy + +Tests establish apparatus behavior only, not GSAE-E0 feasibility. + +Required tests: + +- valid frozen fixture schema accepted; +- malformed/duplicate fixture definitions rejected where required; +- manifest hash deterministic; +- source-binding mismatch fails closed; +- every taxonomy class reachable through deterministic synthetic cases; +- structured-coverage rule rejects generic `detail`/opaque-artifact substitution; +- native fixtures reproduce already-established ACP contract behavior without changing ACP code; +- result ordering deterministic; +- aggregate disposition obeys criticality rules; +- retries create distinct identities and preserve prior evidence; +- evidence manifest rejects missing/mismatched identities. + +Implementation must use TDD: failing apparatus tests precede implementation for each behavior slice. + +## 15. Relationship to PR #5 + +PR #5 remains the exact source-under-test candidate. Stage A does not modify that PR's contract implementation. + +The harness branch is based on PR #5 exact head so it can import the candidate contract directly. Harness findings must be reviewed separately from the contract candidate. If Stage A later motivates schema changes, those changes belong in a subsequent explicit contract revision after the Stage-A evidence bundle is frozen. + +## 16. Governance relationship + +Notion remains the interpreted GSAE-E0 research/governance SSoT. GitHub remains authoritative for exact code, commit, issue, PR, CI, and evidence-artifact identities. + +Apparatus completion may advance only to `APPARATUS_READY`. It must not mark: + +- `PROTOCOL FROZEN` unless the owning GSAE record explicitly records the freeze; +- `EXECUTION AUTHORIZED` without a separate authorization event; +- `RUN` or scientific `N>0` before an actually authorized experiment; +- portability, efficacy, safety, or production-readiness claims. + +## 17. Completion criteria + +The subsystem is implementation-complete when: + +1. all native fixture families exist in the frozen manifest; +2. `AUTH-01` through `AUTH-10` are represented; +3. deterministic tests pass on the exact apparatus head; +4. source/schema/fixture binding is enforced fail-closed; +5. the evidence manifest is deterministic and validated; +6. negative controls demonstrate malformed evidence rejection; +7. no ACP core/schema file is modified by the harness implementation; +8. repository documentation states the evidence ceiling; +9. GitHub issue #6 records exact apparatus head, test command/result, fixture hash, and `APPARATUS_READY` or narrower status. + +These criteria establish measurement-apparatus readiness only. \ No newline at end of file From c26dad92168deb14b9d387ea489e133bc178c3c3 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:14:51 -0400 Subject: [PATCH 02/21] docs: plan GSAE-E0 Stage-A conformance harness --- ...-16-gsae-e0-stage-a-conformance-harness.md | 915 ++++++++++++++++++ 1 file changed, 915 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-16-gsae-e0-stage-a-conformance-harness.md diff --git a/docs/superpowers/plans/2026-09-16-gsae-e0-stage-a-conformance-harness.md b/docs/superpowers/plans/2026-09-16-gsae-e0-stage-a-conformance-harness.md new file mode 100644 index 0000000..876aca0 --- /dev/null +++ b/docs/superpowers/plans/2026-09-16-gsae-e0-stage-a-conformance-harness.md @@ -0,0 +1,915 @@ +# GSAE-E0 Stage-A Conformance Harness 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:** Build a deterministic, research-isolated Stage-A apparatus that can measure whether ACP `agent-control-plane.execution.v1` represents the frozen GSAE-E0 governance semantics without modifying the contract under test or authorizing the experiment. + +**Architecture:** Add an `experiments/gsae_e0/` package that contains immutable fixture definitions, typed validation, semantic-surface classification, native ACP conformance probes, deterministic evidence-manifest construction, and a fail-closed runner. The runner imports the existing ACP contract but ACP production modules never import the experiment package. Unit tests validate the apparatus with synthetic or already-established ACP behaviors; they must not execute the canonical frozen Stage-A manifest as an authorized experiment. + +**Tech Stack:** Python 3.10–3.14, standard library (`dataclasses`, `enum`, `hashlib`, `json`, `pathlib`, `subprocess`, `uuid`), pytest, existing `agent_control_plane` package. + +**Spec:** `docs/superpowers/specs/2026-09-16-gsae-e0-stage-a-conformance-harness-design.md` + +## Global Constraints + +- Source under test: ACP PR #5 exact head `07a09698ca66e8837d04e6ec05b4de3448eced04`. +- Candidate schema: `agent-control-plane.execution.v1`. +- Stage A must not modify any file under `src/agent_control_plane/`. +- No network/provider dependency is permitted. +- No Stage-B portability claim is permitted. +- Apparatus completion may establish only `APPARATUS_READY` or a narrower engineering status. +- GSAE-E0 remains `EXECUTION NOT AUTHORIZED / NOT RUN / N=0` until a separate authorization record exists. +- Free-form `detail`, opaque artifact payloads, overloaded status strings, and runtime-specific identifier conventions do not count as structured semantic coverage. +- Missing required evidence is `NOT_ESTABLISHED`; it is never imputed. +- Retries must preserve prior evidence and use distinct result identities. +- Existing repository verification remains `python -m pip install -e . pytest` followed by `python -m pytest` across Python 3.10–3.14. + +--- + +## File Structure + +Create or modify only these paths during apparatus implementation: + +- Modify `pyproject.toml` — include experiment tests in pytest discovery. +- Create `experiments/gsae_e0/__init__.py` — package marker and intentionally small public surface. +- Create `experiments/gsae_e0/schema.py` — enums, fixture/result dataclasses, fail-closed validation. +- Create `experiments/gsae_e0/fixtures.py` — load frozen JSON and compute deterministic fixture identity. +- Create `experiments/gsae_e0/classify.py` — structured contract surface and pure per-fixture/aggregate classification. +- Create `experiments/gsae_e0/runner.py` — source binding, native observations, canonical-run guard, result-matrix orchestration. +- Create `experiments/gsae_e0/manifest.py` — deterministic evidence bundle construction/validation. +- Create `experiments/gsae_e0/fixtures/stage_a_v1.json` — frozen candidate Stage-A fixture set. +- Create `experiments/gsae_e0/README.md` — evidence ceiling, commands, authorization boundary. +- Create `experiments/gsae_e0/tests/test_fixture_schema.py` — fixture parsing/validation/hash tests. +- Create `experiments/gsae_e0/tests/test_classification.py` — structured-coverage and aggregate-rule tests using synthetic surfaces. +- Create `experiments/gsae_e0/tests/test_runner.py` — source-binding, native-observation, and execution-guard tests. +- Create `experiments/gsae_e0/tests/test_manifest.py` — deterministic evidence-bundle and retry-preservation tests. + +Do **not** modify `src/agent_control_plane/contract/*`, `src/agent_control_plane/core.py`, `src/agent_control_plane/policy.py`, `src/agent_control_plane/provenance.py`, or `src/agent_control_plane/budget.py` in this plan. + +--- + +### Task 1: Add experiment discovery and typed fixture schema + +**Files:** +- Modify: `pyproject.toml` +- Create: `experiments/gsae_e0/__init__.py` +- Create: `experiments/gsae_e0/schema.py` +- Create: `experiments/gsae_e0/tests/test_fixture_schema.py` + +**Interfaces:** +- Produces: `Criticality`, `FixtureFamily`, `ExceptionClass`, `FixtureDisposition`, `AggregateDisposition`, `FixtureDefinition`, `FixtureManifest`, `FixtureResult`, `FixtureValidationError`. +- Later tasks consume `FixtureDefinition.from_dict()`, `FixtureManifest.from_dict()`, and the exact enum values defined here. + +- [ ] **Step 1: Write failing schema tests and enable pytest discovery** + +Change pytest discovery to: + +```toml +[tool.pytest.ini_options] +pythonpath = ["src"] +testpaths = ["tests", "experiments/gsae_e0/tests"] +``` + +Create tests that require duplicate IDs, blank IDs, unknown enum values, empty semantic requirements, and malformed `input_spec` to fail closed: + +```python +import pytest + +from experiments.gsae_e0.schema import FixtureManifest, FixtureValidationError + + +def valid_fixture(fixture_id="AUTH-01"): + return { + "fixture_id": fixture_id, + "family": "authority_semantic", + "title": "principal identity", + "purpose": "require structured principal identity", + "criticality": "critical", + "required_semantics": ["principal_identity"], + "input_spec": { + "required_path_groups": [["principal.principal_id"]], + "gap_class_if_absent": "MISSING_CORE_SEMANTIC", + }, + "expected_classification_domain": ["STRUCTURED_COVERAGE", "NOT_COVERED"], + } + + +def valid_manifest(): + return { + "experiment_id": "GSAE-E0", + "fixture_set_version": "stage-a-v1", + "source_under_test_sha": "07a09698ca66e8837d04e6ec05b4de3448eced04", + "schema_version": "agent-control-plane.execution.v1", + "fixtures": [valid_fixture()], + } + + +def test_valid_manifest_parses(): + manifest = FixtureManifest.from_dict(valid_manifest()) + assert manifest.experiment_id == "GSAE-E0" + assert manifest.fixtures[0].fixture_id == "AUTH-01" + + +def test_duplicate_fixture_ids_fail_closed(): + data = valid_manifest() + data["fixtures"] = [valid_fixture("AUTH-01"), valid_fixture("AUTH-01")] + with pytest.raises(FixtureValidationError, match="duplicate fixture_id"): + FixtureManifest.from_dict(data) + + +def test_blank_fixture_id_fails_closed(): + data = valid_manifest() + data["fixtures"] = [valid_fixture(" ")] + with pytest.raises(FixtureValidationError, match="fixture_id"): + FixtureManifest.from_dict(data) + + +def test_unknown_gap_class_fails_closed(): + data = valid_manifest() + data["fixtures"][0]["input_spec"]["gap_class_if_absent"] = "MYSTERY" + with pytest.raises(FixtureValidationError, match="gap_class_if_absent"): + FixtureManifest.from_dict(data) +``` + +- [ ] **Step 2: Run the targeted test and confirm RED** + +Run: + +```bash +python -m pytest experiments/gsae_e0/tests/test_fixture_schema.py -v +``` + +Expected: collection/import failure because `experiments.gsae_e0.schema` does not exist. + +- [ ] **Step 3: Implement the minimal typed schema** + +Use exact enum values: + +```python +class Criticality(str, Enum): + CRITICAL = "critical" + NONCRITICAL = "noncritical" + +class FixtureFamily(str, Enum): + NATIVE = "native_conformance" + NEGATIVE = "negative_control" + AUTHORITY = "authority_semantic" + +class ExceptionClass(str, Enum): + MISSING_CORE_SEMANTIC = "MISSING_CORE_SEMANTIC" + AMBIGUOUS_SEMANTIC = "AMBIGUOUS_SEMANTIC" + RUNTIME_SPECIFIC = "RUNTIME_SPECIFIC" + ADAPTER_COMPLEXITY = "ADAPTER_COMPLEXITY" + NONCRITICAL_EXTENSION = "NONCRITICAL_EXTENSION" + MALFORMED_INPUT = "MALFORMED_INPUT" + IMPLEMENTATION_DEFECT = "IMPLEMENTATION_DEFECT" + PROVENANCE_GAP = "PROVENANCE_GAP" + +class FixtureDisposition(str, Enum): + PASS = "PASS" + STRUCTURED_COVERAGE = "STRUCTURED_COVERAGE" + EXPECTED_REJECTION = "EXPECTED_REJECTION" + NOT_COVERED = "NOT_COVERED" + NOT_ESTABLISHED = "NOT_ESTABLISHED" + +class AggregateDisposition(str, Enum): + FEASIBLE = "FEASIBLE_FOR_FROZEN_SCOPE" + CONDITIONAL = "CONDITIONALLY_FEASIBLE_NARROW" + NOT_FEASIBLE = "NOT_FEASIBLE_FOR_FROZEN_SCOPE" + NOT_ESTABLISHED = "NOT_ESTABLISHED" +``` + +Implement frozen dataclasses. `FixtureDefinition.from_dict()` must require all documented fields, convert list fields to tuples, validate enum values, reject blank strings, and for `authority_semantic` fixtures require `input_spec.required_path_groups` to be a non-empty list of non-empty string lists plus a valid `gap_class_if_absent`. `FixtureManifest.from_dict()` must reject duplicate IDs and require non-empty `experiment_id`, `fixture_set_version`, `source_under_test_sha`, and `schema_version`. + +`FixtureResult` must contain: + +```python +@dataclass(frozen=True) +class FixtureResult: + result_id: str + run_id: str + attempt: int + fixture_id: str + disposition: FixtureDisposition + source_under_test_sha: str + schema_version: str + fixture_manifest_sha256: str + structured_fields_used: tuple[str, ...] = () + exception_class: ExceptionClass | None = None + evidence_summary: str = "" + error: str | None = None + prior_result_id: str | None = None +``` + +Reject `attempt < 1`, blank identities, `NOT_COVERED` without `exception_class`, and duplicate/blank structured field names. + +- [ ] **Step 4: Run the schema test and whole suite** + +```bash +python -m pytest experiments/gsae_e0/tests/test_fixture_schema.py -v +python -m pytest +``` + +Expected: targeted PASS; existing ACP tests remain PASS. + +- [ ] **Step 5: Commit** + +```bash +git add pyproject.toml experiments/gsae_e0/__init__.py experiments/gsae_e0/schema.py experiments/gsae_e0/tests/test_fixture_schema.py +git commit -m "test: add GSAE-E0 fixture schema" +``` + +--- + +### Task 2: Freeze the exact Stage-A fixture set and deterministic fixture identity + +**Files:** +- Create: `experiments/gsae_e0/fixtures.py` +- Create: `experiments/gsae_e0/fixtures/stage_a_v1.json` +- Modify: `experiments/gsae_e0/tests/test_fixture_schema.py` + +**Interfaces:** +- Produces: `load_fixture_manifest(path: Path) -> FixtureManifest`, `canonical_json_bytes(value: object) -> bytes`, `fixture_manifest_sha256(path: Path) -> str`. +- Later tasks consume the frozen manifest hash and exact fixture IDs. + +- [ ] **Step 1: Add failing loader/hash/frozen-set tests** + +```python +from pathlib import Path + +from experiments.gsae_e0.fixtures import fixture_manifest_sha256, load_fixture_manifest + +FIXTURE_PATH = Path("experiments/gsae_e0/fixtures/stage_a_v1.json") + + +def test_frozen_stage_a_fixture_ids_are_exact(): + manifest = load_fixture_manifest(FIXTURE_PATH) + assert {f.fixture_id for f in manifest.fixtures} == { + "NATIVE-01", "NATIVE-02", "NATIVE-03", "NATIVE-04", "NATIVE-05", + "NATIVE-06", "NATIVE-07", "NATIVE-08", "NATIVE-09", "NATIVE-10", + "NEG-01", "NEG-02", "NEG-03", "NEG-04", "NEG-05", "NEG-06", "NEG-07", "NEG-08", + "AUTH-01", "AUTH-02", "AUTH-03", "AUTH-04", "AUTH-05", + "AUTH-06", "AUTH-07", "AUTH-08", "AUTH-09", "AUTH-10", + } + + +def test_fixture_hash_is_64_lower_hex_and_stable(): + first = fixture_manifest_sha256(FIXTURE_PATH) + second = fixture_manifest_sha256(FIXTURE_PATH) + assert first == second + assert len(first) == 64 + assert first == first.lower() +``` + +- [ ] **Step 2: Run and confirm RED** + +```bash +python -m pytest experiments/gsae_e0/tests/test_fixture_schema.py -v +``` + +Expected: FAIL because loader and frozen JSON do not exist. + +- [ ] **Step 3: Implement canonical JSON and loader** + +```python +def canonical_json_bytes(value: object) -> bytes: + return (json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + "\n").encode("utf-8") + + +def load_fixture_manifest(path: Path) -> FixtureManifest: + data = json.loads(path.read_text(encoding="utf-8")) + return FixtureManifest.from_dict(data) + + +def fixture_manifest_sha256(path: Path) -> str: + data = json.loads(path.read_text(encoding="utf-8")) + FixtureManifest.from_dict(data) + return hashlib.sha256(canonical_json_bytes(data)).hexdigest() +``` + +- [ ] **Step 4: Create the frozen fixture file with these exact semantics** + +Header fields: + +```json +{ + "experiment_id": "GSAE-E0", + "fixture_set_version": "stage-a-v1", + "source_under_test_sha": "07a09698ca66e8837d04e6ec05b4de3448eced04", + "schema_version": "agent-control-plane.execution.v1", + "fixtures": [] +} +``` + +Populate `fixtures` with exactly the following definitions. All are `critical` unless explicitly stated otherwise. + +| ID | family | required semantics / test intent | classification domain | authority path rule / expected rejection | +|---|---|---|---|---| +| NATIVE-01 | native_conformance | allowed ordinary completion | PASS, NOT_ESTABLISHED | dispatch `echo`, require completed event | +| NATIVE-02 | native_conformance | policy denial evidence | PASS, NOT_ESTABLISHED | policy returns `blocked`, require `task.denied` event | +| NATIVE-03 | native_conformance | unknown capability fail-closed evidence | PASS, NOT_ESTABLISHED | require `task.rejected` then `KeyError` | +| NATIVE-04 | native_conformance | handler failure evidence | PASS, NOT_ESTABLISHED | handler raises `RuntimeError`, require FAILED state/event | +| NATIVE-05 | native_conformance | cancellation evidence | PASS, NOT_ESTABLISHED | cancel CREATED task, require CANCELLED event | +| NATIVE-06 | native_conformance | cooperative budget exhaustion | PASS, NOT_ESTABLISHED | max_steps=0 then consume step, require BUDGET_EXHAUSTED | +| NATIVE-07 | native_conformance | artifact linkage | PASS, NOT_ESTABLISHED | construct event with one input and one output `ArtifactRef` | +| NATIVE-08 | native_conformance | trace parent/child linkage | PASS, NOT_ESTABLISHED | construct `TraceContext` with distinct parent/span | +| NATIVE-09 | native_conformance | policy decision reference binding | PASS, NOT_ESTABLISHED | construct event with `policy_decision_ref="decision-1"` | +| NATIVE-10 | native_conformance | legacy provenance mapping | PASS, NOT_ESTABLISHED | map matching run IDs and preserve event/task/capability/state | +| NEG-01 | negative_control | provenance/contract run mismatch | EXPECTED_REJECTION, NOT_ESTABLISHED | ContractValidationError | +| NEG-02 | negative_control | unsupported schema version | EXPECTED_REJECTION, NOT_ESTABLISHED | ContractValidationError | +| NEG-03 | negative_control | blank required identity | EXPECTED_REJECTION, NOT_ESTABLISHED | ContractValidationError | +| NEG-04 | negative_control | span self-parenting | EXPECTED_REJECTION, NOT_ESTABLISHED | ContractValidationError | +| NEG-05 | negative_control | malformed SHA-256 | EXPECTED_REJECTION, NOT_ESTABLISHED | ContractValidationError | +| NEG-06 | negative_control | negative/non-integer monotonic value | EXPECTED_REJECTION, NOT_ESTABLISHED | ContractValidationError | +| NEG-07 | negative_control | non-UTC/naive timestamp | EXPECTED_REJECTION, NOT_ESTABLISHED | ContractValidationError | +| NEG-08 | negative_control | malformed nested contract type | EXPECTED_REJECTION, NOT_ESTABLISHED | ContractValidationError | +| AUTH-01 | authority_semantic | principal / acting identity | STRUCTURED_COVERAGE, NOT_COVERED | groups `[["principal.principal_id"]]`; gap `MISSING_CORE_SEMANTIC` | +| AUTH-02 | authority_semantic | requested capability | STRUCTURED_COVERAGE, NOT_COVERED | groups `[["capability"]]`; gap `MISSING_CORE_SEMANTIC` | +| AUTH-03 | authority_semantic | resource / target scope | STRUCTURED_COVERAGE, NOT_COVERED | groups `[["resource.resource_id"]]`; gap `MISSING_CORE_SEMANTIC` | +| AUTH-04 | authority_semantic | operation / action semantics distinct from generic capability | STRUCTURED_COVERAGE, NOT_COVERED | groups `[["operation.action"]]`; gap `AMBIGUOUS_SEMANTIC` | +| AUTH-05 | authority_semantic | policy identity plus version/hash | STRUCTURED_COVERAGE, NOT_COVERED | groups `[["policy.policy_id"], ["policy.version_or_hash"]]`; gap `MISSING_CORE_SEMANTIC` | +| AUTH-06 | authority_semantic | decision identity plus explicit allow/deny/conditional outcome | STRUCTURED_COVERAGE, NOT_COVERED | groups `[["policy_decision_ref"], ["decision.outcome"]]`; gap `MISSING_CORE_SEMANTIC` | +| AUTH-07 | authority_semantic | lease / authority expiry | STRUCTURED_COVERAGE, NOT_COVERED | groups `[["authority.expires_at"]]`; gap `MISSING_CORE_SEMANTIC` | +| AUTH-08 | authority_semantic | delegation chain / delegator scope | STRUCTURED_COVERAGE, NOT_COVERED | groups `[["delegation.delegator_id"], ["delegation.scope"]]`; gap `MISSING_CORE_SEMANTIC` | +| AUTH-09 | authority_semantic | permitted/forbidden actions or authority conditions | STRUCTURED_COVERAGE, NOT_COVERED | groups `[["authority.conditions"]]`; gap `MISSING_CORE_SEMANTIC` | +| AUTH-10 | authority_semantic | reason code plus decision-to-execution provenance link | STRUCTURED_COVERAGE, NOT_COVERED | groups `[["policy_decision_ref"], ["decision.reason_code"]]`; gap `MISSING_CORE_SEMANTIC` | + +For each JSON object also supply a concrete `title`, one-sentence `purpose`, `required_semantics` containing the named semantic/test intent, and an `input_spec` object with the exact operation parameters above. Do not add alternate authority paths that rely on `detail`, artifact payload contents, or identifier conventions. + +- [ ] **Step 5: Run targeted and full tests** + +```bash +python -m pytest experiments/gsae_e0/tests/test_fixture_schema.py -v +python -m pytest +``` + +Expected: PASS. + +- [ ] **Step 6: Commit** + +```bash +git add experiments/gsae_e0/fixtures.py experiments/gsae_e0/fixtures/stage_a_v1.json experiments/gsae_e0/tests/test_fixture_schema.py +git commit -m "feat: freeze GSAE-E0 Stage-A fixtures" +``` + +--- + +### Task 3: Implement pure structured-coverage classification and aggregate rules + +**Files:** +- Create: `experiments/gsae_e0/classify.py` +- Create: `experiments/gsae_e0/tests/test_classification.py` + +**Interfaces:** +- Produces: `execution_v1_surface_paths() -> frozenset[str]`, `classify_authority_fixture(...) -> FixtureResult`, `aggregate_disposition(fixtures, results) -> AggregateDisposition`. +- Does not execute the frozen manifest in tests. + +- [ ] **Step 1: Write failing tests using synthetic surfaces** + +```python +from experiments.gsae_e0.classify import aggregate_disposition, classify_authority_fixture +from experiments.gsae_e0.schema import AggregateDisposition, ExceptionClass, FixtureDisposition + + +def test_detail_escape_hatch_does_not_count_as_structured_coverage(auth_fixture, result_context): + result = classify_authority_fixture(auth_fixture, frozenset({"detail"}), **result_context) + assert result.disposition is FixtureDisposition.NOT_COVERED + assert result.exception_class is ExceptionClass.MISSING_CORE_SEMANTIC + + +def test_all_required_path_groups_must_be_satisfied(auth_fixture_two_groups, result_context): + result = classify_authority_fixture( + auth_fixture_two_groups, + frozenset({"policy_decision_ref"}), + **result_context, + ) + assert result.disposition is FixtureDisposition.NOT_COVERED + + +def test_critical_missing_semantic_forces_not_feasible(critical_fixture, missing_result): + assert aggregate_disposition((critical_fixture,), (missing_result,)) is AggregateDisposition.NOT_FEASIBLE + + +def test_missing_result_for_required_fixture_forces_not_established(critical_fixture): + assert aggregate_disposition((critical_fixture,), ()) is AggregateDisposition.NOT_ESTABLISHED +``` + +Also test that `PROVENANCE_GAP`/`NOT_ESTABLISHED` results force aggregate `NOT_ESTABLISHED`, and a noncritical `NOT_COVERED` result with all critical fixtures satisfied yields `CONDITIONALLY_FEASIBLE_NARROW`. + +- [ ] **Step 2: Run and confirm RED** + +```bash +python -m pytest experiments/gsae_e0/tests/test_classification.py -v +``` + +Expected: import failure for `classify`. + +- [ ] **Step 3: Implement contract-surface extraction without semantic invention** + +Use `dataclasses.fields()` to enumerate only actual typed fields from `ExecutionEvent`, `ExecutionIdentity`, `TraceContext`, `ComponentIdentity`, and `ArtifactRef`. + +The surface must include paths such as: + +```python +{ + "event_type", "task_id", "status", "utc_timestamp", "monotonic_ns", + "capability", "policy_decision_ref", "detail", + "identity.execution_id", "identity.run_id", "identity.schema_version", + "trace.trace_id", "trace.span_id", "trace.parent_span_id", + "component.component_id", "component.component_type", "component.runtime_id", + "component.adapter_id", "component.version", "component.source_ref", + "input_artifacts[].artifact_id", "input_artifacts[].kind", "input_artifacts[].uri", + "input_artifacts[].version", "input_artifacts[].sha256", + "output_artifacts[].artifact_id", "output_artifacts[].kind", "output_artifacts[].uri", + "output_artifacts[].version", "output_artifacts[].sha256", +} +``` + +Do not add conceptual aliases such as `operation.action` unless they are real typed fields. + +- [ ] **Step 4: Implement pure authority classification** + +For each `required_path_groups` group, at least one listed path must exist in the supplied surface. All groups must pass. If they do, return `STRUCTURED_COVERAGE` with the exact matched paths. If any group fails, return `NOT_COVERED` with the fixture's declared `gap_class_if_absent`. + +Explicitly reject `detail`, `input_artifacts[].uri`, `output_artifacts[].uri`, and arbitrary identifier-string conventions as substitutes unless the fixture itself requires those exact technical fields for a non-authority-native test. + +- [ ] **Step 5: Implement aggregate rules** + +Order of precedence: + +```python +# 1. Missing fixture result, invalid/unknown fixture result, or any required result +# with NOT_ESTABLISHED / PROVENANCE_GAP / IMPLEMENTATION_DEFECT -> NOT_ESTABLISHED. +# 2. Critical NOT_COVERED due to MISSING_CORE_SEMANTIC, AMBIGUOUS_SEMANTIC, +# RUNTIME_SPECIFIC, or ADAPTER_COMPLEXITY -> NOT_FEASIBLE_FOR_FROZEN_SCOPE. +# 3. All critical fixtures satisfied but >=1 noncritical NOT_COVERED -> +# CONDITIONALLY_FEASIBLE_NARROW. +# 4. Otherwise -> FEASIBLE_FOR_FROZEN_SCOPE. +``` + +- [ ] **Step 6: Run tests and commit** + +```bash +python -m pytest experiments/gsae_e0/tests/test_classification.py -v +python -m pytest +git add experiments/gsae_e0/classify.py experiments/gsae_e0/tests/test_classification.py +git commit -m "feat: add GSAE-E0 semantic classification" +``` + +--- + +### Task 4: Implement source binding and native ACP observation helpers + +**Files:** +- Create: `experiments/gsae_e0/runner.py` +- Create: `experiments/gsae_e0/tests/test_runner.py` + +**Interfaces:** +- Produces: `verify_source_binding(repo_root: Path, expected_sha: str) -> None`, `observe_native_fixture(fixture_id: str) -> tuple[FixtureDisposition, str]`. +- `verify_source_binding` verifies exact source ancestry and zero diff under `src/agent_control_plane/` relative to the source-under-test commit. + +- [ ] **Step 1: Write failing source-binding tests** + +```python +from pathlib import Path +import pytest + +from experiments.gsae_e0.runner import SourceBindingError, verify_source_binding + +SOURCE_SHA = "07a09698ca66e8837d04e6ec05b4de3448eced04" + + +def test_bound_source_is_ancestor_and_acp_package_is_unchanged(): + verify_source_binding(Path.cwd(), SOURCE_SHA) + + +def test_unknown_source_sha_fails_closed(): + with pytest.raises(SourceBindingError): + verify_source_binding(Path.cwd(), "0" * 40) +``` + +Implementation must execute both: + +```bash +git merge-base --is-ancestor HEAD +git diff --quiet -- src/agent_control_plane +``` + +Any nonzero exit status raises `SourceBindingError`. + +- [ ] **Step 2: Add failing native observation tests** + +Test only already-established ACP behaviors, one helper at a time; do not load/run the canonical Stage-A manifest: + +```python +def test_native_allowed_completion_observation_passes(): + disposition, summary = observe_native_fixture("NATIVE-01") + assert disposition is FixtureDisposition.PASS + assert "task.completed" in summary + + +def test_unknown_capability_observation_requires_rejection_evidence(): + disposition, summary = observe_native_fixture("NATIVE-03") + assert disposition is FixtureDisposition.PASS + assert "task.rejected" in summary + + +def test_negative_run_mismatch_is_expected_rejection(): + disposition, summary = observe_native_fixture("NEG-01") + assert disposition is FixtureDisposition.EXPECTED_REJECTION + assert "run_id" in summary +``` + +Add equivalent tests for NATIVE-02 through NATIVE-10 and NEG-02 through NEG-08, but each test must invoke only one fixture helper and assert its exact expected evidence condition. + +- [ ] **Step 3: Run and confirm RED** + +```bash +python -m pytest experiments/gsae_e0/tests/test_runner.py -v +``` + +- [ ] **Step 4: Implement source binding and native observations** + +Use existing public APIs only: + +```python +from agent_control_plane import ControlPlane, ExecutionBudget, Task, TaskState +from agent_control_plane.contract import ( + ArtifactRef, ComponentIdentity, ContractValidationError, ExecutionEvent, + ExecutionIdentity, TraceContext, map_provenance_event, +) +``` + +`observe_native_fixture()` must be an explicit dispatch table from known fixture ID to a focused helper. Unknown IDs raise `ValueError`; they must never silently pass. + +Examples: + +```python +def _native_01(): + plane = ControlPlane(run_id="gsae-e0-native-01") + plane.register("echo", lambda task: task.payload) + task = plane.dispatch("echo", Task(payload="hello", id="task-native-01")) + if task.state is not TaskState.COMPLETED: + return FixtureDisposition.NOT_ESTABLISHED, f"unexpected state={task.state.value}" + if not any(event.event == "task.completed" for event in plane.events): + return FixtureDisposition.NOT_ESTABLISHED, "missing task.completed evidence" + return FixtureDisposition.PASS, "task.completed recorded" +``` + +For policy denial, assert the `task.denied` event rather than inventing a terminal DENIED task state. For negative controls, only return `EXPECTED_REJECTION` when the exact expected `ContractValidationError` occurs. + +- [ ] **Step 5: Run tests and commit** + +```bash +python -m pytest experiments/gsae_e0/tests/test_runner.py -v +python -m pytest +git add experiments/gsae_e0/runner.py experiments/gsae_e0/tests/test_runner.py +git commit -m "feat: add GSAE-E0 native conformance observations" +``` + +--- + +### Task 5: Add deterministic evidence-manifest construction and validation + +**Files:** +- Create: `experiments/gsae_e0/manifest.py` +- Create: `experiments/gsae_e0/tests/test_manifest.py` + +**Interfaces:** +- Produces: `EvidenceBundle`, `build_evidence_bundle(...)`, `validate_evidence_bundle(...)`, `canonical_bundle_sha256(...)`, `result_id(run_id, fixture_id, attempt) -> str`. + +- [ ] **Step 1: Write failing determinism and retry tests** + +```python +from experiments.gsae_e0.manifest import canonical_bundle_sha256, result_id + + +def test_result_id_is_deterministic_per_attempt_and_changes_on_retry(): + first = result_id("run-1", "AUTH-01", 1) + same = result_id("run-1", "AUTH-01", 1) + retry = result_id("run-1", "AUTH-01", 2) + assert first == same + assert first != retry + + +def test_semantically_identical_bundle_hash_is_stable(bundle): + assert canonical_bundle_sha256(bundle) == canonical_bundle_sha256(bundle) + + +def test_missing_result_identity_is_rejected(bundle_without_result_id): + with pytest.raises(EvidenceValidationError, match="result_id"): + validate_evidence_bundle(bundle_without_result_id) +``` + +Also test duplicate result IDs, unknown fixture IDs, mismatched source SHA, mismatched schema version, mismatched fixture hash, and a retry whose `prior_result_id` does not point to the prior attempt. + +- [ ] **Step 2: Run and confirm RED** + +```bash +python -m pytest experiments/gsae_e0/tests/test_manifest.py -v +``` + +- [ ] **Step 3: Implement deterministic identities and evidence bundle** + +Use UUIDv5 for result IDs so IDs are deterministic from the immutable tuple while retries remain distinct: + +```python +_RESULT_NAMESPACE = UUID("e063a317-67cb-5d84-a411-7027376c68a0") + + +def result_id(run_id: str, fixture_id: str, attempt: int) -> str: + if attempt < 1: + raise EvidenceValidationError("attempt must be >= 1") + return str(uuid5(_RESULT_NAMESPACE, f"{run_id}|{fixture_id}|{attempt}")) +``` + +`EvidenceBundle` must contain at least: + +```python +experiment_id +protocol_version +run_id +authorization_record_id +source_under_test_sha +harness_commit_sha +schema_version +fixture_set_version +fixture_manifest_sha256 +results +aggregate_disposition +test_command +test_summary +evidence_ceiling +``` + +Canonical serialization uses sorted keys and compact separators. Do not include wall-clock timestamps in the hash-bearing semantic body; if later retained as metadata, keep them in a separately excluded provenance envelope. + +- [ ] **Step 4: Run tests and commit** + +```bash +python -m pytest experiments/gsae_e0/tests/test_manifest.py -v +python -m pytest +git add experiments/gsae_e0/manifest.py experiments/gsae_e0/tests/test_manifest.py +git commit -m "feat: add deterministic GSAE-E0 evidence bundles" +``` + +--- + +### Task 6: Add the fail-closed canonical Stage-A runner without executing it + +**Files:** +- Modify: `experiments/gsae_e0/runner.py` +- Modify: `experiments/gsae_e0/tests/test_runner.py` + +**Interfaces:** +- Produces: `run_stage_a(...) -> EvidenceBundle`. +- This function is apparatus only. CI tests its guards using temporary/synthetic manifests; tests must not invoke it with the frozen `stage_a_v1.json` plus a real authorization record. + +- [ ] **Step 1: Write failing guard tests** + +Exact function signature: + +```python +def run_stage_a( + *, + repo_root: Path, + fixture_path: Path, + protocol_version: str, + authorization_record_id: str, + run_id: str, + harness_commit_sha: str, + test_command: str, + test_summary: str, +) -> EvidenceBundle: + ... +``` + +Tests: + +```python +def test_run_rejects_blank_authorization_record_id(synthetic_manifest_path): + with pytest.raises(ExecutionGuardError, match="authorization_record_id"): + run_stage_a( + repo_root=Path.cwd(), + fixture_path=synthetic_manifest_path, + protocol_version="GSAE-E0-R2-v0.1", + authorization_record_id="", + run_id="synthetic-run", + harness_commit_sha="a" * 40, + test_command="python -m pytest", + test_summary="synthetic apparatus test", + ) + + +def test_run_rejects_wrong_experiment_id(synthetic_wrong_experiment_manifest_path): + with pytest.raises(ExecutionGuardError, match="GSAE-E0"): + ... +``` + +Also test blank protocol/run/harness identities, schema mismatch, and source-binding failure. These are guard tests, not experiment execution. + +- [ ] **Step 2: Run and confirm RED** + +```bash +python -m pytest experiments/gsae_e0/tests/test_runner.py -v +``` + +- [ ] **Step 3: Implement orchestration** + +`run_stage_a()` must perform this exact order: + +1. reject blank execution/provenance identities; +2. load and validate fixture manifest; +3. require `experiment_id == "GSAE-E0"`; +4. require `schema_version == SCHEMA_VERSION`; +5. verify exact source binding before fixture evaluation; +6. compute frozen fixture-manifest SHA-256; +7. evaluate native/negative fixtures through `observe_native_fixture()`; +8. evaluate authority fixtures using `execution_v1_surface_paths()` and `classify_authority_fixture()`; +9. require exactly one result per frozen fixture ID; +10. derive aggregate disposition; +11. build and validate the evidence bundle; +12. return the bundle without writing files or mutating ACP state. + +The runner itself must not decide whether the supplied authorization record is politically/organizationally valid; it records the explicit identifier and fails on absence. The owning GSAE control record remains the authorization authority. + +- [ ] **Step 4: Add an explicit test preventing accidental canonical execution in CI** + +```python +def test_ci_suite_does_not_call_canonical_stage_a_runner(monkeypatch): + called = False + + def forbidden(*args, **kwargs): + nonlocal called + called = True + raise AssertionError("canonical Stage-A execution is not a unit test") + + monkeypatch.setattr("experiments.gsae_e0.runner._execute_frozen_manifest", forbidden, raising=False) + assert called is False +``` + +More importantly, search the experiment tests and ensure no test passes `experiments/gsae_e0/fixtures/stage_a_v1.json` to `run_stage_a()`. + +- [ ] **Step 5: Run tests and commit** + +```bash +python -m pytest experiments/gsae_e0/tests/test_runner.py -v +python -m pytest +git add experiments/gsae_e0/runner.py experiments/gsae_e0/tests/test_runner.py +git commit -m "feat: add guarded GSAE-E0 Stage-A runner" +``` + +--- + +### Task 7: Document apparatus use and evidence ceiling + +**Files:** +- Create: `experiments/gsae_e0/README.md` + +**Interfaces:** +- Documentation only; no new runtime behavior. + +- [ ] **Step 1: Write the README with exact current state** + +It must state: + +```markdown +# GSAE-E0 Stage-A Conformance Harness + +Status: RESEARCH APPARATUS / EXECUTION NOT AUTHORIZED / NOT RUN / N=0 + +Source under test: `07a09698ca66e8837d04e6ec05b4de3448eced04` +Schema: `agent-control-plane.execution.v1` +Frozen fixture candidate: `fixtures/stage_a_v1.json` + +This package measures contract feasibility only. Passing apparatus tests do not establish +GSAE-E0 feasibility, portability, governance efficacy, safety, security, production +readiness, or authorization. +``` + +Document: +- setup: `python -m pip install -e . pytest`; +- apparatus verification: `python -m pytest experiments/gsae_e0/tests -v`; +- full repository verification: `python -m pytest`; +- the source-binding rule; +- the authorization boundary; +- the four possible aggregate dispositions; +- the rule that canonical Stage-A execution occurs only after an explicit owning-control-record authorization event; +- the prohibition on modifying the contract before freezing Stage-A evidence if the experiment finds missing semantics. + +Do not include a copy-paste command that executes the frozen canonical experiment while it remains unauthorized. + +- [ ] **Step 2: Verify documentation does not overclaim** + +Run: + +```bash +grep -R "AUTHORIZED\|FEASIBLE\|portable\|production" experiments/gsae_e0/README.md +``` + +Manually confirm every occurrence is bounded or negated appropriately. + +- [ ] **Step 3: Commit** + +```bash +git add experiments/gsae_e0/README.md +git commit -m "docs: bound GSAE-E0 apparatus evidence ceiling" +``` + +--- + +### Task 8: Final apparatus verification and repository handoff + +**Files:** +- No code files should need changes unless verification finds a defect. +- Update GitHub issue #6 only after exact-head verification succeeds. + +**Interfaces:** +- Produces repository-level `APPARATUS_READY` evidence only if all checks pass. + +- [ ] **Step 1: Verify ACP source-under-test files were not modified** + +```bash +git diff --exit-code 07a09698ca66e8837d04e6ec05b4de3448eced04 -- src/agent_control_plane +``` + +Expected: no output, exit 0. + +- [ ] **Step 2: Run experiment-apparatus tests** + +```bash +python -m pip install -e . pytest +python -m pytest experiments/gsae_e0/tests -v +``` + +Expected: all apparatus tests PASS. + +- [ ] **Step 3: Run full repository suite** + +```bash +python -m pytest +``` + +Expected: all ACP + contract + apparatus tests PASS. + +- [ ] **Step 4: Compute exact fixture identity and record exact apparatus head** + +```bash +python - <<'PY' +from pathlib import Path +from experiments.gsae_e0.fixtures import fixture_manifest_sha256 +print(fixture_manifest_sha256(Path("experiments/gsae_e0/fixtures/stage_a_v1.json"))) +PY +git rev-parse HEAD +``` + +Record both exact values without abbreviation. + +- [ ] **Step 5: Confirm no canonical E0 evidence bundle was produced** + +```bash +find experiments/gsae_e0 -type f \( -name '*result*.json' -o -name '*evidence*.json' -o -name '*run*.json' \) -print +``` + +Expected: no canonical run/result artifact. Source JSON fixtures are permitted. + +- [ ] **Step 6: Update GitHub issue #6** + +Record: +- exact apparatus head; +- fixture-manifest SHA-256; +- exact commands and test counts; +- confirmation that `src/agent_control_plane/` is unchanged relative to the source-under-test commit; +- status `APPARATUS_READY` if and only if all verification above passes; +- explicit `GSAE-E0 EXECUTION NOT AUTHORIZED / NOT RUN / N=0`; +- next gate: owning GSAE protocol-freeze acceptance, then separate execution authorization. + +- [ ] **Step 7: Open a review PR from `research/gsae-e0-stage-a-harness`** + +PR title: + +```text +research: add GSAE-E0 Stage-A conformance apparatus +``` + +PR body must separate: +- implementation changes; +- test/verification evidence; +- source-under-test binding; +- evidence ceiling; +- known semantic findings, if any are discovered during apparatus construction, labeled as non-canonical until authorized execution; +- unchanged scientific/governance state. + +- [ ] **Step 8: Verify PR CI on the exact head** + +Require all Python 3.10, 3.11, 3.12, 3.13, and 3.14 jobs to succeed. Do not call apparatus readiness verified until exact-head CI is green. + +--- + +## Plan Self-Review Result + +- **Spec coverage:** all design sections are mapped: isolated architecture, exact source binding, fixture taxonomy, AUTH-01–AUTH-10, structured-coverage rule, exception taxonomy, deterministic results, aggregate dispositions, runner behavior, evidence bundle, fail-closed rules, testing, PR #5 relationship, governance boundary, and completion criteria. +- **No canonical-run leakage:** unit/CI work validates the apparatus and already-established ACP behaviors only. The canonical frozen Stage-A manifest is not passed to `run_stage_a()` in tests. +- **Type consistency:** enum strings, fixture/result fields, runner inputs, and evidence-bundle identities are consistent across tasks. +- **No ACP contract mutation:** final verification explicitly proves `src/agent_control_plane/` is unchanged from the source-under-test commit. +- **No placeholders:** all required files, commands, identities, fixture IDs, semantic path rules, and status transitions are explicit. From 52ea1aa8b1a64653c33b78a81099ca646dbc979d Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:16:46 -0400 Subject: [PATCH 03/21] test: include GSAE-E0 experiment tests --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a615221..58ffad4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,4 +10,4 @@ requires-python = ">=3.10" [tool.pytest.ini_options] pythonpath = ["src"] -testpaths = ["tests"] +testpaths = ["tests", "experiments/gsae_e0/tests"] From 0dd2b1c9bf6fc180e5c2ad343d3b182465f65502 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:16:57 -0400 Subject: [PATCH 04/21] test: define GSAE-E0 fixture schema expectations --- .../gsae_e0/tests/test_fixture_schema.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 experiments/gsae_e0/tests/test_fixture_schema.py diff --git a/experiments/gsae_e0/tests/test_fixture_schema.py b/experiments/gsae_e0/tests/test_fixture_schema.py new file mode 100644 index 0000000..a7c2b2d --- /dev/null +++ b/experiments/gsae_e0/tests/test_fixture_schema.py @@ -0,0 +1,70 @@ +import pytest + +from experiments.gsae_e0.schema import FixtureManifest, FixtureValidationError + + +def valid_fixture(fixture_id="AUTH-01"): + return { + "fixture_id": fixture_id, + "family": "authority_semantic", + "title": "principal identity", + "purpose": "require structured principal identity", + "criticality": "critical", + "required_semantics": ["principal_identity"], + "input_spec": { + "required_path_groups": [["principal.principal_id"]], + "gap_class_if_absent": "MISSING_CORE_SEMANTIC", + }, + "expected_classification_domain": ["STRUCTURED_COVERAGE", "NOT_COVERED"], + } + + +def valid_manifest(): + return { + "experiment_id": "GSAE-E0", + "fixture_set_version": "stage-a-v1", + "source_under_test_sha": "07a09698ca66e8837d04e6ec05b4de3448eced04", + "schema_version": "agent-control-plane.execution.v1", + "fixtures": [valid_fixture()], + } + + +def test_valid_manifest_parses(): + manifest = FixtureManifest.from_dict(valid_manifest()) + assert manifest.experiment_id == "GSAE-E0" + assert manifest.fixtures[0].fixture_id == "AUTH-01" + + +def test_duplicate_fixture_ids_fail_closed(): + data = valid_manifest() + data["fixtures"] = [valid_fixture("AUTH-01"), valid_fixture("AUTH-01")] + with pytest.raises(FixtureValidationError, match="duplicate fixture_id"): + FixtureManifest.from_dict(data) + + +def test_blank_fixture_id_fails_closed(): + data = valid_manifest() + data["fixtures"] = [valid_fixture(" ")] + with pytest.raises(FixtureValidationError, match="fixture_id"): + FixtureManifest.from_dict(data) + + +def test_unknown_gap_class_fails_closed(): + data = valid_manifest() + data["fixtures"][0]["input_spec"]["gap_class_if_absent"] = "MYSTERY" + with pytest.raises(FixtureValidationError, match="gap_class_if_absent"): + FixtureManifest.from_dict(data) + + +def test_empty_required_semantics_fail_closed(): + data = valid_manifest() + data["fixtures"][0]["required_semantics"] = [] + with pytest.raises(FixtureValidationError, match="required_semantics"): + FixtureManifest.from_dict(data) + + +def test_malformed_required_path_groups_fail_closed(): + data = valid_manifest() + data["fixtures"][0]["input_spec"]["required_path_groups"] = [[]] + with pytest.raises(FixtureValidationError, match="required_path_groups"): + FixtureManifest.from_dict(data) From c472b5c58d62f3a9d59164dab34cb96f2a9c3b7f Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:17:37 -0400 Subject: [PATCH 05/21] feat: initialize GSAE-E0 research package --- experiments/gsae_e0/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 experiments/gsae_e0/__init__.py diff --git a/experiments/gsae_e0/__init__.py b/experiments/gsae_e0/__init__.py new file mode 100644 index 0000000..5d2956a --- /dev/null +++ b/experiments/gsae_e0/__init__.py @@ -0,0 +1,4 @@ +"""Research-scoped GSAE-E0 conformance apparatus. + +This package is not part of the ACP production API and does not authorize experiment execution. +""" From a23d1e8256bac71a51689ea90fa2565f2dec2cab Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:18:01 -0400 Subject: [PATCH 06/21] feat: add GSAE-E0 fixture schema --- experiments/gsae_e0/schema.py | 222 ++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 experiments/gsae_e0/schema.py diff --git a/experiments/gsae_e0/schema.py b/experiments/gsae_e0/schema.py new file mode 100644 index 0000000..45b5335 --- /dev/null +++ b/experiments/gsae_e0/schema.py @@ -0,0 +1,222 @@ +"""Typed schema and fail-closed validation for GSAE-E0 Stage-A evidence.""" + +from dataclasses import dataclass +from enum import Enum +import re +from typing import Any, Mapping + + +class FixtureValidationError(ValueError): + """Raised when fixture or result evidence is malformed.""" + + +class Criticality(str, Enum): + CRITICAL = "critical" + NONCRITICAL = "noncritical" + + +class FixtureFamily(str, Enum): + NATIVE = "native_conformance" + NEGATIVE = "negative_control" + AUTHORITY = "authority_semantic" + + +class ExceptionClass(str, Enum): + MISSING_CORE_SEMANTIC = "MISSING_CORE_SEMANTIC" + AMBIGUOUS_SEMANTIC = "AMBIGUOUS_SEMANTIC" + RUNTIME_SPECIFIC = "RUNTIME_SPECIFIC" + ADAPTER_COMPLEXITY = "ADAPTER_COMPLEXITY" + NONCRITICAL_EXTENSION = "NONCRITICAL_EXTENSION" + MALFORMED_INPUT = "MALFORMED_INPUT" + IMPLEMENTATION_DEFECT = "IMPLEMENTATION_DEFECT" + PROVENANCE_GAP = "PROVENANCE_GAP" + + +class FixtureDisposition(str, Enum): + PASS = "PASS" + STRUCTURED_COVERAGE = "STRUCTURED_COVERAGE" + EXPECTED_REJECTION = "EXPECTED_REJECTION" + NOT_COVERED = "NOT_COVERED" + NOT_ESTABLISHED = "NOT_ESTABLISHED" + + +class AggregateDisposition(str, Enum): + FEASIBLE = "FEASIBLE_FOR_FROZEN_SCOPE" + CONDITIONAL = "CONDITIONALLY_FEASIBLE_NARROW" + NOT_FEASIBLE = "NOT_FEASIBLE_FOR_FROZEN_SCOPE" + NOT_ESTABLISHED = "NOT_ESTABLISHED" + + +def _required_text(value: Any, field_name: str) -> str: + if not isinstance(value, str) or not value.strip(): + raise FixtureValidationError(f"{field_name} must be a non-blank string") + return value.strip() + + +def _enum_value(enum_type: type[Enum], value: Any, field_name: str): + try: + return enum_type(value) + except (TypeError, ValueError) as exc: + raise FixtureValidationError(f"invalid {field_name}: {value!r}") from exc + + +def _string_tuple(value: Any, field_name: str) -> tuple[str, ...]: + if not isinstance(value, (list, tuple)) or not value: + raise FixtureValidationError(f"{field_name} must be a non-empty sequence") + items = tuple(_required_text(item, field_name) for item in value) + if len(set(items)) != len(items): + raise FixtureValidationError(f"{field_name} must not contain duplicates") + return items + + +@dataclass(frozen=True) +class FixtureDefinition: + fixture_id: str + family: FixtureFamily + title: str + purpose: str + criticality: Criticality + required_semantics: tuple[str, ...] + input_spec: Mapping[str, Any] + expected_classification_domain: tuple[FixtureDisposition, ...] + expected_exception_class: ExceptionClass | None = None + + @classmethod + def from_dict(cls, data: Mapping[str, Any]) -> "FixtureDefinition": + if not isinstance(data, Mapping): + raise FixtureValidationError("fixture must be a mapping") + + fixture_id = _required_text(data.get("fixture_id"), "fixture_id") + family = _enum_value(FixtureFamily, data.get("family"), "family") + title = _required_text(data.get("title"), "title") + purpose = _required_text(data.get("purpose"), "purpose") + criticality = _enum_value(Criticality, data.get("criticality"), "criticality") + required_semantics = _string_tuple(data.get("required_semantics"), "required_semantics") + + input_spec_raw = data.get("input_spec") + if not isinstance(input_spec_raw, Mapping): + raise FixtureValidationError("input_spec must be a mapping") + input_spec = dict(input_spec_raw) + + expected_raw = data.get("expected_classification_domain") + if not isinstance(expected_raw, (list, tuple)) or not expected_raw: + raise FixtureValidationError("expected_classification_domain must be a non-empty sequence") + expected = tuple( + _enum_value(FixtureDisposition, value, "expected_classification_domain") + for value in expected_raw + ) + if len(set(expected)) != len(expected): + raise FixtureValidationError("expected_classification_domain must not contain duplicates") + + exception_raw = data.get("expected_exception_class") + expected_exception_class = None + if exception_raw is not None: + expected_exception_class = _enum_value( + ExceptionClass, + exception_raw, + "expected_exception_class", + ) + + if family is FixtureFamily.AUTHORITY: + groups = input_spec.get("required_path_groups") + if not isinstance(groups, (list, tuple)) or not groups: + raise FixtureValidationError("required_path_groups must be a non-empty sequence") + normalized_groups: list[tuple[str, ...]] = [] + for group in groups: + if not isinstance(group, (list, tuple)) or not group: + raise FixtureValidationError("required_path_groups must contain non-empty path groups") + normalized_groups.append(_string_tuple(group, "required_path_groups")) + input_spec["required_path_groups"] = tuple(normalized_groups) + gap_value = input_spec.get("gap_class_if_absent") + gap_class = _enum_value(ExceptionClass, gap_value, "gap_class_if_absent") + input_spec["gap_class_if_absent"] = gap_class.value + + return cls( + fixture_id=fixture_id, + family=family, + title=title, + purpose=purpose, + criticality=criticality, + required_semantics=required_semantics, + input_spec=input_spec, + expected_classification_domain=expected, + expected_exception_class=expected_exception_class, + ) + + +@dataclass(frozen=True) +class FixtureManifest: + experiment_id: str + fixture_set_version: str + source_under_test_sha: str + schema_version: str + fixtures: tuple[FixtureDefinition, ...] + + @classmethod + def from_dict(cls, data: Mapping[str, Any]) -> "FixtureManifest": + if not isinstance(data, Mapping): + raise FixtureValidationError("manifest must be a mapping") + + experiment_id = _required_text(data.get("experiment_id"), "experiment_id") + fixture_set_version = _required_text(data.get("fixture_set_version"), "fixture_set_version") + source_sha = _required_text(data.get("source_under_test_sha"), "source_under_test_sha") + if re.fullmatch(r"[0-9a-f]{40}", source_sha) is None: + raise FixtureValidationError("source_under_test_sha must be 40 lowercase hexadecimal characters") + schema_version = _required_text(data.get("schema_version"), "schema_version") + + fixtures_raw = data.get("fixtures") + if not isinstance(fixtures_raw, (list, tuple)) or not fixtures_raw: + raise FixtureValidationError("fixtures must be a non-empty sequence") + fixtures = tuple(FixtureDefinition.from_dict(item) for item in fixtures_raw) + ids = [fixture.fixture_id for fixture in fixtures] + if len(ids) != len(set(ids)): + raise FixtureValidationError("duplicate fixture_id") + + return cls( + experiment_id=experiment_id, + fixture_set_version=fixture_set_version, + source_under_test_sha=source_sha, + schema_version=schema_version, + fixtures=fixtures, + ) + + +@dataclass(frozen=True) +class FixtureResult: + result_id: str + run_id: str + attempt: int + fixture_id: str + disposition: FixtureDisposition + source_under_test_sha: str + schema_version: str + fixture_manifest_sha256: str + structured_fields_used: tuple[str, ...] = () + exception_class: ExceptionClass | None = None + evidence_summary: str = "" + error: str | None = None + prior_result_id: str | None = None + + def __post_init__(self) -> None: + _required_text(self.result_id, "result_id") + _required_text(self.run_id, "run_id") + _required_text(self.fixture_id, "fixture_id") + _required_text(self.source_under_test_sha, "source_under_test_sha") + _required_text(self.schema_version, "schema_version") + if re.fullmatch(r"[0-9a-f]{64}", self.fixture_manifest_sha256) is None: + raise FixtureValidationError( + "fixture_manifest_sha256 must be 64 lowercase hexadecimal characters" + ) + if isinstance(self.attempt, bool) or not isinstance(self.attempt, int) or self.attempt < 1: + raise FixtureValidationError("attempt must be an integer >= 1") + if not isinstance(self.disposition, FixtureDisposition): + raise FixtureValidationError("disposition must be FixtureDisposition") + if self.disposition is FixtureDisposition.NOT_COVERED and self.exception_class is None: + raise FixtureValidationError("NOT_COVERED requires exception_class") + if self.exception_class is not None and not isinstance(self.exception_class, ExceptionClass): + raise FixtureValidationError("exception_class must be ExceptionClass") + fields = tuple(_required_text(value, "structured_fields_used") for value in self.structured_fields_used) + if len(fields) != len(set(fields)): + raise FixtureValidationError("structured_fields_used must not contain duplicates") + if self.prior_result_id is not None: + _required_text(self.prior_result_id, "prior_result_id") From d1afe2d11fd6ed1a6db765e791785ffbeb408e63 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:18:54 -0400 Subject: [PATCH 07/21] test: define frozen GSAE-E0 fixture identity --- .../gsae_e0/tests/test_fixture_schema.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/experiments/gsae_e0/tests/test_fixture_schema.py b/experiments/gsae_e0/tests/test_fixture_schema.py index a7c2b2d..e0af0b5 100644 --- a/experiments/gsae_e0/tests/test_fixture_schema.py +++ b/experiments/gsae_e0/tests/test_fixture_schema.py @@ -1,8 +1,14 @@ +from pathlib import Path + import pytest +from experiments.gsae_e0.fixtures import fixture_manifest_sha256, load_fixture_manifest from experiments.gsae_e0.schema import FixtureManifest, FixtureValidationError +FIXTURE_PATH = Path("experiments/gsae_e0/fixtures/stage_a_v1.json") + + def valid_fixture(fixture_id="AUTH-01"): return { "fixture_id": fixture_id, @@ -68,3 +74,45 @@ def test_malformed_required_path_groups_fail_closed(): data["fixtures"][0]["input_spec"]["required_path_groups"] = [[]] with pytest.raises(FixtureValidationError, match="required_path_groups"): FixtureManifest.from_dict(data) + + +def test_frozen_stage_a_fixture_ids_are_exact(): + manifest = load_fixture_manifest(FIXTURE_PATH) + assert {fixture.fixture_id for fixture in manifest.fixtures} == { + "NATIVE-01", + "NATIVE-02", + "NATIVE-03", + "NATIVE-04", + "NATIVE-05", + "NATIVE-06", + "NATIVE-07", + "NATIVE-08", + "NATIVE-09", + "NATIVE-10", + "NEG-01", + "NEG-02", + "NEG-03", + "NEG-04", + "NEG-05", + "NEG-06", + "NEG-07", + "NEG-08", + "AUTH-01", + "AUTH-02", + "AUTH-03", + "AUTH-04", + "AUTH-05", + "AUTH-06", + "AUTH-07", + "AUTH-08", + "AUTH-09", + "AUTH-10", + } + + +def test_fixture_hash_is_64_lower_hex_and_stable(): + first = fixture_manifest_sha256(FIXTURE_PATH) + second = fixture_manifest_sha256(FIXTURE_PATH) + assert first == second + assert len(first) == 64 + assert first == first.lower() From e4f8f5fac2c419f6700b1a964a24b2467ce31ec0 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:20:02 -0400 Subject: [PATCH 08/21] feat: add deterministic GSAE-E0 fixture loader --- experiments/gsae_e0/fixtures.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 experiments/gsae_e0/fixtures.py diff --git a/experiments/gsae_e0/fixtures.py b/experiments/gsae_e0/fixtures.py new file mode 100644 index 0000000..c8245bc --- /dev/null +++ b/experiments/gsae_e0/fixtures.py @@ -0,0 +1,27 @@ +"""Load and identify frozen GSAE-E0 fixture manifests deterministically.""" + +import hashlib +import json +from pathlib import Path + +from .schema import FixtureManifest + + +def canonical_json_bytes(value: object) -> bytes: + """Serialize JSON deterministically for content identity.""" + return ( + json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + "\n" + ).encode("utf-8") + + +def load_fixture_manifest(path: Path) -> FixtureManifest: + """Load and validate a fixture manifest from JSON.""" + data = json.loads(path.read_text(encoding="utf-8")) + return FixtureManifest.from_dict(data) + + +def fixture_manifest_sha256(path: Path) -> str: + """Return SHA-256 of canonical validated fixture-manifest JSON.""" + data = json.loads(path.read_text(encoding="utf-8")) + FixtureManifest.from_dict(data) + return hashlib.sha256(canonical_json_bytes(data)).hexdigest() From 02ac6740738272867859ca4d013e292861d2fcd8 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:20:28 -0400 Subject: [PATCH 09/21] feat: freeze GSAE-E0 Stage-A fixture candidate --- experiments/gsae_e0/fixtures/stage_a_v1.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 experiments/gsae_e0/fixtures/stage_a_v1.json diff --git a/experiments/gsae_e0/fixtures/stage_a_v1.json b/experiments/gsae_e0/fixtures/stage_a_v1.json new file mode 100644 index 0000000..c5fdd29 --- /dev/null +++ b/experiments/gsae_e0/fixtures/stage_a_v1.json @@ -0,0 +1 @@ +{"experiment_id":"GSAE-E0","fixture_set_version":"stage-a-v1","source_under_test_sha":"07a09698ca66e8837d04e6ec05b4de3448eced04","schema_version":"agent-control-plane.execution.v1","fixtures":[{"fixture_id":"NATIVE-01","family":"native_conformance","title":"allowed ordinary completion","purpose":"Verify ordinary registered dispatch records successful completion evidence.","criticality":"critical","required_semantics":["ordinary_completion"],"input_spec":{"operation":"dispatch_echo","payload":"hello"},"expected_classification_domain":["PASS","NOT_ESTABLISHED"]},{"fixture_id":"NATIVE-02","family":"native_conformance","title":"policy denial evidence","purpose":"Verify an explicit policy denial is recorded as task.denied evidence without inventing a denied task state.","criticality":"critical","required_semantics":["policy_denial_evidence"],"input_spec":{"operation":"policy_denial","reason":"blocked"},"expected_classification_domain":["PASS","NOT_ESTABLISHED"]},{"fixture_id":"NATIVE-03","family":"native_conformance","title":"unknown capability rejection","purpose":"Verify unknown capabilities fail closed with rejection evidence before KeyError.","criticality":"critical","required_semantics":["unknown_capability_rejection"],"input_spec":{"operation":"unknown_capability","capability":"missing"},"expected_classification_domain":["PASS","NOT_ESTABLISHED"]},{"fixture_id":"NATIVE-04","family":"native_conformance","title":"handler runtime failure","purpose":"Verify handler exceptions produce FAILED task state and task.failed evidence.","criticality":"critical","required_semantics":["handler_failure"],"input_spec":{"operation":"handler_failure","exception":"RuntimeError"},"expected_classification_domain":["PASS","NOT_ESTABLISHED"]},{"fixture_id":"NATIVE-05","family":"native_conformance","title":"cancellation evidence","purpose":"Verify cancellation records CANCELLED state and task.cancelled evidence.","criticality":"critical","required_semantics":["cancellation"],"input_spec":{"operation":"cancel_created_task"},"expected_classification_domain":["PASS","NOT_ESTABLISHED"]},{"fixture_id":"NATIVE-06","family":"native_conformance","title":"cooperative budget exhaustion","purpose":"Verify cooperative budget overrun produces BUDGET_EXHAUSTED evidence.","criticality":"critical","required_semantics":["budget_exhaustion"],"input_spec":{"operation":"budget_exhaustion","max_steps":0,"consume_steps":1},"expected_classification_domain":["PASS","NOT_ESTABLISHED"]},{"fixture_id":"NATIVE-07","family":"native_conformance","title":"artifact linkage","purpose":"Verify execution events can structurally bind input and output artifact references.","criticality":"critical","required_semantics":["artifact_linkage"],"input_spec":{"operation":"construct_event_with_artifacts","input_artifact_id":"input-1","output_artifact_id":"output-1"},"expected_classification_domain":["PASS","NOT_ESTABLISHED"]},{"fixture_id":"NATIVE-08","family":"native_conformance","title":"trace parent child linkage","purpose":"Verify trace context structurally represents distinct parent and child spans.","criticality":"critical","required_semantics":["trace_parent_child"],"input_spec":{"operation":"construct_trace","trace_id":"trace-1","span_id":"span-1","parent_span_id":"span-0"},"expected_classification_domain":["PASS","NOT_ESTABLISHED"]},{"fixture_id":"NATIVE-09","family":"native_conformance","title":"policy decision reference binding","purpose":"Verify execution events structurally carry a policy decision reference.","criticality":"critical","required_semantics":["policy_decision_reference"],"input_spec":{"operation":"construct_event","policy_decision_ref":"decision-1"},"expected_classification_domain":["PASS","NOT_ESTABLISHED"]},{"fixture_id":"NATIVE-10","family":"native_conformance","title":"legacy provenance mapping","purpose":"Verify legacy ACP provenance maps into execution.v1 when required context and run identity agree.","criticality":"critical","required_semantics":["legacy_provenance_mapping"],"input_spec":{"operation":"map_provenance","run_id":"run-1"},"expected_classification_domain":["PASS","NOT_ESTABLISHED"]},{"fixture_id":"NEG-01","family":"negative_control","title":"provenance run mismatch rejection","purpose":"Verify mismatched legacy provenance and contract run identity fail closed.","criticality":"critical","required_semantics":["run_identity_consistency"],"input_spec":{"operation":"map_provenance_mismatched_run","source_run_id":"run-source","contract_run_id":"run-contract"},"expected_classification_domain":["EXPECTED_REJECTION","NOT_ESTABLISHED"],"expected_exception_class":"PROVENANCE_GAP"},{"fixture_id":"NEG-02","family":"negative_control","title":"unsupported schema rejection","purpose":"Verify unsupported execution schema versions are rejected.","criticality":"critical","required_semantics":["schema_version_validation"],"input_spec":{"operation":"construct_identity","schema_version":"agent-control-plane.execution.v999"},"expected_classification_domain":["EXPECTED_REJECTION","NOT_ESTABLISHED"],"expected_exception_class":"MALFORMED_INPUT"},{"fixture_id":"NEG-03","family":"negative_control","title":"blank identity rejection","purpose":"Verify blank required execution identities are rejected.","criticality":"critical","required_semantics":["required_identity_validation"],"input_spec":{"operation":"construct_identity","execution_id":" ","run_id":"run-1"},"expected_classification_domain":["EXPECTED_REJECTION","NOT_ESTABLISHED"],"expected_exception_class":"MALFORMED_INPUT"},{"fixture_id":"NEG-04","family":"negative_control","title":"span self parenting rejection","purpose":"Verify a span cannot name itself as parent.","criticality":"critical","required_semantics":["trace_acyclicity_guard"],"input_spec":{"operation":"construct_trace","trace_id":"trace-1","span_id":"span-1","parent_span_id":"span-1"},"expected_classification_domain":["EXPECTED_REJECTION","NOT_ESTABLISHED"],"expected_exception_class":"MALFORMED_INPUT"},{"fixture_id":"NEG-05","family":"negative_control","title":"malformed SHA-256 rejection","purpose":"Verify malformed artifact SHA-256 values are rejected.","criticality":"critical","required_semantics":["artifact_hash_validation"],"input_spec":{"operation":"construct_artifact","sha256":"not-a-hash"},"expected_classification_domain":["EXPECTED_REJECTION","NOT_ESTABLISHED"],"expected_exception_class":"MALFORMED_INPUT"},{"fixture_id":"NEG-06","family":"negative_control","title":"invalid monotonic ordering rejection","purpose":"Verify negative and non-integer monotonic ordering values are rejected.","criticality":"critical","required_semantics":["monotonic_order_validation"],"input_spec":{"operation":"construct_event","monotonic_values":[-1,"1"]},"expected_classification_domain":["EXPECTED_REJECTION","NOT_ESTABLISHED"],"expected_exception_class":"MALFORMED_INPUT"},{"fixture_id":"NEG-07","family":"negative_control","title":"non UTC timestamp rejection","purpose":"Verify naive and non-UTC event timestamps are rejected.","criticality":"critical","required_semantics":["utc_timestamp_validation"],"input_spec":{"operation":"construct_event","timestamps":["2026-09-16T00:00:00","2026-09-16T01:00:00+01:00"]},"expected_classification_domain":["EXPECTED_REJECTION","NOT_ESTABLISHED"],"expected_exception_class":"MALFORMED_INPUT"},{"fixture_id":"NEG-08","family":"negative_control","title":"malformed nested contract rejection","purpose":"Verify malformed nested contract objects are rejected.","criticality":"critical","required_semantics":["nested_type_validation"],"input_spec":{"operation":"construct_event","invalid_component":"not-a-component"},"expected_classification_domain":["EXPECTED_REJECTION","NOT_ESTABLISHED"],"expected_exception_class":"MALFORMED_INPUT"},{"fixture_id":"AUTH-01","family":"authority_semantic","title":"principal / acting identity","purpose":"Require a stable structured representation of the principal or acting identity.","criticality":"critical","required_semantics":["principal_identity"],"input_spec":{"required_path_groups":[["principal.principal_id"]],"gap_class_if_absent":"MISSING_CORE_SEMANTIC"},"expected_classification_domain":["STRUCTURED_COVERAGE","NOT_COVERED"]},{"fixture_id":"AUTH-02","family":"authority_semantic","title":"requested capability","purpose":"Require a stable structured representation of the requested capability.","criticality":"critical","required_semantics":["requested_capability"],"input_spec":{"required_path_groups":[["capability"]],"gap_class_if_absent":"MISSING_CORE_SEMANTIC"},"expected_classification_domain":["STRUCTURED_COVERAGE","NOT_COVERED"]},{"fixture_id":"AUTH-03","family":"authority_semantic","title":"resource / target scope","purpose":"Require a stable structured representation of the resource or target scope.","criticality":"critical","required_semantics":["resource_scope"],"input_spec":{"required_path_groups":[["resource.resource_id"]],"gap_class_if_absent":"MISSING_CORE_SEMANTIC"},"expected_classification_domain":["STRUCTURED_COVERAGE","NOT_COVERED"]},{"fixture_id":"AUTH-04","family":"authority_semantic","title":"operation / action semantics","purpose":"Require operation semantics distinct from generic capability naming.","criticality":"critical","required_semantics":["operation_semantics"],"input_spec":{"required_path_groups":[["operation.action"]],"gap_class_if_absent":"AMBIGUOUS_SEMANTIC"},"expected_classification_domain":["STRUCTURED_COVERAGE","NOT_COVERED"]},{"fixture_id":"AUTH-05","family":"authority_semantic","title":"policy identity plus version or hash","purpose":"Require policy identity and policy version or hash as structured values.","criticality":"critical","required_semantics":["policy_identity","policy_version_or_hash"],"input_spec":{"required_path_groups":[["policy.policy_id"],["policy.version_or_hash"]],"gap_class_if_absent":"MISSING_CORE_SEMANTIC"},"expected_classification_domain":["STRUCTURED_COVERAGE","NOT_COVERED"]},{"fixture_id":"AUTH-06","family":"authority_semantic","title":"decision identity and explicit outcome","purpose":"Require decision identity plus explicit allow, deny, or conditional outcome semantics.","criticality":"critical","required_semantics":["decision_identity","decision_outcome"],"input_spec":{"required_path_groups":[["policy_decision_ref"],["decision.outcome"]],"gap_class_if_absent":"MISSING_CORE_SEMANTIC"},"expected_classification_domain":["STRUCTURED_COVERAGE","NOT_COVERED"]},{"fixture_id":"AUTH-07","family":"authority_semantic","title":"lease / authority expiry","purpose":"Require structured authority lease or expiry semantics.","criticality":"critical","required_semantics":["authority_expiry"],"input_spec":{"required_path_groups":[["authority.expires_at"]],"gap_class_if_absent":"MISSING_CORE_SEMANTIC"},"expected_classification_domain":["STRUCTURED_COVERAGE","NOT_COVERED"]},{"fixture_id":"AUTH-08","family":"authority_semantic","title":"delegation chain / delegator scope","purpose":"Require structured delegation identity and delegated scope.","criticality":"critical","required_semantics":["delegator_identity","delegated_scope"],"input_spec":{"required_path_groups":[["delegation.delegator_id"],["delegation.scope"]],"gap_class_if_absent":"MISSING_CORE_SEMANTIC"},"expected_classification_domain":["STRUCTURED_COVERAGE","NOT_COVERED"]},{"fixture_id":"AUTH-09","family":"authority_semantic","title":"authority conditions","purpose":"Require structured permitted, forbidden, or conditional action semantics.","criticality":"critical","required_semantics":["authority_conditions"],"input_spec":{"required_path_groups":[["authority.conditions"]],"gap_class_if_absent":"MISSING_CORE_SEMANTIC"},"expected_classification_domain":["STRUCTURED_COVERAGE","NOT_COVERED"]},{"fixture_id":"AUTH-10","family":"authority_semantic","title":"reason code and decision execution linkage","purpose":"Require a structured decision reason code plus decision-to-execution linkage.","criticality":"critical","required_semantics":["decision_reason_code","decision_execution_linkage"],"input_spec":{"required_path_groups":[["policy_decision_ref"],["decision.reason_code"]],"gap_class_if_absent":"MISSING_CORE_SEMANTIC"},"expected_classification_domain":["STRUCTURED_COVERAGE","NOT_COVERED"]}]} \ No newline at end of file From d49fde4720429d3360d30390c455ad4414e82761 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:22:06 -0400 Subject: [PATCH 10/21] test: define GSAE-E0 semantic classification rules --- .../gsae_e0/tests/test_classification.py | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 experiments/gsae_e0/tests/test_classification.py diff --git a/experiments/gsae_e0/tests/test_classification.py b/experiments/gsae_e0/tests/test_classification.py new file mode 100644 index 0000000..493ef1d --- /dev/null +++ b/experiments/gsae_e0/tests/test_classification.py @@ -0,0 +1,212 @@ +from experiments.gsae_e0.classify import ( + aggregate_disposition, + classify_authority_fixture, + execution_v1_surface_paths, +) +from experiments.gsae_e0.schema import ( + AggregateDisposition, + Criticality, + ExceptionClass, + FixtureDefinition, + FixtureDisposition, + FixtureFamily, + FixtureResult, +) + + +SOURCE_SHA = "07a09698ca66e8837d04e6ec05b4de3448eced04" +SCHEMA_VERSION = "agent-control-plane.execution.v1" +FIXTURE_HASH = "a" * 64 + + +def authority_fixture( + fixture_id="AUTH-01", + groups=None, + gap_class="MISSING_CORE_SEMANTIC", + criticality="critical", +): + if groups is None: + groups = [["principal.principal_id"]] + return FixtureDefinition.from_dict( + { + "fixture_id": fixture_id, + "family": "authority_semantic", + "title": fixture_id, + "purpose": "synthetic apparatus classification test", + "criticality": criticality, + "required_semantics": ["synthetic_semantic"], + "input_spec": { + "required_path_groups": groups, + "gap_class_if_absent": gap_class, + }, + "expected_classification_domain": ["STRUCTURED_COVERAGE", "NOT_COVERED"], + } + ) + + +def result_context(result_id="result-1"): + return { + "result_id": result_id, + "run_id": "synthetic-run", + "attempt": 1, + "source_under_test_sha": SOURCE_SHA, + "schema_version": SCHEMA_VERSION, + "fixture_manifest_sha256": FIXTURE_HASH, + } + + +def result_for( + fixture_id, + disposition, + *, + exception_class=None, + result_id=None, +): + return FixtureResult( + result_id=result_id or f"result-{fixture_id}", + run_id="synthetic-run", + attempt=1, + fixture_id=fixture_id, + disposition=disposition, + source_under_test_sha=SOURCE_SHA, + schema_version=SCHEMA_VERSION, + fixture_manifest_sha256=FIXTURE_HASH, + exception_class=exception_class, + ) + + +def test_execution_v1_surface_contains_only_actual_structured_contract_paths(): + surface = execution_v1_surface_paths() + assert "capability" in surface + assert "policy_decision_ref" in surface + assert "identity.run_id" in surface + assert "trace.parent_span_id" in surface + assert "component.adapter_id" in surface + assert "input_artifacts[].sha256" in surface + assert "output_artifacts[].artifact_id" in surface + assert "principal.principal_id" not in surface + assert "resource.resource_id" not in surface + assert "policy.version_or_hash" not in surface + assert "delegation.scope" not in surface + + +def test_detail_escape_hatch_does_not_count_as_structured_authority_coverage(): + fixture = authority_fixture(groups=[["principal.principal_id"]]) + result = classify_authority_fixture( + fixture, + frozenset({"detail"}), + **result_context(), + ) + assert result.disposition is FixtureDisposition.NOT_COVERED + assert result.exception_class is ExceptionClass.MISSING_CORE_SEMANTIC + assert result.structured_fields_used == () + + +def test_all_required_path_groups_must_be_satisfied(): + fixture = authority_fixture( + fixture_id="AUTH-06", + groups=[["policy_decision_ref"], ["decision.outcome"]], + ) + result = classify_authority_fixture( + fixture, + frozenset({"policy_decision_ref"}), + **result_context(), + ) + assert result.disposition is FixtureDisposition.NOT_COVERED + assert result.exception_class is ExceptionClass.MISSING_CORE_SEMANTIC + + +def test_structured_coverage_records_exact_matching_paths(): + fixture = authority_fixture( + fixture_id="AUTH-02", + groups=[["capability"]], + ) + result = classify_authority_fixture( + fixture, + frozenset({"capability", "detail"}), + **result_context(), + ) + assert result.disposition is FixtureDisposition.STRUCTURED_COVERAGE + assert result.exception_class is None + assert result.structured_fields_used == ("capability",) + + +def test_critical_missing_semantic_forces_not_feasible(): + fixture = authority_fixture() + result = result_for( + fixture.fixture_id, + FixtureDisposition.NOT_COVERED, + exception_class=ExceptionClass.MISSING_CORE_SEMANTIC, + ) + assert aggregate_disposition((fixture,), (result,)) is AggregateDisposition.NOT_FEASIBLE + + +def test_critical_ambiguous_semantic_forces_not_feasible(): + fixture = authority_fixture(gap_class="AMBIGUOUS_SEMANTIC") + result = result_for( + fixture.fixture_id, + FixtureDisposition.NOT_COVERED, + exception_class=ExceptionClass.AMBIGUOUS_SEMANTIC, + ) + assert aggregate_disposition((fixture,), (result,)) is AggregateDisposition.NOT_FEASIBLE + + +def test_missing_result_for_required_fixture_forces_not_established(): + fixture = authority_fixture() + assert aggregate_disposition((fixture,), ()) is AggregateDisposition.NOT_ESTABLISHED + + +def test_provenance_gap_forces_not_established(): + fixture = authority_fixture() + result = result_for( + fixture.fixture_id, + FixtureDisposition.NOT_ESTABLISHED, + exception_class=ExceptionClass.PROVENANCE_GAP, + ) + assert aggregate_disposition((fixture,), (result,)) is AggregateDisposition.NOT_ESTABLISHED + + +def test_implementation_defect_forces_not_established(): + fixture = authority_fixture() + result = result_for( + fixture.fixture_id, + FixtureDisposition.NOT_ESTABLISHED, + exception_class=ExceptionClass.IMPLEMENTATION_DEFECT, + ) + assert aggregate_disposition((fixture,), (result,)) is AggregateDisposition.NOT_ESTABLISHED + + +def test_noncritical_gap_with_all_critical_coverage_is_conditional(): + critical = authority_fixture( + fixture_id="AUTH-C", + groups=[["capability"]], + criticality="critical", + ) + noncritical = authority_fixture( + fixture_id="AUTH-NC", + groups=[["optional.extension"]], + gap_class="NONCRITICAL_EXTENSION", + criticality="noncritical", + ) + critical_result = result_for(critical.fixture_id, FixtureDisposition.STRUCTURED_COVERAGE) + noncritical_result = result_for( + noncritical.fixture_id, + FixtureDisposition.NOT_COVERED, + exception_class=ExceptionClass.NONCRITICAL_EXTENSION, + ) + assert ( + aggregate_disposition((critical, noncritical), (critical_result, noncritical_result)) + is AggregateDisposition.CONDITIONAL + ) + + +def test_all_required_results_satisfied_is_feasible_for_frozen_scope(): + fixture = authority_fixture(fixture_id="AUTH-02", groups=[["capability"]]) + result = result_for(fixture.fixture_id, FixtureDisposition.STRUCTURED_COVERAGE) + assert aggregate_disposition((fixture,), (result,)) is AggregateDisposition.FEASIBLE + + +def test_unknown_result_fixture_id_fails_closed(): + fixture = authority_fixture() + unknown = result_for("AUTH-UNKNOWN", FixtureDisposition.STRUCTURED_COVERAGE) + assert aggregate_disposition((fixture,), (unknown,)) is AggregateDisposition.NOT_ESTABLISHED From 4b7191b50e4096ef46463bfaa858f84236b955a1 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:23:04 -0400 Subject: [PATCH 11/21] feat: add GSAE-E0 semantic classification --- experiments/gsae_e0/classify.py | 174 ++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 experiments/gsae_e0/classify.py diff --git a/experiments/gsae_e0/classify.py b/experiments/gsae_e0/classify.py new file mode 100644 index 0000000..613ab85 --- /dev/null +++ b/experiments/gsae_e0/classify.py @@ -0,0 +1,174 @@ +"""Pure structured-coverage and aggregate classification for GSAE-E0 Stage A.""" + +from dataclasses import fields +from typing import Iterable + +from agent_control_plane.contract import ( + ArtifactRef, + ComponentIdentity, + ExecutionEvent, + ExecutionIdentity, + TraceContext, +) + +from .schema import ( + AggregateDisposition, + Criticality, + ExceptionClass, + FixtureDefinition, + FixtureDisposition, + FixtureFamily, + FixtureResult, +) + + +_AUTHORITY_ESCAPE_HATCH_PATHS = frozenset( + { + "detail", + "input_artifacts[].uri", + "output_artifacts[].uri", + } +) + +_NOT_FEASIBLE_GAP_CLASSES = frozenset( + { + ExceptionClass.MISSING_CORE_SEMANTIC, + ExceptionClass.AMBIGUOUS_SEMANTIC, + ExceptionClass.RUNTIME_SPECIFIC, + ExceptionClass.ADAPTER_COMPLEXITY, + } +) + + +def _paths_for(dataclass_type: type, prefix: str = "") -> set[str]: + return {f"{prefix}{field.name}" for field in fields(dataclass_type)} + + +def execution_v1_surface_paths() -> frozenset[str]: + """Return only machine-addressable fields actually present in execution.v1. + + This deliberately does not create conceptual aliases for meanings that the + contract does not structurally encode. + """ + paths = _paths_for(ExecutionEvent) + paths.update(_paths_for(ExecutionIdentity, "identity.")) + paths.update(_paths_for(TraceContext, "trace.")) + paths.update(_paths_for(ComponentIdentity, "component.")) + paths.update(_paths_for(ArtifactRef, "input_artifacts[].")) + paths.update(_paths_for(ArtifactRef, "output_artifacts[].")) + return frozenset(paths) + + +def classify_authority_fixture( + fixture: FixtureDefinition, + surface_paths: frozenset[str], + *, + result_id: str, + run_id: str, + attempt: int, + source_under_test_sha: str, + schema_version: str, + fixture_manifest_sha256: str, + prior_result_id: str | None = None, +) -> FixtureResult: + """Classify one authority-semantic fixture against a typed field surface.""" + if fixture.family is not FixtureFamily.AUTHORITY: + raise ValueError("classify_authority_fixture requires an authority_semantic fixture") + + groups = fixture.input_spec.get("required_path_groups") + if not isinstance(groups, tuple) or not groups: + raise ValueError("authority fixture lacks validated required_path_groups") + + matched: list[str] = [] + for group in groups: + eligible = sorted( + path + for path in group + if path in surface_paths and path not in _AUTHORITY_ESCAPE_HATCH_PATHS + ) + if not eligible: + gap_class = ExceptionClass(fixture.input_spec["gap_class_if_absent"]) + return FixtureResult( + result_id=result_id, + run_id=run_id, + attempt=attempt, + fixture_id=fixture.fixture_id, + disposition=FixtureDisposition.NOT_COVERED, + source_under_test_sha=source_under_test_sha, + schema_version=schema_version, + fixture_manifest_sha256=fixture_manifest_sha256, + structured_fields_used=(), + exception_class=gap_class, + evidence_summary="one or more required structured path groups are absent", + prior_result_id=prior_result_id, + ) + matched.append(eligible[0]) + + return FixtureResult( + result_id=result_id, + run_id=run_id, + attempt=attempt, + fixture_id=fixture.fixture_id, + disposition=FixtureDisposition.STRUCTURED_COVERAGE, + source_under_test_sha=source_under_test_sha, + schema_version=schema_version, + fixture_manifest_sha256=fixture_manifest_sha256, + structured_fields_used=tuple(matched), + evidence_summary="all required structured path groups are represented", + prior_result_id=prior_result_id, + ) + + +def aggregate_disposition( + fixtures: Iterable[FixtureDefinition], + results: Iterable[FixtureResult], +) -> AggregateDisposition: + """Derive the bounded Stage-A disposition with fail-closed precedence.""" + fixture_tuple = tuple(fixtures) + result_tuple = tuple(results) + + fixture_ids = [fixture.fixture_id for fixture in fixture_tuple] + result_ids = [result.fixture_id for result in result_tuple] + + if not fixture_tuple: + return AggregateDisposition.NOT_ESTABLISHED + if len(fixture_ids) != len(set(fixture_ids)): + return AggregateDisposition.NOT_ESTABLISHED + if len(result_ids) != len(set(result_ids)): + return AggregateDisposition.NOT_ESTABLISHED + if set(result_ids) != set(fixture_ids): + return AggregateDisposition.NOT_ESTABLISHED + + by_fixture = {result.fixture_id: result for result in result_tuple} + + for fixture in fixture_tuple: + result = by_fixture[fixture.fixture_id] + if result.disposition not in fixture.expected_classification_domain: + return AggregateDisposition.NOT_ESTABLISHED + if result.disposition is FixtureDisposition.NOT_ESTABLISHED: + return AggregateDisposition.NOT_ESTABLISHED + if result.exception_class in { + ExceptionClass.PROVENANCE_GAP, + ExceptionClass.IMPLEMENTATION_DEFECT, + }: + return AggregateDisposition.NOT_ESTABLISHED + + for fixture in fixture_tuple: + result = by_fixture[fixture.fixture_id] + if ( + fixture.criticality is Criticality.CRITICAL + and result.disposition is FixtureDisposition.NOT_COVERED + and result.exception_class in _NOT_FEASIBLE_GAP_CLASSES + ): + return AggregateDisposition.NOT_FEASIBLE + if fixture.criticality is Criticality.CRITICAL and result.disposition is FixtureDisposition.NOT_COVERED: + return AggregateDisposition.NOT_ESTABLISHED + + if any( + fixture.criticality is Criticality.NONCRITICAL + and by_fixture[fixture.fixture_id].disposition is FixtureDisposition.NOT_COVERED + for fixture in fixture_tuple + ): + return AggregateDisposition.CONDITIONAL + + return AggregateDisposition.FEASIBLE From 2fa683bec5a3e8c4904672973c6282ba6f34a306 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:23:48 -0400 Subject: [PATCH 12/21] test: define GSAE-E0 native observation and source-binding rules --- experiments/gsae_e0/tests/test_runner.py | 60 ++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 experiments/gsae_e0/tests/test_runner.py diff --git a/experiments/gsae_e0/tests/test_runner.py b/experiments/gsae_e0/tests/test_runner.py new file mode 100644 index 0000000..4bdac04 --- /dev/null +++ b/experiments/gsae_e0/tests/test_runner.py @@ -0,0 +1,60 @@ +from pathlib import Path + +import pytest + +from experiments.gsae_e0.runner import ( + SourceBindingError, + observe_native_fixture, + verify_source_binding, +) +from experiments.gsae_e0.schema import FixtureDisposition + + +SOURCE_SHA = "07a09698ca66e8837d04e6ec05b4de3448eced04" + + +def test_bound_source_is_ancestor_and_acp_package_is_unchanged(): + verify_source_binding(Path.cwd(), SOURCE_SHA) + + +def test_unknown_source_sha_fails_closed(): + with pytest.raises(SourceBindingError): + verify_source_binding(Path.cwd(), "0" * 40) + + +@pytest.mark.parametrize( + ("fixture_id", "expected_disposition", "summary_fragment"), + [ + ("NATIVE-01", FixtureDisposition.PASS, "task.completed"), + ("NATIVE-02", FixtureDisposition.PASS, "task.denied"), + ("NATIVE-03", FixtureDisposition.PASS, "task.rejected"), + ("NATIVE-04", FixtureDisposition.PASS, "task.failed"), + ("NATIVE-05", FixtureDisposition.PASS, "task.cancelled"), + ("NATIVE-06", FixtureDisposition.PASS, "budget_exhausted"), + ("NATIVE-07", FixtureDisposition.PASS, "artifact"), + ("NATIVE-08", FixtureDisposition.PASS, "parent_span_id"), + ("NATIVE-09", FixtureDisposition.PASS, "policy_decision_ref"), + ("NATIVE-10", FixtureDisposition.PASS, "mapped provenance"), + ("NEG-01", FixtureDisposition.EXPECTED_REJECTION, "run_id"), + ("NEG-02", FixtureDisposition.EXPECTED_REJECTION, "schema_version"), + ("NEG-03", FixtureDisposition.EXPECTED_REJECTION, "execution_id"), + ("NEG-04", FixtureDisposition.EXPECTED_REJECTION, "parent_span_id"), + ("NEG-05", FixtureDisposition.EXPECTED_REJECTION, "sha256"), + ("NEG-06", FixtureDisposition.EXPECTED_REJECTION, "monotonic_ns"), + ("NEG-07", FixtureDisposition.EXPECTED_REJECTION, "utc_timestamp"), + ("NEG-08", FixtureDisposition.EXPECTED_REJECTION, "component"), + ], +) +def test_native_observation_matches_expected_contract_behavior( + fixture_id, + expected_disposition, + summary_fragment, +): + disposition, summary = observe_native_fixture(fixture_id) + assert disposition is expected_disposition + assert summary_fragment in summary + + +def test_unknown_observation_fixture_fails_closed(): + with pytest.raises(ValueError, match="unknown native fixture"): + observe_native_fixture("NATIVE-999") From 59133dd41449469373691d41570d4c2e2b4beaf2 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:25:04 -0400 Subject: [PATCH 13/21] feat: add GSAE-E0 native conformance observations --- experiments/gsae_e0/runner.py | 332 ++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 experiments/gsae_e0/runner.py diff --git a/experiments/gsae_e0/runner.py b/experiments/gsae_e0/runner.py new file mode 100644 index 0000000..f4b4289 --- /dev/null +++ b/experiments/gsae_e0/runner.py @@ -0,0 +1,332 @@ +"""Source binding and deterministic native ACP observations for GSAE-E0 Stage A.""" + +from pathlib import Path +import re +import subprocess +from typing import Callable + +from agent_control_plane import ControlPlane, ExecutionBudget, Task, TaskState +from agent_control_plane.contract import ( + SCHEMA_VERSION, + ArtifactRef, + ComponentIdentity, + ContractValidationError, + ExecutionEvent, + ExecutionIdentity, + TraceContext, + map_provenance_event, +) +from agent_control_plane.provenance import ProvenanceEvent + +from .schema import FixtureDisposition + + +class SourceBindingError(RuntimeError): + """Raised when the working tree cannot prove the frozen ACP source binding.""" + + +def _git(repo_root: Path, *args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + ["git", *args], + cwd=repo_root, + capture_output=True, + text=True, + check=False, + ) + + +def verify_source_binding(repo_root: Path, expected_sha: str) -> None: + """Prove expected source ancestry and zero ACP-source drift from that commit.""" + if re.fullmatch(r"[0-9a-f]{40}", expected_sha) is None: + raise SourceBindingError("expected_sha must be 40 lowercase hexadecimal characters") + + ancestor = _git(repo_root, "merge-base", "--is-ancestor", expected_sha, "HEAD") + if ancestor.returncode != 0: + detail = ancestor.stderr.strip() or ancestor.stdout.strip() or "ancestor check failed" + raise SourceBindingError(f"source-under-test ancestry not established: {detail}") + + diff = _git(repo_root, "diff", "--quiet", expected_sha, "--", "src/agent_control_plane") + if diff.returncode != 0: + detail = diff.stderr.strip() or diff.stdout.strip() or "ACP source differs" + raise SourceBindingError(f"ACP source package drifted from source under test: {detail}") + + +def _event_kwargs() -> dict[str, object]: + 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-16T00:00:00Z", + "monotonic_ns": 42, + } + + +def _native_01() -> tuple[FixtureDisposition, str]: + plane = ControlPlane(run_id="gsae-e0-native-01") + plane.register("echo", lambda task: task.payload) + task = plane.dispatch("echo", Task(payload="hello", id="task-native-01")) + if task.state is not TaskState.COMPLETED: + return FixtureDisposition.NOT_ESTABLISHED, f"unexpected state={task.state.value}" + if not any(event.event == "task.completed" for event in plane.events): + return FixtureDisposition.NOT_ESTABLISHED, "missing task.completed evidence" + return FixtureDisposition.PASS, "task.completed recorded" + + +def _native_02() -> tuple[FixtureDisposition, str]: + plane = ControlPlane(policy=lambda capability, task: "blocked", run_id="gsae-e0-native-02") + plane.register("echo", lambda task: task.payload) + task = plane.dispatch("echo", Task(payload="hello", id="task-native-02")) + if task.error != "blocked": + return FixtureDisposition.NOT_ESTABLISHED, "policy denial reason not retained" + if not any(event.event == "task.denied" for event in plane.events): + return FixtureDisposition.NOT_ESTABLISHED, "missing task.denied evidence" + return FixtureDisposition.PASS, "task.denied recorded" + + +def _native_03() -> tuple[FixtureDisposition, str]: + plane = ControlPlane(run_id="gsae-e0-native-03") + task = Task(payload="hello", id="task-native-03") + try: + plane.dispatch("missing", task) + except KeyError: + if any(event.event == "task.rejected" for event in plane.events): + return FixtureDisposition.PASS, "task.rejected recorded before KeyError" + return FixtureDisposition.NOT_ESTABLISHED, "KeyError without task.rejected evidence" + return FixtureDisposition.NOT_ESTABLISHED, "unknown capability did not raise KeyError" + + +def _native_04() -> tuple[FixtureDisposition, str]: + def fail(task: Task) -> object: + raise RuntimeError("synthetic failure") + + plane = ControlPlane(run_id="gsae-e0-native-04") + plane.register("fail", fail) + task = plane.dispatch("fail", Task(payload=None, id="task-native-04")) + if task.state is not TaskState.FAILED: + return FixtureDisposition.NOT_ESTABLISHED, f"unexpected state={task.state.value}" + if not any(event.event == "task.failed" for event in plane.events): + return FixtureDisposition.NOT_ESTABLISHED, "missing task.failed evidence" + return FixtureDisposition.PASS, "task.failed recorded" + + +def _native_05() -> tuple[FixtureDisposition, str]: + plane = ControlPlane(run_id="gsae-e0-native-05") + task = plane.cancel(Task(payload=None, id="task-native-05")) + if task.state is not TaskState.CANCELLED: + return FixtureDisposition.NOT_ESTABLISHED, f"unexpected state={task.state.value}" + if not any(event.event == "task.cancelled" for event in plane.events): + return FixtureDisposition.NOT_ESTABLISHED, "missing task.cancelled evidence" + return FixtureDisposition.PASS, "task.cancelled recorded" + + +def _native_06() -> tuple[FixtureDisposition, str]: + def consume(task: Task) -> object: + task.consume(steps=1) + return "unreachable" + + plane = ControlPlane(run_id="gsae-e0-native-06") + plane.register("consume", consume) + task = plane.dispatch( + "consume", + Task( + payload=None, + id="task-native-06", + budget=ExecutionBudget(max_steps=0), + ), + ) + if task.state is not TaskState.BUDGET_EXHAUSTED: + return FixtureDisposition.NOT_ESTABLISHED, f"unexpected state={task.state.value}" + if not any(event.event == "task.budget_exhausted" for event in plane.events): + return FixtureDisposition.NOT_ESTABLISHED, "missing task.budget_exhausted evidence" + return FixtureDisposition.PASS, "task.budget_exhausted recorded" + + +def _native_07() -> tuple[FixtureDisposition, str]: + event = ExecutionEvent( + **_event_kwargs(), + input_artifacts=(ArtifactRef(artifact_id="input-1", kind="input"),), + output_artifacts=(ArtifactRef(artifact_id="output-1", kind="output"),), + ) + if event.input_artifacts[0].artifact_id != "input-1" or event.output_artifacts[0].artifact_id != "output-1": + return FixtureDisposition.NOT_ESTABLISHED, "artifact identities not preserved" + return FixtureDisposition.PASS, "input/output artifact linkage preserved" + + +def _native_08() -> tuple[FixtureDisposition, str]: + trace = TraceContext(trace_id="trace-1", span_id="span-1", parent_span_id="span-0") + if trace.parent_span_id != "span-0": + return FixtureDisposition.NOT_ESTABLISHED, "parent span not preserved" + return FixtureDisposition.PASS, "parent_span_id structurally preserved" + + +def _native_09() -> tuple[FixtureDisposition, str]: + event = ExecutionEvent(**_event_kwargs(), policy_decision_ref="decision-1") + if event.policy_decision_ref != "decision-1": + return FixtureDisposition.NOT_ESTABLISHED, "policy decision reference not preserved" + return FixtureDisposition.PASS, "policy_decision_ref structurally preserved" + + +def _native_10() -> tuple[FixtureDisposition, str]: + source = ProvenanceEvent( + event="task.completed", + task_id="task-1", + run_id="run-1", + capability="echo", + state="completed", + timestamp="2026-09-16T00:00:00Z", + ) + mapped = map_provenance_event( + source, + 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", + ), + monotonic_ns=1, + ) + if ( + mapped.event_type != source.event + or mapped.task_id != source.task_id + or mapped.capability != source.capability + or mapped.status != source.state + ): + return FixtureDisposition.NOT_ESTABLISHED, "mapped provenance fields differ" + return FixtureDisposition.PASS, "mapped provenance preserves event/task/capability/state" + + +def _expected_rejection(action: Callable[[], object], fragment: str) -> tuple[FixtureDisposition, str]: + try: + action() + except ContractValidationError as exc: + message = str(exc) + if fragment in message: + return FixtureDisposition.EXPECTED_REJECTION, message + return FixtureDisposition.NOT_ESTABLISHED, f"unexpected validation message: {message}" + return FixtureDisposition.NOT_ESTABLISHED, "expected ContractValidationError was not raised" + + +def _neg_01() -> tuple[FixtureDisposition, str]: + source = ProvenanceEvent( + event="task.started", + task_id="task-1", + run_id="run-source", + state="running", + timestamp="2026-09-16T00:00:00Z", + ) + return _expected_rejection( + lambda: map_provenance_event( + source, + identity=ExecutionIdentity(execution_id="exec-1", run_id="run-contract"), + 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=1, + ), + "run_id", + ) + + +def _neg_02() -> tuple[FixtureDisposition, str]: + return _expected_rejection( + lambda: ExecutionIdentity( + execution_id="exec-1", + run_id="run-1", + schema_version=f"{SCHEMA_VERSION}.unsupported", + ), + "schema_version", + ) + + +def _neg_03() -> tuple[FixtureDisposition, str]: + return _expected_rejection( + lambda: ExecutionIdentity(execution_id=" ", run_id="run-1"), + "execution_id", + ) + + +def _neg_04() -> tuple[FixtureDisposition, str]: + return _expected_rejection( + lambda: TraceContext(trace_id="trace-1", span_id="span-1", parent_span_id="span-1"), + "parent_span_id", + ) + + +def _neg_05() -> tuple[FixtureDisposition, str]: + return _expected_rejection( + lambda: ArtifactRef(artifact_id="artifact-1", kind="input", sha256="not-a-hash"), + "sha256", + ) + + +def _neg_06() -> tuple[FixtureDisposition, str]: + for value in (-1, "1"): + disposition, summary = _expected_rejection( + lambda value=value: ExecutionEvent(**{**_event_kwargs(), "monotonic_ns": value}), + "monotonic_ns", + ) + if disposition is not FixtureDisposition.EXPECTED_REJECTION: + return disposition, summary + return FixtureDisposition.EXPECTED_REJECTION, "monotonic_ns rejects negative and non-integer values" + + +def _neg_07() -> tuple[FixtureDisposition, str]: + for value in ("2026-09-16T00:00:00", "2026-09-16T01:00:00+01:00"): + disposition, summary = _expected_rejection( + lambda value=value: ExecutionEvent(**{**_event_kwargs(), "utc_timestamp": value}), + "utc_timestamp", + ) + if disposition is not FixtureDisposition.EXPECTED_REJECTION: + return disposition, summary + return FixtureDisposition.EXPECTED_REJECTION, "utc_timestamp rejects naive and non-UTC values" + + +def _neg_08() -> tuple[FixtureDisposition, str]: + return _expected_rejection( + lambda: ExecutionEvent(**{**_event_kwargs(), "component": "not-a-component"}), + "component", + ) + + +_OBSERVATIONS: dict[str, Callable[[], tuple[FixtureDisposition, str]]] = { + "NATIVE-01": _native_01, + "NATIVE-02": _native_02, + "NATIVE-03": _native_03, + "NATIVE-04": _native_04, + "NATIVE-05": _native_05, + "NATIVE-06": _native_06, + "NATIVE-07": _native_07, + "NATIVE-08": _native_08, + "NATIVE-09": _native_09, + "NATIVE-10": _native_10, + "NEG-01": _neg_01, + "NEG-02": _neg_02, + "NEG-03": _neg_03, + "NEG-04": _neg_04, + "NEG-05": _neg_05, + "NEG-06": _neg_06, + "NEG-07": _neg_07, + "NEG-08": _neg_08, +} + + +def observe_native_fixture(fixture_id: str) -> tuple[FixtureDisposition, str]: + """Observe one already-established ACP native/negative behavior deterministically.""" + observation = _OBSERVATIONS.get(fixture_id) + if observation is None: + raise ValueError(f"unknown native fixture: {fixture_id}") + return observation() From 81f480d8abe6cf473e391c86df30f60e8ebe5922 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:25:52 -0400 Subject: [PATCH 14/21] ci: fetch history for GSAE-E0 source binding --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 12c3f7d..88470db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,6 +16,8 @@ jobs: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} From 2d5dc2b4e3150a74b0c2a046976eefb0ed63028d Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:27:20 -0400 Subject: [PATCH 15/21] test: define deterministic GSAE-E0 evidence bundle rules --- experiments/gsae_e0/tests/test_manifest.py | 138 +++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 experiments/gsae_e0/tests/test_manifest.py diff --git a/experiments/gsae_e0/tests/test_manifest.py b/experiments/gsae_e0/tests/test_manifest.py new file mode 100644 index 0000000..8f99ba6 --- /dev/null +++ b/experiments/gsae_e0/tests/test_manifest.py @@ -0,0 +1,138 @@ +from dataclasses import replace + +import pytest + +from experiments.gsae_e0.manifest import ( + EvidenceBundle, + EvidenceValidationError, + canonical_bundle_sha256, + result_id, + validate_evidence_bundle, +) +from experiments.gsae_e0.schema import ( + AggregateDisposition, + FixtureDisposition, + FixtureResult, +) + + +SOURCE_SHA = "07a09698ca66e8837d04e6ec05b4de3448eced04" +SCHEMA_VERSION = "agent-control-plane.execution.v1" +FIXTURE_HASH = "79eda76151d82f9e0292e441bc6f7fe6572d858930baaaf8ddb0560641643bd6" +HARNESS_SHA = "8" * 40 + + +def fixture_result( + fixture_id="AUTH-02", + *, + run_id="run-1", + attempt=1, + prior_result_id=None, +): + return FixtureResult( + result_id=result_id(run_id, fixture_id, attempt), + run_id=run_id, + attempt=attempt, + fixture_id=fixture_id, + disposition=FixtureDisposition.STRUCTURED_COVERAGE, + source_under_test_sha=SOURCE_SHA, + schema_version=SCHEMA_VERSION, + fixture_manifest_sha256=FIXTURE_HASH, + structured_fields_used=("capability",), + evidence_summary="synthetic apparatus evidence", + prior_result_id=prior_result_id, + ) + + +def bundle(results=None): + if results is None: + results = (fixture_result(),) + return EvidenceBundle( + experiment_id="GSAE-E0", + protocol_version="GSAE-E0-R2-v0.1", + run_id="run-1", + authorization_record_id="synthetic-authorization-record", + source_under_test_sha=SOURCE_SHA, + harness_commit_sha=HARNESS_SHA, + schema_version=SCHEMA_VERSION, + fixture_set_version="stage-a-v1", + fixture_manifest_sha256=FIXTURE_HASH, + results=tuple(results), + aggregate_disposition=AggregateDisposition.FEASIBLE, + test_command="python -m pytest", + test_summary="synthetic apparatus verification", + evidence_ceiling="contract-feasibility evidence only", + ) + + +def test_result_id_is_deterministic_per_attempt_and_changes_on_retry(): + first = result_id("run-1", "AUTH-01", 1) + same = result_id("run-1", "AUTH-01", 1) + retry = result_id("run-1", "AUTH-01", 2) + assert first == same + assert first != retry + + +def test_semantically_identical_bundle_hash_is_stable(): + evidence = bundle() + assert canonical_bundle_sha256(evidence) == canonical_bundle_sha256(evidence) + assert len(canonical_bundle_sha256(evidence)) == 64 + + +def test_valid_bundle_passes_validation(): + evidence = bundle() + validate_evidence_bundle(evidence, known_fixture_ids={"AUTH-02"}) + + +def test_blank_result_id_is_rejected(): + broken = replace(fixture_result(), result_id="") + with pytest.raises((EvidenceValidationError, ValueError), match="result_id"): + validate_evidence_bundle(bundle((broken,)), known_fixture_ids={"AUTH-02"}) + + +def test_duplicate_result_ids_are_rejected(): + first = fixture_result("AUTH-02") + second = replace(fixture_result("AUTH-03"), result_id=first.result_id) + with pytest.raises(EvidenceValidationError, match="duplicate result_id"): + validate_evidence_bundle(bundle((first, second)), known_fixture_ids={"AUTH-02", "AUTH-03"}) + + +def test_unknown_fixture_id_is_rejected(): + with pytest.raises(EvidenceValidationError, match="unknown fixture_id"): + validate_evidence_bundle(bundle(), known_fixture_ids={"AUTH-01"}) + + +@pytest.mark.parametrize( + ("field", "value", "message"), + [ + ("source_under_test_sha", "0" * 40, "source_under_test_sha"), + ("schema_version", "wrong-schema", "schema_version"), + ("fixture_manifest_sha256", "b" * 64, "fixture_manifest_sha256"), + ], +) +def test_result_binding_mismatch_is_rejected(field, value, message): + broken = replace(fixture_result(), **{field: value}) + with pytest.raises(EvidenceValidationError, match=message): + validate_evidence_bundle(bundle((broken,)), known_fixture_ids={"AUTH-02"}) + + +def test_retry_must_point_to_exact_prior_attempt_result(): + first = fixture_result("AUTH-02", attempt=1) + retry = fixture_result( + "AUTH-02", + attempt=2, + prior_result_id="not-the-prior-result", + ) + with pytest.raises(EvidenceValidationError, match="prior_result_id"): + validate_evidence_bundle(bundle((first, retry)), known_fixture_ids={"AUTH-02"}) + + +def test_retry_chain_with_exact_prior_result_is_valid(): + first = fixture_result("AUTH-02", attempt=1) + retry = fixture_result( + "AUTH-02", + attempt=2, + prior_result_id=first.result_id, + ) + evidence = bundle((first, retry)) + validate_evidence_bundle(evidence, known_fixture_ids={"AUTH-02"}) From 40fd99049ce305b221a36dd90f89ff42bdceaa1d Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:28:13 -0400 Subject: [PATCH 16/21] feat: add deterministic GSAE-E0 evidence bundles --- experiments/gsae_e0/manifest.py | 216 ++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 experiments/gsae_e0/manifest.py diff --git a/experiments/gsae_e0/manifest.py b/experiments/gsae_e0/manifest.py new file mode 100644 index 0000000..b2ea091 --- /dev/null +++ b/experiments/gsae_e0/manifest.py @@ -0,0 +1,216 @@ +"""Deterministic evidence bundle construction for GSAE-E0 Stage A.""" + +from dataclasses import asdict, dataclass +import hashlib +import json +import re +from typing import Iterable +from uuid import UUID, uuid5 + +from .schema import AggregateDisposition, FixtureResult + + +_RESULT_NAMESPACE = UUID("e063a317-67cb-5d84-a411-7027376c68a0") + + +class EvidenceValidationError(ValueError): + """Raised when an evidence bundle is incomplete or internally inconsistent.""" + + +def _required_text(value: object, field_name: str) -> str: + if not isinstance(value, str) or not value.strip(): + raise EvidenceValidationError(f"{field_name} must be a non-blank string") + return value.strip() + + +def _sha(value: object, field_name: str, length: int) -> str: + text = _required_text(value, field_name) + if re.fullmatch(rf"[0-9a-f]{{{length}}}", text) is None: + raise EvidenceValidationError( + f"{field_name} must be {length} lowercase hexadecimal characters" + ) + return text + + +def result_id(run_id: str, fixture_id: str, attempt: int) -> str: + """Derive a deterministic result identity while making retries distinct.""" + run = _required_text(run_id, "run_id") + fixture = _required_text(fixture_id, "fixture_id") + if isinstance(attempt, bool) or not isinstance(attempt, int) or attempt < 1: + raise EvidenceValidationError("attempt must be an integer >= 1") + return str(uuid5(_RESULT_NAMESPACE, f"{run}|{fixture}|{attempt}")) + + +@dataclass(frozen=True) +class EvidenceBundle: + experiment_id: str + protocol_version: str + run_id: str + authorization_record_id: str + source_under_test_sha: str + harness_commit_sha: str + schema_version: str + fixture_set_version: str + fixture_manifest_sha256: str + results: tuple[FixtureResult, ...] + aggregate_disposition: AggregateDisposition + test_command: str + test_summary: str + evidence_ceiling: str + + def to_dict(self) -> dict[str, object]: + """Return the semantic, hash-bearing representation with stable ordering inputs.""" + return { + "experiment_id": self.experiment_id, + "protocol_version": self.protocol_version, + "run_id": self.run_id, + "authorization_record_id": self.authorization_record_id, + "source_under_test_sha": self.source_under_test_sha, + "harness_commit_sha": self.harness_commit_sha, + "schema_version": self.schema_version, + "fixture_set_version": self.fixture_set_version, + "fixture_manifest_sha256": self.fixture_manifest_sha256, + "results": [_result_to_dict(result) for result in self.results], + "aggregate_disposition": self.aggregate_disposition.value, + "test_command": self.test_command, + "test_summary": self.test_summary, + "evidence_ceiling": self.evidence_ceiling, + } + + +def _result_to_dict(result: FixtureResult) -> dict[str, object]: + data = asdict(result) + data["disposition"] = result.disposition.value + data["exception_class"] = ( + result.exception_class.value if result.exception_class is not None else None + ) + data["structured_fields_used"] = list(result.structured_fields_used) + return data + + +def canonical_bundle_bytes(bundle: EvidenceBundle) -> bytes: + """Serialize the semantic evidence body deterministically.""" + return ( + json.dumps( + bundle.to_dict(), + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + ) + + "\n" + ).encode("utf-8") + + +def canonical_bundle_sha256(bundle: EvidenceBundle) -> str: + return hashlib.sha256(canonical_bundle_bytes(bundle)).hexdigest() + + +def build_evidence_bundle( + *, + experiment_id: str, + protocol_version: str, + run_id: str, + authorization_record_id: str, + source_under_test_sha: str, + harness_commit_sha: str, + schema_version: str, + fixture_set_version: str, + fixture_manifest_sha256: str, + results: Iterable[FixtureResult], + aggregate_disposition: AggregateDisposition, + test_command: str, + test_summary: str, + evidence_ceiling: str, + known_fixture_ids: set[str] | frozenset[str], +) -> EvidenceBundle: + """Construct and validate one in-memory Stage-A evidence bundle.""" + bundle = EvidenceBundle( + experiment_id=experiment_id, + protocol_version=protocol_version, + run_id=run_id, + authorization_record_id=authorization_record_id, + source_under_test_sha=source_under_test_sha, + harness_commit_sha=harness_commit_sha, + schema_version=schema_version, + fixture_set_version=fixture_set_version, + fixture_manifest_sha256=fixture_manifest_sha256, + results=tuple(results), + aggregate_disposition=aggregate_disposition, + test_command=test_command, + test_summary=test_summary, + evidence_ceiling=evidence_ceiling, + ) + validate_evidence_bundle(bundle, known_fixture_ids=known_fixture_ids) + return bundle + + +def validate_evidence_bundle( + bundle: EvidenceBundle, + *, + known_fixture_ids: set[str] | frozenset[str], +) -> None: + """Fail closed on missing identities, binding drift, and invalid retry history.""" + if not isinstance(bundle, EvidenceBundle): + raise EvidenceValidationError("bundle must be EvidenceBundle") + + _required_text(bundle.experiment_id, "experiment_id") + _required_text(bundle.protocol_version, "protocol_version") + _required_text(bundle.run_id, "run_id") + _required_text(bundle.authorization_record_id, "authorization_record_id") + _sha(bundle.source_under_test_sha, "source_under_test_sha", 40) + _sha(bundle.harness_commit_sha, "harness_commit_sha", 40) + _required_text(bundle.schema_version, "schema_version") + _required_text(bundle.fixture_set_version, "fixture_set_version") + _sha(bundle.fixture_manifest_sha256, "fixture_manifest_sha256", 64) + _required_text(bundle.test_command, "test_command") + _required_text(bundle.test_summary, "test_summary") + _required_text(bundle.evidence_ceiling, "evidence_ceiling") + if not isinstance(bundle.aggregate_disposition, AggregateDisposition): + raise EvidenceValidationError("aggregate_disposition must be AggregateDisposition") + if not bundle.results: + raise EvidenceValidationError("results must not be empty") + + known = set(known_fixture_ids) + if not known or any(not isinstance(item, str) or not item.strip() for item in known): + raise EvidenceValidationError("known_fixture_ids must contain non-blank strings") + + result_ids: set[str] = set() + attempts: dict[tuple[str, int], FixtureResult] = {} + + for result in bundle.results: + result_identity = _required_text(result.result_id, "result_id") + if result_identity in result_ids: + raise EvidenceValidationError("duplicate result_id") + result_ids.add(result_identity) + + if result.fixture_id not in known: + raise EvidenceValidationError(f"unknown fixture_id: {result.fixture_id}") + if result.run_id != bundle.run_id: + raise EvidenceValidationError("result run_id does not match bundle run_id") + if result.source_under_test_sha != bundle.source_under_test_sha: + raise EvidenceValidationError("result source_under_test_sha does not match bundle") + if result.schema_version != bundle.schema_version: + raise EvidenceValidationError("result schema_version does not match bundle") + if result.fixture_manifest_sha256 != bundle.fixture_manifest_sha256: + raise EvidenceValidationError("result fixture_manifest_sha256 does not match bundle") + + expected_id = result_id(result.run_id, result.fixture_id, result.attempt) + if result.result_id != expected_id: + raise EvidenceValidationError("result_id does not match deterministic identity") + + key = (result.fixture_id, result.attempt) + if key in attempts: + raise EvidenceValidationError("duplicate fixture attempt") + attempts[key] = result + + for (fixture_id, attempt), result in attempts.items(): + if attempt == 1: + if result.prior_result_id is not None: + raise EvidenceValidationError("attempt 1 must not set prior_result_id") + continue + + prior = attempts.get((fixture_id, attempt - 1)) + if prior is None: + raise EvidenceValidationError("retry is missing the immediately prior attempt") + if result.prior_result_id != prior.result_id: + raise EvidenceValidationError("prior_result_id does not match prior attempt") From e0749fa0a3830b1fac11d5c0dc494597c0d740da Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:29:11 -0400 Subject: [PATCH 17/21] test: assert blank result identity at schema boundary --- experiments/gsae_e0/tests/test_manifest.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/experiments/gsae_e0/tests/test_manifest.py b/experiments/gsae_e0/tests/test_manifest.py index 8f99ba6..0f30c62 100644 --- a/experiments/gsae_e0/tests/test_manifest.py +++ b/experiments/gsae_e0/tests/test_manifest.py @@ -85,9 +85,8 @@ def test_valid_bundle_passes_validation(): def test_blank_result_id_is_rejected(): - broken = replace(fixture_result(), result_id="") - with pytest.raises((EvidenceValidationError, ValueError), match="result_id"): - validate_evidence_bundle(bundle((broken,)), known_fixture_ids={"AUTH-02"}) + with pytest.raises(ValueError, match="result_id"): + replace(fixture_result(), result_id="") def test_duplicate_result_ids_are_rejected(): From 903ec63702568d82d55dc6321eaed9a36a467878 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:30:17 -0400 Subject: [PATCH 18/21] test: define guarded GSAE-E0 Stage-A runner --- experiments/gsae_e0/tests/test_runner.py | 108 ++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/experiments/gsae_e0/tests/test_runner.py b/experiments/gsae_e0/tests/test_runner.py index 4bdac04..fbdc599 100644 --- a/experiments/gsae_e0/tests/test_runner.py +++ b/experiments/gsae_e0/tests/test_runner.py @@ -1,16 +1,20 @@ +import json from pathlib import Path import pytest from experiments.gsae_e0.runner import ( + ExecutionGuardError, SourceBindingError, observe_native_fixture, + run_stage_a, verify_source_binding, ) -from experiments.gsae_e0.schema import FixtureDisposition +from experiments.gsae_e0.schema import AggregateDisposition, FixtureDisposition SOURCE_SHA = "07a09698ca66e8837d04e6ec05b4de3448eced04" +SCHEMA_VERSION = "agent-control-plane.execution.v1" def test_bound_source_is_ancestor_and_acp_package_is_unchanged(): @@ -58,3 +62,105 @@ def test_native_observation_matches_expected_contract_behavior( def test_unknown_observation_fixture_fails_closed(): with pytest.raises(ValueError, match="unknown native fixture"): observe_native_fixture("NATIVE-999") + + +def _synthetic_manifest( + tmp_path: Path, + *, + experiment_id: str = "GSAE-E0", + schema_version: str = SCHEMA_VERSION, + source_sha: str = SOURCE_SHA, +) -> Path: + path = tmp_path / "synthetic-fixture-manifest.json" + path.write_text( + json.dumps( + { + "experiment_id": experiment_id, + "fixture_set_version": "synthetic-stage-a-v1", + "source_under_test_sha": source_sha, + "schema_version": schema_version, + "fixtures": [ + { + "fixture_id": "AUTH-SYNTHETIC-CAPABILITY", + "family": "authority_semantic", + "title": "synthetic capability probe", + "purpose": "exercise apparatus orchestration without the canonical fixture set", + "criticality": "critical", + "required_semantics": ["requested_capability"], + "input_spec": { + "required_path_groups": [["capability"]], + "gap_class_if_absent": "MISSING_CORE_SEMANTIC", + }, + "expected_classification_domain": [ + "STRUCTURED_COVERAGE", + "NOT_COVERED", + ], + } + ], + }, + sort_keys=True, + ), + encoding="utf-8", + ) + return path + + +def _run_kwargs(tmp_path: Path, **overrides): + values = { + "repo_root": Path.cwd(), + "fixture_path": _synthetic_manifest(tmp_path), + "protocol_version": "GSAE-E0-R2-v0.1-SYNTHETIC", + "authorization_record_id": "synthetic-apparatus-test-authorization", + "run_id": "synthetic-stage-a-run", + "harness_commit_sha": "8" * 40, + "test_command": "python -m pytest experiments/gsae_e0/tests/test_runner.py", + "test_summary": "synthetic apparatus orchestration only", + } + values.update(overrides) + return values + + +@pytest.mark.parametrize( + ("field", "value"), + [ + ("authorization_record_id", ""), + ("protocol_version", " "), + ("run_id", ""), + ("harness_commit_sha", ""), + ("test_command", ""), + ("test_summary", ""), + ], +) +def test_run_rejects_blank_required_execution_identity(tmp_path, field, value): + with pytest.raises(ExecutionGuardError, match=field): + run_stage_a(**_run_kwargs(tmp_path, **{field: value})) + + +def test_run_rejects_wrong_experiment_id(tmp_path): + fixture_path = _synthetic_manifest(tmp_path, experiment_id="NOT-GSAE-E0") + with pytest.raises(ExecutionGuardError, match="GSAE-E0"): + run_stage_a(**_run_kwargs(tmp_path, fixture_path=fixture_path)) + + +def test_run_rejects_schema_mismatch(tmp_path): + fixture_path = _synthetic_manifest(tmp_path, schema_version="wrong-schema") + with pytest.raises(ExecutionGuardError, match="schema_version"): + run_stage_a(**_run_kwargs(tmp_path, fixture_path=fixture_path)) + + +def test_run_rejects_unbound_source(tmp_path): + fixture_path = _synthetic_manifest(tmp_path, source_sha="0" * 40) + with pytest.raises(ExecutionGuardError, match="source"): + run_stage_a(**_run_kwargs(tmp_path, fixture_path=fixture_path)) + + +def test_synthetic_runner_returns_in_memory_bounded_bundle(tmp_path): + evidence = run_stage_a(**_run_kwargs(tmp_path)) + assert evidence.experiment_id == "GSAE-E0" + assert evidence.fixture_set_version == "synthetic-stage-a-v1" + assert evidence.aggregate_disposition is AggregateDisposition.FEASIBLE + assert [result.fixture_id for result in evidence.results] == [ + "AUTH-SYNTHETIC-CAPABILITY" + ] + assert not list(tmp_path.glob("*result*.json")) + assert not list(tmp_path.glob("*evidence*.json")) From 269b766d413d23d2c6f5ad23fe7e67839ee2f9b9 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:32:49 -0400 Subject: [PATCH 19/21] feat: add guarded GSAE-E0 Stage-A runner --- experiments/gsae_e0/runner.py | 169 +++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/experiments/gsae_e0/runner.py b/experiments/gsae_e0/runner.py index f4b4289..101c60b 100644 --- a/experiments/gsae_e0/runner.py +++ b/experiments/gsae_e0/runner.py @@ -18,13 +18,30 @@ ) from agent_control_plane.provenance import ProvenanceEvent -from .schema import FixtureDisposition +from .classify import ( + aggregate_disposition, + classify_authority_fixture, + execution_v1_surface_paths, +) +from .fixtures import fixture_manifest_sha256, load_fixture_manifest +from .manifest import EvidenceBundle, build_evidence_bundle, result_id +from .schema import ( + ExceptionClass, + FixtureDisposition, + FixtureFamily, + FixtureResult, + FixtureValidationError, +) class SourceBindingError(RuntimeError): """Raised when the working tree cannot prove the frozen ACP source binding.""" +class ExecutionGuardError(RuntimeError): + """Raised when Stage-A orchestration lacks a required execution guard or binding.""" + + def _git(repo_root: Path, *args: str) -> subprocess.CompletedProcess[str]: return subprocess.run( ["git", *args], @@ -330,3 +347,153 @@ def observe_native_fixture(fixture_id: str) -> tuple[FixtureDisposition, str]: if observation is None: raise ValueError(f"unknown native fixture: {fixture_id}") return observation() + + +def _guard_text(value: str, field_name: str) -> str: + if not isinstance(value, str) or not value.strip(): + raise ExecutionGuardError(f"{field_name} must be a non-blank string") + return value.strip() + + +def _native_result( + *, + fixture_id: str, + disposition: FixtureDisposition, + summary: str, + run_id: str, + source_under_test_sha: str, + schema_version: str, + fixture_manifest_sha256: str, +) -> FixtureResult: + exception_class = None + if disposition is FixtureDisposition.NOT_ESTABLISHED: + exception_class = ExceptionClass.IMPLEMENTATION_DEFECT + return FixtureResult( + result_id=result_id(run_id, fixture_id, 1), + run_id=run_id, + attempt=1, + fixture_id=fixture_id, + disposition=disposition, + source_under_test_sha=source_under_test_sha, + schema_version=schema_version, + fixture_manifest_sha256=fixture_manifest_sha256, + exception_class=exception_class, + evidence_summary=summary, + ) + + +def run_stage_a( + *, + repo_root: Path, + fixture_path: Path, + protocol_version: str, + authorization_record_id: str, + run_id: str, + harness_commit_sha: str, + test_command: str, + test_summary: str, +) -> EvidenceBundle: + """Run one explicitly identified Stage-A fixture manifest in memory only. + + This function requires an explicit authorization-record identifier but does + not itself decide whether that external governance record is valid. The GSAE + control record remains the authority for execution authorization. + """ + protocol = _guard_text(protocol_version, "protocol_version") + authorization = _guard_text(authorization_record_id, "authorization_record_id") + run = _guard_text(run_id, "run_id") + harness_sha = _guard_text(harness_commit_sha, "harness_commit_sha") + command = _guard_text(test_command, "test_command") + summary = _guard_text(test_summary, "test_summary") + if re.fullmatch(r"[0-9a-f]{40}", harness_sha) is None: + raise ExecutionGuardError( + "harness_commit_sha must be 40 lowercase hexadecimal characters" + ) + + try: + manifest = load_fixture_manifest(fixture_path) + except (OSError, ValueError, FixtureValidationError) as exc: + raise ExecutionGuardError(f"fixture manifest not established: {exc}") from exc + + if manifest.experiment_id != "GSAE-E0": + raise ExecutionGuardError( + f"experiment_id must be GSAE-E0, got {manifest.experiment_id!r}" + ) + if manifest.schema_version != SCHEMA_VERSION: + raise ExecutionGuardError( + f"schema_version must be {SCHEMA_VERSION}, got {manifest.schema_version!r}" + ) + + try: + verify_source_binding(repo_root, manifest.source_under_test_sha) + except SourceBindingError as exc: + raise ExecutionGuardError(f"source binding not established: {exc}") from exc + + try: + fixture_hash = fixture_manifest_sha256(fixture_path) + except (OSError, ValueError, FixtureValidationError) as exc: + raise ExecutionGuardError(f"fixture manifest identity not established: {exc}") from exc + + surface = execution_v1_surface_paths() + results: list[FixtureResult] = [] + + for fixture in manifest.fixtures: + if fixture.family in {FixtureFamily.NATIVE, FixtureFamily.NEGATIVE}: + try: + disposition, evidence_summary = observe_native_fixture(fixture.fixture_id) + except Exception as exc: # apparatus must convert unexpected observation errors to evidence + disposition = FixtureDisposition.NOT_ESTABLISHED + evidence_summary = f"unexpected observation error: {type(exc).__name__}: {exc}" + results.append( + _native_result( + fixture_id=fixture.fixture_id, + disposition=disposition, + summary=evidence_summary, + run_id=run, + source_under_test_sha=manifest.source_under_test_sha, + schema_version=manifest.schema_version, + fixture_manifest_sha256=fixture_hash, + ) + ) + elif fixture.family is FixtureFamily.AUTHORITY: + results.append( + classify_authority_fixture( + fixture, + surface, + result_id=result_id(run, fixture.fixture_id, 1), + run_id=run, + attempt=1, + source_under_test_sha=manifest.source_under_test_sha, + schema_version=manifest.schema_version, + fixture_manifest_sha256=fixture_hash, + ) + ) + else: + raise ExecutionGuardError(f"unsupported fixture family: {fixture.family.value}") + + fixture_ids = [fixture.fixture_id for fixture in manifest.fixtures] + result_fixture_ids = [result.fixture_id for result in results] + if len(results) != len(manifest.fixtures) or set(result_fixture_ids) != set(fixture_ids): + raise ExecutionGuardError("exactly one result per frozen fixture was not established") + + aggregate = aggregate_disposition(manifest.fixtures, results) + return build_evidence_bundle( + experiment_id=manifest.experiment_id, + protocol_version=protocol, + run_id=run, + authorization_record_id=authorization, + source_under_test_sha=manifest.source_under_test_sha, + harness_commit_sha=harness_sha, + schema_version=manifest.schema_version, + fixture_set_version=manifest.fixture_set_version, + fixture_manifest_sha256=fixture_hash, + results=results, + aggregate_disposition=aggregate, + test_command=command, + test_summary=summary, + evidence_ceiling=( + "GSAE-E0 Stage-A contract-feasibility evidence only; not portability, " + "authorization, governance efficacy, safety, security, or production readiness." + ), + known_fixture_ids=set(fixture_ids), + ) From 74a10eaf139bb10446173b99e8b916e2b47d2143 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:33:54 -0400 Subject: [PATCH 20/21] test: preserve overridden synthetic runner manifests --- experiments/gsae_e0/tests/test_runner.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/experiments/gsae_e0/tests/test_runner.py b/experiments/gsae_e0/tests/test_runner.py index fbdc599..9a0de48 100644 --- a/experiments/gsae_e0/tests/test_runner.py +++ b/experiments/gsae_e0/tests/test_runner.py @@ -106,9 +106,12 @@ def _synthetic_manifest( def _run_kwargs(tmp_path: Path, **overrides): + fixture_path = overrides.get("fixture_path") + if fixture_path is None: + fixture_path = _synthetic_manifest(tmp_path) values = { "repo_root": Path.cwd(), - "fixture_path": _synthetic_manifest(tmp_path), + "fixture_path": fixture_path, "protocol_version": "GSAE-E0-R2-v0.1-SYNTHETIC", "authorization_record_id": "synthetic-apparatus-test-authorization", "run_id": "synthetic-stage-a-run", From 2548ec7a2072d848360481fcf972270204f8aff8 Mon Sep 17 00:00:00 2001 From: "Andrew // Ndr \"Ender\" Hensel" <246370637+ndrorchestration@users.noreply.github.com> Date: Wed, 16 Sep 2026 02:34:41 -0400 Subject: [PATCH 21/21] docs: bound GSAE-E0 apparatus evidence ceiling --- experiments/gsae_e0/README.md | 114 ++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 experiments/gsae_e0/README.md diff --git a/experiments/gsae_e0/README.md b/experiments/gsae_e0/README.md new file mode 100644 index 0000000..4987860 --- /dev/null +++ b/experiments/gsae_e0/README.md @@ -0,0 +1,114 @@ +# GSAE-E0 Stage-A Conformance Harness + +**Status:** `RESEARCH APPARATUS / EXECUTION NOT AUTHORIZED / NOT RUN / N=0` + +**Source under test:** `07a09698ca66e8837d04e6ec05b4de3448eced04` +**Schema:** `agent-control-plane.execution.v1` +**Frozen fixture candidate:** `fixtures/stage_a_v1.json` + +This package measures a bounded contract-feasibility question. Passing apparatus tests establish only that the measurement apparatus behaves as tested. They do **not** establish GSAE-E0 feasibility, cross-runtime portability, governance efficacy, safety, security, production readiness, or execution authorization. + +## Purpose + +Stage A asks whether the bound ACP execution contract can represent the frozen ordinary and consequential governance states with stable structured semantics, without semantic overload, runtime-specific reinterpretation, or a core-schema fork. + +The harness deliberately measures the existing contract before any schema repair. Missing or ambiguous governance meanings remain evidence. Values hidden only in free-form `detail`, opaque artifact payloads, overloaded status strings, or runtime-specific identifier conventions do not count as structured authority coverage. + +## Apparatus verification + +Install the repository and test dependency: + +```bash +python -m pip install -e . pytest +``` + +Verify the research apparatus: + +```bash +python -m pytest experiments/gsae_e0/tests -v +``` + +Verify the full repository: + +```bash +python -m pytest +``` + +The repository CI runs the full suite on Python 3.10, 3.11, 3.12, 3.13, and 3.14. + +These commands test the apparatus and already-established ACP behavior. They do not execute the frozen canonical Stage-A experiment. + +## Source binding + +Before an evidence-producing Stage-A run can proceed, the runner requires the exact source-under-test commit to be an ancestor of the current apparatus head and requires zero changes under `src/agent_control_plane/` relative to that source commit. + +The CI workflow fetches full repository history so this binding can be proven rather than assumed. + +Changing the source-under-test SHA, schema version, or ACP source package invalidates the previous source binding for current-state claims and requires an explicit rebind/retest. Historical evidence remains provenance. + +## Fixture identity + +The frozen candidate manifest contains: + +- 10 native ACP conformance fixtures (`NATIVE-01` through `NATIVE-10`); +- 8 negative controls (`NEG-01` through `NEG-08`); +- 10 governance-authority semantic probes (`AUTH-01` through `AUTH-10`). + +Its canonical SHA-256 is computed from validated, canonical JSON. The content identity is an apparatus/protocol input identity, not an experiment result. + +## Execution authorization boundary + +`run_stage_a(...)` requires explicit protocol, authorization-record, run, harness-commit, source, schema, and fixture identities. It fails closed when required identities or bindings are absent. + +The function records an authorization-record identifier; it does **not** decide whether that external governance record is valid. The owning GSAE control record remains the authority for whether execution is authorized. + +Canonical Stage-A execution must not occur until: + +1. the owning GSAE record accepts the protocol freeze; +2. a separate execution-authorization record is issued; +3. the exact apparatus/source/fixture identities are bound to that authorization. + +No canonical execution command is documented here while the study remains unauthorized. + +## Result dispositions + +Stage A can produce only one aggregate contract-feasibility disposition: + +- `FEASIBLE_FOR_FROZEN_SCOPE`; +- `CONDITIONALLY_FEASIBLE_NARROW`; +- `NOT_FEASIBLE_FOR_FROZEN_SCOPE`; +- `NOT_ESTABLISHED`. + +A high numerical coverage rate cannot override a critical missing or ambiguous semantic. Missing required evidence produces `NOT_ESTABLISHED`; it is never imputed. + +These dispositions are not authorization, safety, efficacy, portability, or production-readiness conclusions. + +## Evidence bundle + +The in-memory evidence bundle binds, at minimum: + +- experiment and protocol identities; +- explicit authorization-record identity; +- source-under-test and harness commit identities; +- schema and fixture-set identities; +- fixture-manifest SHA-256; +- deterministic per-fixture result identities and retry lineage; +- aggregate disposition; +- test command and test summary; +- explicit evidence ceiling. + +The semantic bundle hash excludes wall-clock timestamps. Retries receive distinct deterministic result identities and must preserve the immediately prior result identity. + +The runner returns the bundle in memory; the apparatus does not silently write or publish canonical result artifacts. + +## Known boundary of the current contract candidate + +The Stage-A fixture set intentionally probes governance meanings that may be narrower than the current framework-neutral execution contract, including principal identity, resource scope, distinct operation semantics, policy identity/version, explicit decision outcome/reason codes, authority expiry, delegation, and authority conditions. + +Their absence from the current typed surface is not pre-declared as an experiment result. The canonical disposition remains unestablished until a separately authorized run evaluates the frozen fixture set. + +If an authorized Stage-A run later finds a missing or ambiguous critical semantic, freeze and retain that evidence before proposing a schema extension. Do not repair the contract first and erase the failure that motivated the change. + +## Stage B + +Cross-runtime portability is a separate Stage-B question. It remains `NOT ESTABLISHED` until a materially different runtime/adapter is selected, bound, and tested under a separate admitted protocol slice. A Stage-A result alone cannot establish portability.